4

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

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

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

这段代码大致说明了我想要的。

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

但这可能是不可能的或不切实际的。

流媒体库打算如何面对这些问题?

4

7 回答 7

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;
}
于 2010-01-31T19:10:40.323 回答
1

是的,有可能,您正在寻找的神奇词是流操纵器

看看这个问题:C++ custom stream manipulator that changes next item on stream

于 2010-01-31T14:07:21.507 回答
1

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

于 2010-01-31T14:14:46.810 回答
0

Item.CustomItemFormat() 有什么不好?

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

你也可以有 CustomItemFormat(Item)?

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

于 2010-01-31T14:08:21.533 回答
0

我想得越多,我开始怀疑我是否以错误的方式接近这个问题。也许我应该有一个通用的格式接口并为项目设置格式,以便项目可以使用这种格式输出自己。

这有意义吗?

于 2010-01-31T14:56:00.507 回答
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} 的作者。

于 2018-12-16T02:43:15.540 回答
-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 文件。与重载技术不同,我在开发新的Writersoperator <<时不必修改原理类。

于 2010-01-31T21:06:19.717 回答