在C++中寻找MemoryStream

3
在C#的奇妙世界中,我可以创建一个内存流而不指定其大小,写入数据,然后只需获取底层缓冲区即可。
那么在C++中如何做到相同的效果呢?基本上我需要做以下操作:
memory_stream  ms(GROW_AS_MUCH_AS_YOU_LIKE);

ms << someLargeObjects << someSmallObjects << someObjectsWhosSizeIDontKnow;

unsigned char* buffer = ms.GetBuffer();
int bufferSize = ms.GetBufferSize();

rawNetworkSocket.Send(buffer, bufferSize);

顺便说一下,我的项目中使用了boost,虽然我并不是很熟悉它。

谢谢。


@Martin:我不想这样做,我想创建一个流,填充它,获取底层缓冲区并发送。另外,我不能使用boost::socket,我正在使用WINSOCK API上的自定义协议,因此我只能发送整个缓冲区。 - AK_
4个回答

6
#include <sstream>

std::ostringstream  buffer; // no growth specification necessary
buffer << "a char buffer" << customObject << someOtherObject;

std::string contents = buffer.str();
size_t bufferSize = contents.size();

rawNetworkSocket.Send(contents); // you can take the size in Send

使用这种方法,您将不得不在接收结果时对其进行解析(因为上面的代码只是将数据转换为非结构化字符串)。

它的另一个问题是,由于C++不支持反射,您将不得不为您的对象定义运算符 <<。 这是一个 Custom 类的代码示例:

template<typename C, typename T>
std::basic_ostream<C,T>& operator << (
    std::basic_ostream<C,T>& out, const Custom& object)
{
    out << object.member1 << "," << object.member2 /* ... */ << object.memberN;
    return out;
}

如果您需要结构化序列化,请查看boost::serialization

3

您可能需要查看 std::stringstream 来实现该目的。流会随需增长。除非您想以二进制形式而不是 ASCII 形式保留对象,否则您可以查看 streambuf 对象和实现。

请注意,C++ 没有反射或双/多重分派,因此您必须自己提供对未知大小对象的支持:

class unknown_base {
   virtual void dump( std::ostream & ) const;
};
std::ostream& operator<<( std::ostream& o, unknown_base const & obj ) {
   obj.dump( o );
   return o;
}
std::string serialize( std::vector<unknown_base*> const & data ) {
   std::ostringstream st;
   for ( std::vector<unknown_base*>::const_iterator it = data.begin(), end = data.end();
         it != end; ++it ) {
      st << **it; // double dereference: iterator, pointer
   }
   return st.str();
}

为什么 std::stringstream 不能用于二进制对象?它可以处理任何东西。只需编写或 << 想要的任何内容,然后获取生成的 std::string 对象,该对象可以包含任何内容,包括 0(C 字符串终止字符),但在 std::string 中间没有相同的含义。 - Didier Trosset
@Didier,惯例是使用运算符<<将某些可读性强的内容写入ostream。 - Pete Kirkham
@Pete:你也可以使用std::stringstream的write()成员函数。 - Didier Trosset
如果您使用 ostream 来打印基本类型或支持 operator<< 的库类型,则它将以人类可读的形式编写。我不应该说 ASCII,而应该说是人类可读的。 - David Rodríguez - dribeas

1
在 Boost 方面,有一个名为 Iostreams 的库非常相似。

0

既然你在谈论网络,那么似乎你想要创建某种消息并将其发送到网络上。

有一些库可以创建消息并为这些消息生成API,其中最著名的是Google Protocol Buffers(简称protobuf)。它允许您在一个短文件(自定义格式)中描述消息的语法,然后自动生成用于解码此消息的API,支持C++ / Python / Java。

优点包括:

  • 互操作性,这也意味着可以使用脚本语言检查消息,方便调试。
  • 版本处理(向后和向前兼容性),因为我们知道您很快就会修改消息,但可能不会立即升级所有内容。
  • 文本/二进制输出。文本对于调试很方便,二进制则在每个位都很重要时需要。

此外,还可以使用文本输出并使用LZO或类似工具进行压缩以节省空间 :)


这是一个关于编程的内容。以下是翻译后的文本:那太好了,但是我已经有一个自定义协议和API来传递消息了,我的消息将使用XML格式,客户端使用C#语言... 我想做的是使用boost::serialization将我的消息转换为XML格式,然后获取生成的流数据,将缓冲区直接发送到C#端。 - AK_
不要使用序列化。它将两个独立的实体绑定在一起。你可以完美地为谷歌编写一个C# API生成器(考虑到他们与微软的战争,他们不太可能自己这样做)。或者使用另一种消息传递方案。但是序列化是用于解码相同二进制的对象,如果您有一个字段对应一个对象,那么您必须立即更新C#端,否则它就会停止工作...并且您还必须在C#端拥有相同的模型:这是一个维护地狱。您需要定义一个正式的接口(如果必须使用xml或json等)。 - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接