我需要为输出文件的格式创建新标志。我有一个班级
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 文件。
我该怎么做?
答案 0 :(得分:6)
您可以轻松创建自己的操纵者,劫持现有的操纵者
标记或使用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
的单独类来处理
写入整个流,而不仅仅是单个对象。)
答案 1 :(得分:1)
这个问题是iostream库中最大的缺陷。
James Kanze的解决方案是一个可以在你自己的类中工作的部分解决方案,但是一般来说,对象被赋予了一种独特的流式传输方式。
我通常的方法是使用可以传递到流中的函数创建我自己的包装类,并且xml将包含xml_node()
或xml_attribute()
的重载,例如
os << xml_attribute( "Id", id );
将属性Id设置为xml格式的变量中的任何内容。
我也编写了节点作用域,因此他们会编写流程来构建构造中的节点打开文本,并在销毁时自动编写结束逻辑。
我的方法优于James Kanze的解决方案的优势在于它是可扩展的。我认为James Kanze的结束评论表明他不支持他的解决方案,并且可能会使用更像我的方法。
使用上述解决方案,为了添加更多格式,您必须编辑运算符&lt;&lt;遍布各处的函数,而json格式代码将是一组完全不同的函数,如果你添加了另一种格式,你可以为它添加代码,而无需编辑任何现有代码。
顺便说一下,对于XML,您可以使用现有的DOM或SAX解析器,而不会直接以这种方式使用iostream。
答案 2 :(得分:0)
最简单的方法是首先创建一个标记类型及其单个实例:
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)并为operator<<(JsonStream&, T)
添加重载。
然后,按照相同的流程,使用XmlStream
和XmlStreamTag
撰写相应的write_xml()
。这假设您的输出可以完全根据您正在编写的特定值构建;如果你需要在你要编写的每个文件中使用相同的页眉或页脚,只需使用构造函数和析构函数:
XmlStream(std::ostream& ostream) : ostream(ostream) {
ostream << "<?xml version=\"1.0\"?><my_document>"
}
~XmlStream() {
ostream << "</my_document>";
}