c++创建自己的格式化标志

c++ creating own formatting flags

本文关键字:标志 格式化 自己的 创建 c++      更新时间:2023-10-16

我需要为输出文件的格式创建新的标志。我有

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文件。

我该怎么做?

您可以轻松创建自己的操纵器,或者劫持现有的标志或使用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;
}

在头中声明xmljson,工作就完成了。

(话虽如此,我认为这有点滥用操纵器。像xml这样的格式超越了简单的本地格式最好由一个单独的类处理,该类拥有ostream,并且写入整个流,而不仅仅是单个对象。)

此问题是iostream库中最大的缺陷。

James Kanze的解决方案是一个局部的解决方案,它将在您自己的类中工作,但通常情况下,对象被赋予了一种独特的流式传输方式。

我通常的方法是创建我自己的包装器类,其中包含一个可以传递到流中的函数,并且xml将包含对xml_node()xml_attribute()的重载,例如

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

将属性Id设置为xml格式的变量中的任何属性。

我还编写了节点作用域,因此它们将在构造时编写节点打开文本流,并在销毁时自动编写关闭逻辑。

与James Kanze的解决方案相比,我的方法的优势在于它是可扩展的。我认为詹姆斯·坎泽的结束语表明他不支持他的解决方案,可能会使用更像我的解决方案。

使用上述解决方案,为了添加更多格式,您必须编辑运算符<lt;函数,而json格式的代码将是一组完全不同的函数,如果您添加了另一种格式,则无需编辑任何现有代码即可为其添加代码。

顺便说一句,对于输入,对于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),改为添加operator<<(JsonStream&, T)的过载。

然后按照相同的过程使用XmlStreamTagwrite_xml()写入相应的XmlStream。这假设您的输出可以完全由您正在编写的特定值构建;如果你需要在你要写的每个文件中都有相同的页眉或页脚,只需使用构造函数和析构函数:

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