UNIX sockets::recv、std::byte 和严格别名

UNIX sockets::recv, std::byte, and strict aliasing

我正在写一个函数,基本上包装 recv:

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

特别是,我想写入接收一些字节;有时这些字节将是 ASCII 字符串的一部分,有时它们将是整数,或者可能只是一些更高级别协议的一部分的普通“字节”。

我认为在现代 C++ 中抽象它的正确方法可能是写入 std::byte 缓冲区,所以可能是这样的

std::vector<std::byte> buffer;
buffer.resize(100);
recv(socket, buffer.data(), 100, /* flags = */ 0);

我的第一个问题是:写入上述 std::byte 的“缓冲区”是否有任何问题?缓冲区应该是 std::vector<char> 类型吗?我想这没问题,但我不是 100% 确定。

我的第二个问题如下:假设现在我想将 buffer 视为一个字符串。代码

std::string str(buffer.data(), 100);

失败,因为 std::byte* 没有转换为 const char*,我几乎可以肯定

std::string str(reinterpret_cast<const char*>(buffer.data()), 100);

由于严格的别名规则,是未定义的行为。

是用 memcpy:

之类的东西来解决这个问题的唯一方法
std::string ret;
ret.resize(100);
std::memcpy(ret.data(), buffer.data(), 100);

?

如果我想要 std::string_view 怎么办?我可以制作 bufferstd::string_view 而无需先将字节实际复制到某个中间位置吗? std::bit_cast 可以吗?

有趣的是,clang 不会抱怨类似于 std::string(reinterpret_cast... 解决方案的问题:https://godbolt.org/z/7zshhr(甚至使用 -fsanitize=address-fsanitize=undefined 编译)

在这种情况下,charbyte 之间没有区别。事实上,最初这些网络功能是根据 char 数据类型定义的。很久以前,recv 的第二个参数是 char *,而不是现在的 void *

这将使 std::vector<char> 变成 std::string 一个空汉堡。您甚至可以选择完全放弃 std::vector<char>。您可以预先调整 std::string 的大小,然后直接 recv() 调整大小,然后根据收到的字节数再次调整大小。