二进制版本的iostream

Tha*_*tos 7 c++ binary iostream streambuf

我一直在编写iostreams的二进制版本.它本质上允许您编写二进制文件,但可以很好地控制文件的格式.用法示例:

my_file << binary::u32le << my_int << binary::u16le << my_string;
Run Code Online (Sandbox Code Playgroud)

将my_int写为无符号的32位整数,将my_string写为长度前缀的字符串(前缀为u16le.)要读回文件,可以翻转箭头.效果很好.然而,我在设计上遇到了一个障碍,我仍然围着它.所以,是时候问问了.(我们做了一些假设,例如8位字节,2s补码整数和IEEE浮点数.)

引擎盖下的iostream使用streambufs.这真是一个梦幻般的设计 - iostreams编码' int'到文本的序列化,并让底层的streambuf处理其余的.因此,你得到cout,fstreams,stringstreams等.所有这些,包括iostream和streambufs,都是模板化的,通常是在char上,但有时也是一个wchar.但是,我的数据是一个字节流,最好用' unsigned char' 表示.

我的第一次尝试是基于的模板unsigned char.std::basic_string模板很好,但streambuf没有.我遇到了一个名为class的问题codecvt,我永远无法遵循这个unsigned char主题.这提出了两个问题:

1)为什么streambuf对此类事情负责?似乎代码转换不属于streambuf的职责 - streambufs应该采取流,并缓冲数据到/从它缓冲数据.而已.像代码转换一样高级的东西感觉它应该属于iostreams.

由于我无法使用模板化的streambuf来处理unsigned char,所以我回到char,只是在char/unsigned char之间传递数据.出于显而易见的原因,我试图尽量减少演员阵容.大多数数据基本上都是在read()或write()函数中结束,然后调用底层的streambuf.(并在此过程中使用强制转换.)读取功能基本上是:

size_t read(unsigned char *buffer, size_t size)
{
    size_t ret;
    ret = stream()->sgetn(reinterpret_cast<char *>(buffer), size);
    // deal with ret for return size, eof, errors, etc.
    ...
}
Run Code Online (Sandbox Code Playgroud)

好的解决方案,糟糕的解


前两个问题表明需要更多信息.首先,查看了boost :: serialization等项目,但它们存在于更高级别,因为它们定义了自己的二进制格式.这更适用于较低级别的读/写,其中希望定义格式,或者已经定义了格式,或者不需要或不需要批量元数据.

其次,有些人询问了binary::u32le修饰符.它是一个类的实例化,它具有所需的字节顺序和宽度,此刻可能是未来的签名.该流保存该类的最后传递的实例的副本,并在序列化中使用该副本.这是一个解决方法,我个人试图重载<<运算符:

bostream &operator << (uint8_t n);
bostream &operator << (uint16_t n);
bostream &operator << (uint32_t n);
bostream &operator << (uint64_t n);
Run Code Online (Sandbox Code Playgroud)

但当时,这似乎不起作用.我有一些模糊函数调用的问题.对于常数来说尤其如此,尽管你可以像一张海报所说的那样投射或仅仅宣称它是一个const <type>.我似乎记得有一些其他更大的问题.

Tim*_*ter 1

据我了解,用于指定类型的流属性更适合指定字节序、打包或其他“元数据”值。类型本身的处理应该由编译器完成。至少,STL 看起来就是这样设计的。

如果使用重载自动分隔类型,则仅当类型与变量的声明类型不同时才需要指定类型:

Stream& operator<<(int8_t);
Stream& operator<<(uint8_t);
Stream& operator<<(int16_t);
Stream& operator<<(uint16_t);
etc.

uint32_t x;
stream << x << (uint16_t)x;
Run Code Online (Sandbox Code Playgroud)

读取声明类型以外的类型会有点混乱。但总的来说,我认为应该避免读取或写入与输出类型不同的变量。

我相信 std::codecvt 的默认版本不执行任何操作,对所有内容都返回“noconv”。它只有在使用“宽”字符流时才真正起作用。你不能为codecvt设置一个类似的定义吗?如果由于某种原因,为您的流定义无操作编解码器是不切实际的,那么我认为您的转换解决方案没有任何问题,特别是因为它被隔离到一个位置。

最后,您确定使用一些标准序列化代码(例如Boost)会比自己编写更好吗?