c++ fstream-создание собственных флагов форматирования
мне нужно создать новые флаги для формата выходного файла. у меня есть класс
class foo{
bar* members;
ofstream& operator<<(ofstream&);
ifstream& operator>>(ifstream&);
};
и я хочу использовать его как:
fstream os('filename.xml');
foo f;
os << xml << f;
os.close();
это спасет xml.
fstream os('filename.json');
foo f;
os << json << f;
os.close();
и это в JSON.
как я могу это сделать?
3 ответов
вы можете легко создать yor собственных манипуляторов, либо угон существующих
флаг или использование 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;
}
объявить xml
и json
в вашем заголовке, и работа сделана.
(сказав это, я скорее думаю, что это немного злоупотребление
манипуляторы. Такие форматы, как xml, выходят за рамки простого, локального форматирования и
лучше всего обрабатываются отдельным классом, которому принадлежит ostream
, и
пишет весь поток, а не только отдельные объекты.)
эта проблема является самым большим недостатком в библиотеке iostream.
решение Джеймса Канзе является частичным, которое будет работать в ваших собственных классах, но в целом объектам предоставляется отдельный способ потоковой передачи.
мое обычное средство-создать свой собственный класс-оболочку с функцией, которую вы можете передать в свой поток, и с xml будет содержать перегрузки до xml_node()
или xml_attribute()
например
os << xml_attribute( "Id", id );
установит идентификатор атрибута на то, что находится в переменной в xml формат.
Я также написал области узлов, поэтому они будут записывать поток текста открытия узла при построении и автоматически записывать логику закрытия при разрушении.
преимущество моего метода над решением Джеймса Канзе заключается в том, что он расширяем. Я думаю, что заключительный комментарий Джеймса Канзе предполагает, что он не поддерживает свое решение и, вероятно, использовал бы что-то вроде моего.
С вышеуказанным решением, для того, чтобы добавить больше форматов, вы должны отредактируйте оператор
для ввода, кстати, для XML вы бы использовали существующий парсер DOM или SAX и не использовали бы iostream непосредственно таким образом.
самый простой способ, который приходит на ум, - начать с создания типа тега и одного его экземпляра:
struct JsonStreamTag {} json;
тогда пусть такой тег построит объект для обертывания потока:
class JsonStream {
public:
// (1)
friend JsonStream operator<<(std::ostream& ostream, const JsonStreamTag&) {
return JsonStream(ostream);
}
// (2)
template<class T>
friend JsonStream& operator<<(JsonStream& json_stream, const T& value) {
write_json(json_stream.ostream, value); // (3)
return json_stream;
}
protected:
JsonStream(std::ostream& ostream) : ostream(ostream) {}
private:
std::ostream& ostream;
};
конструктор 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) и добавьте перегрузки для .
затем просто выполните тот же процесс, чтобы написать соответствующий XmlStream
используя XmlStreamTag
и write_xml()
. Это предполагает, что ваш вывод может быть полностью построен из конкретных значений, которые вы пишете; если вам нужен какой-то верхний или нижний колонтитул, который одинаков для каждого файла, который вы напишете, просто используйте конструктор и деструктор:
XmlStream(std::ostream& ostream) : ostream(ostream) {
ostream << "<?xml version=\"1.0\"?><my_document>"
}
~XmlStream() {
ostream << "</my_document>";
}