Предположим, вы хотите написать фильтр для удаления комментариев в стиле оболочки. Основной алгоритм заключается в следующем: вы просматриваете символы по одному за раз, пересылая их без изменений, пока не встретите символ комментария, обычно '#'. Когда вы находите символ комментария, вы исследуете и игнорируете символы, пока не столкнетесь с персонажем новой строки, и в этот момент алгоритм начинается снова. Обратите внимание, что этот алгоритм состоит из двух субалгоритмов: один алгоритм для чтения обычного текста и один для чтения комментариев.
В следующих трех разделах я выражу этот алгоритм как stdio_filter, InputFilter и OutputFilter. Исходный код можно найти в заголовке . Эти примеры были вдохновлены Джеймсом Канзе UncommentExtractor.hh ( см. [Kanze]).
shell_comments_stdio_filter
Вы можете выразить комментарии Фильтр stdio_filter выглядит следующим образом:
#include<cstdio>// EOF#include<iostream>// cin, cout#include<boost/iostreams/filter/stdio.hpp>class shell_comments_stdio_filter : public stdio_filter {
public:
explicit shell_comments_stdio_filter(char comment_char = '#')
: comment_char_(comment_char)
{ }
private:
void do_filter()
{
bool skip = false;
int c;
while ((c = std::cin.get()) != EOF) {
skip = c == comment_char_ ?
true :
c == '\n' ?
false :
skip;
if (!skip)
std::cout.put(c);
}
}
char comment_char_;
};
} } } // End namespace boost::iostreams:example
Реализация функции virtualdo_filter проста: Локальная переменная skip отслеживает, обрабатываете ли вы комментарий; while петля читает символ c из std::cin, обновляет skip и пишет c в std::cout, если skip не является true.
Фильтры, которые получают из stdio_filter, являются DualUseFilters, что означает, что они могут использоваться либо для вывода, либо для ввода, но не оба одновременно. Поэтому unix2dos_stdio_filter можно использовать вместо shell_comments_input_filter и shell_comments_output_filter, ниже.
shell_comments_input_filter
Далее вы будете выражать комментарии оболочки Фильтр как InputFilter. Типичный узкохарактерный InputFilter выглядит так:
#include<boost/iostreams/categories.hpp>// input_filter_tag#include<boost/iostreams/char_traits.hpp>// EOF, WOULD_BLOCK#include<boost/iostreams/operations.hpp>// get, read, putbacknamespace io = boost::iostreams;
class my_input_filter {
public:
typedefchar char_type;
typedef input_filter_tag category;
template<typename Source>
int get(Source& src)
{
// Attempt to produce one character of filtered// data, reading from src as necessary. If successful,// return the character; otherwise return EOF to// indicate end-of-stream, or WOULD_BLOCK
}
/* Other members */
};
Функция get пытается произвести один символ фильтрованного вывода. Он получает доступ к нефильтрованной последовательности символов через предоставленный Источник src, используя основные операции i/o get, read и putback. Если персонаж создан, get возвращает его. В противном случае get возвращает один из кодов состояния EOF или WOULD_BLOCK. EOF, который указывает на конец потока, является макросом, определенным в стандартном заголовке . WOULD_BLOCK, что указывает на временную недоступность ввода, представляет собой константу, определенную в пространстве имен boost::iostreams, в заголовке
Вы также можете написать приведенный выше пример следующим образом:
#include<boost/iostreams/concepts.hpp>// input_filterclass my_input_filter : public input_filter {
public:
template<typename Source>
int get(Source& src);
/* Other members */
};
Здесь input_filter является базовым классом удобства, который предоставляет типы членов char_type и категория, а также реализации функций членов без операции close и imbue. В ближайшее время мы поговорим о close.
Теперь вы готовы выразить оболочку Фильтр комментариев в виде InputFilter:
#include<boost/iostreams/char_traits.hpp>// EOF, WOULD_BLOCK#include<boost/iostreams/concepts.hpp>// input_filter#include<boost/iostreams/operations.hpp>// getnamespace boost { namespace iostreams { namespace example {
class shell_comments_input_filter : public input_filter {
public:
explicit shell_comments_input_filter(char comment_char = '#')
: comment_char_(comment_char), skip_(false)
{ }
template<typename Source>
int get(Source& src)
{
int c;
while (true) {
if ((c = boost::iostreams::get(src)) == EOF || c == WOULD_BLOCK)
break;
skip_ = c == comment_char_ ?
true :
c == '\n' ?
false :
skip_;
if (!skip_)
break;
}
return c;
}
template<typename Source>
void close(Source&) { skip_ = false; }
private:
char comment_char_;
bool skip_;
};
} } } // End namespace boost::iostreams:example
Здесь переменная члена skip_ играет ту же роль, что и локальная переменная skipshell_comments_stdio_filter::do_filter. Реализация get очень похожа на реализацию shell_comments_stdio_filter::do_filter: петля в то время как читает символ c, обновляет skip_ и возвращает c, если skip_ не является true. Основное отличие заключается в том, что вы должны обрабатывать специальное значение WOULD_BLOCK, что указывает на отсутствие ввода в настоящее время.
Таким образом, вы видите, что реализация InputFilter с нуля немного сложнее, чем получение из stdio_filter. При написании InputFilter вы должны быть готовы к прерыванию в любой момент в середине алгоритма; когда это происходит, вы должны записать достаточно информации о текущем состоянии алгоритма, чтобы позволить вам забрать позже именно там, где вы остановились. То же самое относится и к OutputFilters. На самом деле, многие Inputfilters и OutputFilters можно рассматривать как машины с конечным состоянием; я оформлю эту идею позже. См. Фильтры конечного состояния .
Есть еще одна проблема с shell_comments_input_filter: его экземпляры можно использовать только один раз. Это потому, что кто-то может закрыть поток, пока установлен флаг skip_. Если бы поток был позже вновь открыт и #8212; со свежей последовательностью нефильтрованных данных и #8212; первая строка текста была бы отфильтрована, независимо от того, были ли они прокомментированы.
Чтобы исправить это, нужно сделать фильтр Closable. Для этого необходимо реализовать функцию close. Вы также должны дать своему фильтру тег категории , конвертируемый в closable_tag , чтобы сообщить библиотеке Iostream, что ваш фильтр реализует close.
Улучшенный фильтр выглядит так:
namespace boost { namespace iostreams { namespace example {
class shell_comments_input_filter : public input_filter {
public:
shell_comments_input_filter();
template<typename Source>
int get(Source& src);
template<typename Source>
void close(Source&) { skip_ = false; }
private:
bool skip_;
};
} } } // End namespace boost::iostreams:example
Здесь я получил от класса помощника input_filter, который обеспечивает тип члена char_type, равный char, и тег категории, конвертируемый в input_filter_tag и в closable_tag. Реализация close просто очищает флаг skip_, чтобы фильтр был готов к повторному использованию.
shell_comments_output_filter
Далее выразим комментарий оболочки Фильтр как Фильтр вывода. Типичный узкохарактерный OutputFilter выглядит так:
#include<boost/iostreams/categories.hpp>#include<boost/iostreams/operations.hpp>// put, writenamespace io = boost::iostreams;
class my_output_filter {
public:
typedefchar char_type;
typedef output_filter_tag category;
template<typename Sink>
bool put(Sink& dest, int c)
{
// Attempt to consume the given character of unfiltered// data, writing filtered data to dest as appropriate. // Return true if the character was successfully consumed.
}
/* Other members */
};
Функция put пытается фильтровать один символ c, записывая отфильтрованный вывод на Sinkdest. Он получает доступ к dest с использованием основных операций i/o put и write. Обе эти функции могут выйти из строя: iostreams::put может возвращать false, а iostreams::write может потреблять меньше символов, чем запрашивалось. Если это происходит, функция put может вернуться false, что указывает на то, что c не может быть потреблено. В противном случае он должен потреблять c и возвращать true.
Вы также можете написать приведенный выше пример следующим образом:
#include<boost/iostreams/concepts.hpp>// output_filterclass my_output_filter : public output_filter {
public:
template<typename Sink>
bool put(Sink& dest, int c);
/* Other members */
};
Здесь output_filter является базовым классом удобства, который предоставляет типы членов char_type и категория, а также реализации функций членов без операции close и imbue.
Теперь вы готовы выражать комментарии Фильтр как OutputFilter:
#include<boost/iostreams/concepts.hpp>// output_filter#include<boost/iostreams/operations.hpp>// putnamespace boost { namespace iostreams { namespace example {
class shell_comments_output_filter : public output_filter {
public:
explicit shell_comments_output_filter(char comment_char = '#')
: comment_char_(comment_char), skip_(false)
{ }
template<typename Sink>
bool put(Sink& dest, int c)
{
skip_ = c == comment_char_ ?
true :
c == '\n' ?
false :
skip_;
if (skip_)
returntrue;
return iostreams::put(dest, c);
}
template<typename Source>
void close(Source&) { skip_ = false; }
private:
char comment_char_;
bool skip_;
};
} } } // End namespace boost::iostreams:example
Функция put сначала проверяет данный символ c и обновляет переменную skip_; далее, если skip_ не является true, она пытается написать c. Функция close просто очищает флаг skip_, чтобы фильтр был готов к повторному использованию.
Статья Tutorial раздела может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.