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

Is there a standards-compliant way to do zero-copy IPC in C++?

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

现在,这违反了严格的别名规则,但我怀疑它实际上是否会在实践中引起问题。不过,在标准 C++ 中是否有公认的方法来执行此操作?因为我们可能会以这种方式传输数百千兆字节,并且在任何情况下都不想引入将此数据从缓冲区复制到具有 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...
   }
}

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

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

或者我完全错了,按照这些规则就可以了?

如果是,为什么?

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

由于您的目标(大概)是具有众所周知的特征和明确定义的行为的嵌入式平台,因此您可以做出明智的决定来禁用 -fno-strict-aliasing 的严格别名。未定义的行为只是不可移植的行为,如果定义了会先发制人地悲观某些系统。

如果您真的关心遵守标准,那么使用工会是您最好的选择。您可以尝试通过使用某种类型的不执行任何分配的专用类型安全联合或使用某种可以接受指向 char 缓冲区的指针的 span/view class 类型来对其进行 C++ 化,但包装您的 POD 特定 API.

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

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

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