4

背景

正如这里所描绘的https://godbolt.org/z/xaf95qWee(与下面的代码大部分相同),我正在使用一个以内存映射文件的形式提供共享内存资源的库。

对于静态大小的消息,read 方法可以非常优雅地返回一个结构(与缓冲区的布局匹配),并且客户端有一个很好的类型化接口,而不必担心内部结构。

template<typename DataType>
struct Message{
    //Metadata
    std::uint32_t type;
    std::uint32_t error;
    DataType data;
};
struct FixedLengthData{
    std::int32_t height;
    std::int32_t age;
};
MessageType Read(){
    MessageType msg;
    std::memcpy(&msg, rawBuffer, sizeof msg);
    return msg;
}
const auto receivedMsg = Read<Message<FixedLengthData>>();

问题/问题

然而,一些数据负载构成动态数组,编码为缓冲区包含数组 S 的大小(即条目数),后跟 S 一些已知类型的条目(通常是整数)。因此,示例可能如下所示: [type|type|error|error|size(eg4)|elem|elem|elem|elem|undef|...]

我想知道,在这种动态结构中是否有类似优雅的阅读方式,只有在收到 msg 时才知道大小。

struct DynamicLengthData{
    std::uint32_t size;
    std::array<std::int32_t, size> data; //obviously doesn't work.
};

我考虑过 的一个想法是使用 std::vector 成员定义动态数据。这种方法的“问题”是向量的数据在堆上,而不是栈上。因此“直接”初始化将不起作用。当然,我可以在没有向量的情况下定义结构,直到 size 成员。然后在第二步中读取大小并专门从缓冲区中读取许多整数,从偏移量开始。但我正在寻找一种没有第二步的方法。

struct StaticPartOfDynamicData{
  //possibly other members
  std::uint32_t size;
};
const auto msg = Read<Message<StaticPartOfDynamicData>>();
std::vector<std::int32_t> dynamicData;
// for 0 to msg.data.size fill vector by reading from buffer at offset sizeof(type + error + otherData + size)

Another idea: Because the buffer has a maximum size, I could create a c-array member that is as large as possible. This will be able to be directly initialized, but most of the array will be empty which does not sound efficient (I know not to optimize prematurely but this is mostly a theoretical question at this point and not for a production system).

4

1 回答 1

0

A example of how i handle it in my code.

class packet
{
public:
  packet(absl::Span<const char> data)
  {
    auto current = data.data();
    std::memcpy(&length_, current, sizeof(length_));
    std::advance(current, sizeof(length_));
  
    vec_.reserve(length_);
    vec_.assign(current, current + length_);
  }
  
  //public stuff as needed

private:
  std::vector<char> vec_{};
  uint16_t length_{};
  //...other members
};

to deserialize the object all you have to do is something like packet{{data_ptr, data_len}};

I have a helper function that removes a lot of the duplication and boilerplate of deserializing multiple members, but its not important to the example.

This should fit nicely into your read method

MessageType Read(){
    return MessageType{{rawBuffer, sizeof msg}};
}
于 2022-02-14T23:41:10.087 回答