c ++ fstream - создание собственных флагов форматирования - PullRequest
6 голосов
/ 09 марта 2012

Мне нужно создать новые флаги для формата выходного файла.у меня есть класс

class foo{
    bar* members;
    ofstream& operator<<(ofstream&);
    ifstream& operator>>(ifstream&);
};

, и я хочу использовать его следующим образом:

fstream os('filename.xml');
foo f;
os << xml << f;
os.close();

. Это сохранит файл xml .1011 * и это json файл.

Как я могу это сделать?

Ответы [ 3 ]

6 голосов
/ 09 марта 2012

Вы можете легко создавать свои собственные манипуляторы, либо захватывая существующий флаг, либо используя std::ios_base::xalloc для получения памяти, специфичной для нового потока, например (в файле реализации Foo:

static int const manipFlagId = std::ios_base::xalloc();

enum
{
    fmt_xml,        //  Becomes the default.
    fmt_json
};

std::ostream&
xml( std::ostream& stream )
{
    stream.iword( manipFlagId ) = fmt_xml;
    return stream;
}

std::ostream&
json( std::ostream& stream )
{
    stream.iword( manipFlagId ) = fmt_json;
    return stream;
}

std::ostream&
operator<<( std::ostream& dest, Foo const& obj )
{
    switch ( dest.iword( manipFlagId ) ) {
    case fmt_xml:
        // ...
        break;
    case fmt_json:
        //  ...
        break;
    default:
        assert(0);  //  Or log error, or abort, or...
    }
    return dest;
}

Declare xml и json в вашем заголовке, и работа выполнена.

(Сказав это, я скорее думаю, что это немного злоупотребление манипуляторами. Форматы, такие как xml, выходят за рамки простого локального форматированияи лучше всего обрабатываются отдельным классом, который владеет ostream и записывает весь поток, а не только отдельные объекты.)

1 голос
/ 09 марта 2012

Эта проблема является самой большой ошибкой в ​​библиотеке iostream.

Решение Джеймса Канзе является частичным, которое будет работать в ваших собственных классах, но в целом объектам предоставляется особый способ потоковой передачи.

Мое обычное средство - создать мой собственный класс-обертку с функцией, которую вы можете передать в свой поток, а с помощью xml будет содержать перегрузки до xml_node() или xml_attribute(), например

os << xml_attribute( "Id", id );

установит для атрибута Id значение, указанное в переменной в формате xml.

Я также написал области видимости узлов, чтобы они писали в потоковом режиме текст, открывающий узлы при построении, и автоматически записывали логику закрытия при разрушении.

Преимущество моего метода перед решением Джеймса Канзев том, что это расширяемое.Я думаю, что заключительный комментарий Джеймса Канзе говорит о том, что он не одобряет свое решение и, вероятно, будет использовать что-то более похожее на мое.

С решением выше, чтобы добавить больше форматов, вы должны отредактировать оператор << функции всев то время как кодирование в формате json будет представлять собой совершенно другой набор функций, и если вы добавите еще один формат, вы добавите код для него без необходимости редактирования какого-либо существующего кода. </p>

Между прочим, для вводадля XML вы бы использовали существующий синтаксический анализатор DOM или SAX и не использовали бы iostream напрямую таким образом.

0 голосов
/ 09 марта 2012

Самый простой способ, который приходит на ум, - начать с создания типа тега и его отдельного экземпляра:

struct JsonStreamTag {} json;

Затем позвольте такому тегу создать объект для переноса потока:1005 *

Конструктор protected, чтобы вы могли использовать только some_ostream << json (1) для построения JsonStream.Другой оператор вставки (2) выполняет фактическое форматирование.Затем вы определяете перегрузку write_json() (3) для каждого соответствующего типа:

void write_json(std::ostream& stream, int value) {
    stream << value;
}

void write_json(std::ostream& stream, std::string value) {
    stream << '"' << escape_json(value) << '"';
}

// Overloads for double, std::vector, std::map, &c.

В качестве альтернативы, опустите (2) и добавьте перегрузки для operator<<(JsonStream&, T) вместо.тот же процесс для записи соответствующих XmlStream с использованием XmlStreamTag и write_xml().Это предполагает, что ваш вывод может быть построен полностью из определенных значений, которые вы пишете;если вам нужен какой-то заголовок или нижний колонтитул, который одинаков для всех файлов, которые вы пишете, просто используйте конструктор и деструктор:

XmlStream(std::ostream& ostream) : ostream(ostream) {
    ostream << "<?xml version=\"1.0\"?><my_document>"
}

~XmlStream() {
    ostream << "</my_document>";
}
...