草庐IT

c++ - iostream 的二进制版本

coder 2023-11-17 原文

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

my_file << binary::u32le << my_int << binary::u16le << my_string;

会将 my_int 写成无符号 32 位整数,将 my_string 写成长度前缀字符串(其中前缀为 u16le。)要读回文件,您需要翻转箭头。效果很好。然而,我在设计中遇到了一个障碍,我仍然对此持观望态度。所以,是时候问了。 (我们做了几个假设,例如 8 位字节、2s 补码整数和 IEEE float 。)

iostreams 在底层使用 streambufs。这真是一个绝妙的设计——iostreams 编码了一个'int| 的序列化。 ' 到文本中,让底层的 streambuf 处理其余的。因此,您得到 cout、fstreams、stringstreams 等。所有这些,包括 iostreams 和 streambufs,都是模板化的,通常在 char 上,但有时也作为 wchar。然而,我的数据是一个字节流,最好用 ' unsigned char 表示'.

我的第一次尝试是根据 unsigned char 对类进行模板化. std::basic_string模板够好,但是streambuf才不是。我遇到了一个名为 codecvt 的类的几个问题,我永远无法关注 unsigned char主题。这就提出了两个问题:

1) 为什么 streambuf 负责这些事情?代码转换似乎超出了 streambuf 的责任——streambuf 应该接收一个流,并将数据缓冲到流中或从中缓冲数据。而已。像代码转换这样高级的东西感觉应该属于 iostreams。

因为我无法让模板化的流缓冲区与 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.
    ...
}

好的解决方案,坏的解决方案?


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

其次,有人询问了 binary::u32le修饰符。它是一个类的实例化,它持有所需的字节序和宽度,目前可能在未来有符号。该流保存该类最后传递的实例的拷贝,并在序列化中使用它。这是一种解决方法,我最初尝试过重载 <>

bostream &operator << (uint8_t n);
bostream &operator << (uint16_t n);
bostream &operator << (uint32_t n);
bostream &operator << (uint64_t n);

然而在当时,这似乎行不通。我有几个关于模糊函数调用的问题。对于常量尤其如此,尽管您可以像一位海报所建议的那样将其强制转换或仅声明为 const <type>。 .不过,我似乎记得还有其他一些更大的问题。

最佳答案

我同意合法化。我几乎需要做你正在做的事情,并查看了重载 <</>> , 但得出的结论是 iostream 并不是为了适应它而设计的。一方面,我不想必须对流类进行子类化才能定义我的重载。

我的解决方案(只需要在一台机器上临时序列化数据,因此不需要解决字节顺序问题)基于这种模式:

// deducible template argument read
template <class T>
void read_raw(std::istream& stream, T& value,
    typename boost::enable_if< boost::is_pod<T> >::type* dummy = 0)
{
    stream.read(reinterpret_cast<char*>(&value), sizeof(value));
}

// explicit template argument read
template <class T>
T read_raw(std::istream& stream)
{
    T value;
    read_raw(stream, value);
    return value;
}

template <class T>
void write_raw(std::ostream& stream, const T& value,
    typename boost::enable_if< boost::is_pod<T> >::type* dummy = 0)
{
    stream.write(reinterpret_cast<const char*>(&value), sizeof(value));
}

然后我为任何非 POD 类型(例如字符串)进一步重载了 read_raw/write_raw。请注意,只需要重载 read_raw 的第一个版本;如果你use ADL correctly ,第二个 (1-arg) 版本可以调用稍后定义的和其他命名空间中的 2-arg 重载。

写例子:

int32_t x;
int64_t y;
int8_t z;
write_raw(is, x);
write_raw(is, y);
write_raw<int16_t>(is, z); // explicitly write int8_t as int16_t

阅读范例:

int32_t x = read_raw<int32_t>(is); // explicit form
int64_t y;
read_raw(is, y); // implicit form
int8_t z = numeric_cast<int8_t>(read_raw<int16_t>(is));

它不像重载运算符那么吸引人,而且事情也不会那么容易地放在一行上(无论如何我倾向于避免这种情况,因为调试断点是面向行的),但我认为它变得更简单、更明显,并没有更冗长。

关于c++ - iostream 的二进制版本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1150843/

有关c++ - iostream 的二进制版本的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  3. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

    我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

  4. ruby-on-rails - 如果我将 ruby​​ 版本 2.5.1 与 rails 版本 2.3.18 一起使用会怎样? - 2

    如果我使用ruby​​版本2.5.1和Rails版本2.3.18会怎样?我有基于rails2.3.18和ruby​​1.9.2p320构建的rails应用程序,我只想升级ruby的版本,而不是rails,这可能吗?我必须面对哪些挑战? 最佳答案 GitHub维护apublicfork它有针对旧Rails版本的分支,有各种变化,它们一直在运行。有一段时间,他们在较新的Ruby版本上运行较旧的Rails版本,而不是最初支持的版本,因此您可能会发现一些关于需要向后移植的有用提示。不过,他们现在已经有几年没有使用2.3了,所以充其量只能让更

  5. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  6. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  7. ruby-on-rails - 如何在发布新的 Ruby 或 Rails 版本时收到通知? - 2

    有人知道在发布新版本的Ruby和Rails时收到电子邮件的方法吗?他们有邮件列表,RubyonRails有一个推特,但我不想听到那些随之而来的喧嚣,我只想知道什么时候发布新版本,尤其是那些有安全修复的版本。 最佳答案 从therailsblog获取提要.http://weblog.rubyonrails.org/feed/atom.xml 关于ruby-on-rails-如何在发布新的Ruby或Rails版本时收到通知?,我们在StackOverflow上找到一个类似的问题:

  8. Ruby - 如何将消息长度表示为 2 个二进制字节 - 2

    我正在使用Ruby,我正在与一个网络端点通信,该端点在发送消息本身之前需要格式化“header”。header中的第一个字段必须是消息长度,它被定义为网络字节顺序中的2二进制字节消息长度。比如我的消息长度是1024。如何将1024表示为二进制双字节? 最佳答案 Ruby(以及Perl和Python等)中字节整理的标准工具是pack和unpack。ruby的packisinArray.您的长度应该是两个字节长,并且按网络字节顺序排列,这听起来像是n格式说明符的工作:n|Integer|16-bitunsigned,network(bi

  9. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  10. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

随机推荐