使用std :: streams格式化输出

时间:2010-01-31 13:57:04

标签: c++ format stream std

我有一个我希望能够流式传输的对象。但是我希望能够通过使用不同的格式以不同的方式流式传输它,或者我应该说出描述这个对象的方法。我想知道这应该如何通过流来解决。

我想要的是能够使用通用格式并使用某种格式适配器将通用格式转换为首选格式。

我还希望能够将格式与Item的实现分开,因此每次添加或更改新格式时都不必更改Item。

此代码大致说明了我想要的内容。

Item item;
std::cout << "generic formatted:" << item;
std::cout << "custom formatted:" << CustomItemFormat() << item;

但这可能不可行或不实用。

如何使用流媒体库来解决这些问题?

7 个答案:

答案 0 :(得分:4)

就个人而言,我会写一套格式化程序 格式化程序必须知道它们正在格式化的对象的内部 但是让他们成为朋友应该不是什么大问题。

class X
{ friend std::ostream& operator<<(std::ostream& str,XML_Format const& formatter);
  friend std::ostream& operator<<(std::ostream& str,Json_Format const& formatter);
  friend std::ostream& operator<<(std::ostream& str,Fred_Format const& formatter);
  public: int value() const {return 5;}
};
struct XML__Foram   { X const& print; XML_Format(X const& v):   print(v) {} };
struct Json_Format  { X const& print; Json_Format(X const& v):  print(v) {} };
struct Fred_Format  { X const& print; Fred_Format(X const& v):  print(v) {} };

std::ostream& operator<<(std::ostream& str,XML_Format const& formatter)
{
     return str << "<XObj>" << formatter.print.value() << "</XObj>";
}
std::ostream& operator<<(std::ostream& str,Json_Format const& formatter)
{
     return str << "{XObj:{" << formatter.print.value() << "}}";
}
std::ostream& operator<<(std::ostream& str,Fred_Format const& formatter)
{
     return str << "Killl Kill Kill. Friday 13th";
}

int main()
{
     X   obj;
     std::cout << XML_Format(obj) << std::endl;
}

答案 1 :(得分:1)

是的,有可能,你正在寻找的神奇词是流manipulators

查看此问题:C++ custom stream manipulator that changes next item on stream

答案 2 :(得分:1)

您需要编写一个流操纵器,它将信息存储在流中,然后由operator<<(std::ostream&,const Item&)使用。有关如何在流中存储用户数据,请参阅 this 答案的开头部分。

答案 3 :(得分:0)

Item.CustomItemFormat()有什么不好?

或者,有一些旨在解决这个问题的设计模式,即访客模式。

你也可以有CustomItemFormat(Item)?

我不认为要求流来解决这个问题是正确的方向,你应该将自己的任务委托给Item类

答案 4 :(得分:0)

我想的越多,我开始怀疑自己是否正在以错误的方式接近这一点。也许我应该有一个通用格式界面并设置项目的格式,因此项目可以使用这种格式输出自己。

这有意义吗?

答案 5 :(得分:0)

IOStreams不太适合此操作,但是您可以使用诸如{fmt}之类的格式库,以提供更好的可扩展性。例如:

struct Item {
  int value;
};

template <>
struct fmt::formatter<Item> {
  enum format { generic, custom };
  format f = generic;

  constexpr auto parse(parse_context &ctx) {
    auto it = ctx.begin(), end = ctx.end();
    if (it == end) return it;
    if (*it == 'g') f = generic;
    else if (*it == 'c') f = custom;
    else return it;
    return ++it;
  }

  template <typename FormatContext>
  auto format(const Item& item, FormatContext &ctx) {
    return format_to(ctx.out(), f == generic ? "{}" : "{:x}", item.value);
  }
};

然后,您可以将Item对象与任何格式设置功能一起使用,例如print

fmt::print("{}", Item{42}); // Default generic format - value formatted as decimal
fmt::print("{:g}", Item{42}); // Explicit generic format
fmt::print("{:c}", Item{42}); // Custom format - value formatted as hex

免责声明:我是{fmt}的作者。

答案 6 :(得分:-1)

尝试使用访客设计模式:

struct Object_Writer_Interface
{
  virtual void write_member_i(int value) = 0;
  virtual void write_member_c(char value) = 0;
};

struct Object
{
    int i;
    char c;
    void write(Object_Writer_Interface * p_writer)
    {
        if (p_writer)
        {
            p_writer->write_member_i(i);
            p_writer->write_member_c(c);
        }
    }
};

struct Stream_Object_Writer
  : public Object_Writer_Interface
{
    Stream_Object_Writer(std::ostream& out)
       : m_out(out)
       { ; }
    void write_member_i(int value)
    {
        m_out << i << '\n';
    }
    void write_member_c(char c)
    {
        m_out << "'" << c << "'\n";
    }
    std::ostream& m_out;
};


int main(void)
{
    Object  o;
    o.i = 5;
    o.c = 'M';

    // Create a writer for std::cout
    Stream_Object_Writer writer(std::cout);

    // Write the object to std::cout using the writer
    o.write(&writer);

    return EXIT_SUCCESS;
}

通过这种设计,要将对象写入套接字,可以从Object_Writer_Interface派生一个类来处理套接字。同样,如果你想序列化对象。

我目前正在使用这种技术将对象写入GUI列表框,数据库记录和Xml文件。与重载operator <<的技术不同,我在开发新的 Writers 时不必修改原则类。