是否有符合标准的方法在 C++ 中进行零拷贝 IPC?

Omn*_*ous 5 c++ ipc type-punning

我有一个应用程序当前从流(套接字、命名、管道、stdin 等)中读取数据到char缓冲区中,然后使用reinterpret_cast将 a Foo *(其中Foo是 POD)指向缓冲区的中间,然后处理通过该指针的缓冲区。

现在,这打破了严格的别名规则,尽管我怀疑它在实践中是否真的会导致问题。尽管如此,在标准 C++ 中是否有一种可接受的方法来做到这一点?因为我们可能会以这种方式传输 100 千兆字节的数据,并且在任何情况下都不想引入将这些数据从缓冲区复制到带有memcpy.

需要明确的是,代码如下所示:

MessageData *msg = new MessageData();
while (ipc.we_have_data()) {
   ipc.read(msg);
   char *buf = msg->data();
   Header *h = reinterpret_cast<Header *>(buf);
   if (h->tag == 0) {
      Payload *p = reinterpret_cast<Payload *>(buf + sizeof(Header));
      do_stuff_with_payload(p);
   } else if (h->tag == 1) {
      // etc...
   }
}
Run Code Online (Sandbox Code Playgroud)

我意识到可能存在对齐问题,但目前我不关心这些问题。数据是由同一个编译器在同一个平台上生成的,因此结构成员的布局不同不存在问题。但是,据我所知,这在技术上打破了严格的别名规则。

有没有一种不违反严格别名规则的有效方法?

还是我完全错了,按照这些规则就好了?

如果是这样,为什么?

编辑:一条已删除的评论指出了别名规则的这个定义,char *获得免费通行证。所以,我的例子实际上并没有违反严格的别名规则。有人知道标准的正确部分吗?

Ser*_*sta 1

不幸的是,该标准对于处理读入字符缓冲区的结构化数据不是很友好。只允许相反的情况:如果您知道要读取 POD 对象,则可以构建一个对象并将其转换为 char 指针的地址传递给任何能够用实际数据填充它的函数,然后使用它通常情况下。

提供的自由通道char *允许在字节级别处理对象,但严格的别名规则通常禁止确定 char 缓冲区实际上包含对象。无论如何,这里的较高风险是对齐问题。

对于其余的,编译器的特定实现完全可以忽略严格的别名规则。然后发生的事情是标准未定义的,但如果您向编译器传递适当的标志,则可以由编译器完美定义。然后,您的程序可能会因不同的编译器或同一编译器的不同配置而中断,因此它将存在可移植性问题,但只要有明确的记录,这可能是可以接受的 - 并且您确信不会出现对齐问题......