有没有一种优雅的方法可以将动态长度的字节缓冲区解析为结构?
Is there an elegant way of parsing a byte buffer of dynamic length into a struct?
背景
如此处所述https://godbolt.org/z/xaf95qWee(与下面的代码大致相同),我正在使用一个以内存映射文件形式提供共享内存资源的库。
对于静态大小的消息,读取方法可以非常优雅地return一个结构(匹配缓冲区的布局)并且客户端有一个很好的类型化接口,而不必担心内部结构。
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 条目。
因此,一个示例可能如下所示:
[类型|类型|错误|错误|大小(例如4)|elem|elem|elem|elem|undef|...]
我在想,在这个动态结构中是否有一种类似优雅的读取方式,只有在收到消息时才知道大小。
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)
另一个想法:因为缓冲区有最大大小,所以我可以创建一个尽可能大的 c 数组成员。这将能够直接初始化,但大部分数组都是空的,这听起来效率不高(我知道不能过早优化,但这主要是一个理论问题,而不是生产系统)。
我如何在我的代码中处理它的示例。
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
};
要反序列化对象,您所要做的就是 packet{{data_ptr, data_len}};
我有一个辅助函数,它删除了反序列化多个成员的大量重复和样板,但它对示例并不重要。
这应该很适合您的读取方法
MessageType Read(){
return MessageType{{rawBuffer, sizeof msg}};
}
背景
如此处所述https://godbolt.org/z/xaf95qWee(与下面的代码大致相同),我正在使用一个以内存映射文件形式提供共享内存资源的库。
对于静态大小的消息,读取方法可以非常优雅地return一个结构(匹配缓冲区的布局)并且客户端有一个很好的类型化接口,而不必担心内部结构。
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 条目。 因此,一个示例可能如下所示: [类型|类型|错误|错误|大小(例如4)|elem|elem|elem|elem|undef|...]
我在想,在这个动态结构中是否有一种类似优雅的读取方式,只有在收到消息时才知道大小。
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)
另一个想法:因为缓冲区有最大大小,所以我可以创建一个尽可能大的 c 数组成员。这将能够直接初始化,但大部分数组都是空的,这听起来效率不高(我知道不能过早优化,但这主要是一个理论问题,而不是生产系统)。
我如何在我的代码中处理它的示例。
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
};
要反序列化对象,您所要做的就是 packet{{data_ptr, data_len}};
我有一个辅助函数,它删除了反序列化多个成员的大量重复和样板,但它对示例并不重要。
这应该很适合您的读取方法
MessageType Read(){
return MessageType{{rawBuffer, sizeof msg}};
}