使用 reinterpret_cast 解析模板化网络数据

Using reinterpret_cast to parse templated network data

提问人:asimes 提问时间:11/11/2023 最后编辑:Abderrahmene Rayene Mihoubasimes 更新时间:11/11/2023 访问量:96

问:

我正在研究一种通过网络发送小端数据的协议,我的机器也是小端。我不会关心大端机器。

与通过网络发送的数据分开的是指示如何解释数据的模板文件。

为了这个问题,我们只说唯一的模板指示将数据解析为 4 字节整数,紧跟 8 字节整数。

#include <cstdint>
#include <iostream>

class Int32 final {
public:
    Int32(
        const char* const a_buf,
        const int         a_offset
    ) :
        m_buf   (a_buf),
        m_offset(a_offset)
    {}

    int32_t getVal() const { return *reinterpret_cast<const int32_t*>(m_buf + m_offset); }

private:
    const char* const m_buf;
    const int         m_offset;
};

class Int64 final {
public:
    Int64(
        const char* const a_buf,
        const int         a_offset
    ) :
        m_buf   (a_buf),
        m_offset(a_offset)
    {}

    int64_t getVal() const { return *reinterpret_cast<const int64_t*>(m_buf + m_offset); }

private:
    const char* const m_buf;
    const int         m_offset;
};

int main(int argc, char** argv) {
    // Make up some data for the sake of a SO example
    //
    // The real data is sent over a network with little endian ordering and my
    // machine is also little endian
    char buf[12];
    *reinterpret_cast<int32_t*>(buf + 0) = 42;
    *reinterpret_cast<int64_t*>(buf + 4) = 69;

    // Example of "applying a template" to the data, in this case the "template"
    // is just a 4 byte int followed by a 8 byte int
    const Int32 foo(buf, 0);
    const Int64 bar(buf, 4);

    std::cout << foo.getVal() << " " << bar.getVal() << std::endl;

    return 0;
}

在上面的代码中,我自己填充,在实际代码中,通过复制通过网络发送的数据来填充。bufbuf

我关心的是是否存在与这两种方法相关的任何未定义的行为。getVal

安全吗?reinterpret_castm_buf + m_offset

C++ C++17 重新解释强制转换

评论

4赞 NathanOliver 11/11/2023
那么你就在UB的土地上。在 C++17 中做到这一点并且是合法的方法是创建一个您想要的类型的对象,然后将缓冲区放入该对象中。memcpy
1赞 Jarod42 11/11/2023
您应该具有 和 ,否则您的数据将未对齐。(buf + 0) % alignof(std::uint32_t) == 0(buf + 4) % alignof(std::uint64_t) == 0
0赞 Pepijn Kramer 11/11/2023
你正在做的被称为(二进制)序列化,这是一个不平凡的问题(字节序,不同系统上不同大小的 int,8/16/32/64 位)。对于类,您可能无法确保对象被正确构造(参见 std::bit_cast,在 C++17 中很容易重新实现)。我的建议是使用一个库,例如 flatbuffers 或 protobuf。(还有更多)。
0赞 Pepijn Kramer 11/11/2023
@NathanOliver对于 C++17,它确实是 memcpy,但使用 std::bit_cast (C++20) 或 std::start_lifetime_as (C++23)
0赞 asimes 11/11/2023
@NathanOliver,如果我理解正确的话,你是说我需要实际和成员进入?如果是,那么为什么要避免导致的UB?int32_tint64_tmemcpymemcpyreinterpret_cast

答: 暂无答案