使用不同的字段解析消息
Parsing Message with Varying Fields
我有一个字节流,表示我的应用程序中的消息。消息中有 5 个字段用于演示。流中的第一个字节指示当前流存在哪些消息字段。例如,字节 0 中的0x2
表示当前流仅存在字段 1。
掩码字段可能有 2^5=32 个不同的值。为了解析这种不同宽度的消息,我在下面编写了示例结构和解析器。我的问题是,有没有其他方法可以解析这种动态变化的字段?如果消息有 64 个字段,我将不得不写 64 个案例,这很麻烦。
#include <iostream>
typedef struct
{
uint8_t iDummy0;
int iDummy1;
}__attribute__((packed, aligned(1)))Field4;
typedef struct
{
int iField0;
uint8_t ui8Field1;
short i16Field2;
long long i64Field3;
Field4 stField4;
}__attribute__((packed, aligned(1)))MessageStream;
char* constructIncomingMessage()
{
char* cpStream = new char(1+sizeof(MessageStream)); // Demonstrative message byte array
// 1 byte for Mask, 20 bytes for messageStream
cpStream[0] = 0x1F; // the 0-th byte is a mask marking
// which fields are present for the messageStream
// all 5 fields are present for the example
return cpStream;
}
void deleteMessage( char* cpMessage)
{
delete cpMessage;
}
int main() {
MessageStream messageStream; // Local storage for messageStream
uint8_t ui8FieldMask; // Mask to indicate which fields of messageStream
// are present for the current incoming message
const uint8_t ui8BitIsolator = 0x01;
uint8_t ui8FieldPresent; // ANDed result of Mask and Isolator
std::size_t szParsedByteCount = 0; // Total number of parsed bytes
const std::size_t szMaxMessageFieldCount = 5; // There can be maximum 5 fields in
// the messageStream
char* cpMessageStream = constructIncomingMessage();
ui8FieldMask = (uint8_t)cpMessageStream[0];
szParsedByteCount += 1;
for(std::size_t i = 0; i<szMaxMessageFieldCount; ++i)
{
ui8FieldPresent = ui8FieldMask & ui8BitIsolator;
if(ui8FieldPresent)
{
switch(i)
{
case 0:
{
memcpy(&messageStream.iField0, cpMessageStream+szParsedByteCount, sizeof(messageStream.iField0));
szParsedByteCount += sizeof(messageStream.iField0);
break;
}
case 1:
{
memcpy(&messageStream.ui8Field1, cpMessageStream+szParsedByteCount, sizeof(messageStream.ui8Field1));
szParsedByteCount += sizeof(messageStream.ui8Field1);
break;
}
case 2:
{
memcpy(&messageStream.i16Field2, cpMessageStream+szParsedByteCount, sizeof(messageStream.i16Field2));
szParsedByteCount += sizeof(messageStream.i16Field2);
break;
}
case 3:
{
memcpy(&messageStream.i64Field3, cpMessageStream+szParsedByteCount, sizeof(messageStream.i64Field3));
szParsedByteCount += sizeof(messageStream.i64Field3);
break;
}
case 4:
{
memcpy(&messageStream.stField4, cpMessageStream+szParsedByteCount, sizeof(messageStream.stField4));
szParsedByteCount += sizeof(messageStream.stField4);
break;
}
default:
{
std::cerr << "Undefined Message field number: " << i << 'n';
break;
}
}
}
ui8FieldMask >>= 1; // shift the mask
}
delete deleteMessage(cpMessageStream);
return 0;
}
我要更改的第一件事是在 Field4 上删除__attribute__((packed, aligned(1)))
。这是一个创建镜像打包线格式的结构的黑客,但无论如何这不是您正在处理的格式。
接下来,我会让 MessageStream 成为std::optional<T>
字段的std::tuple
。
您现在知道掩码中std::tuple_size<MessageStream>
可能的位。显然,你不能在ui8FieldMask
中容纳64位,但我认为这是一个需要解决的微不足道的问题。
您可以编写一个从 0 到std::tuple_size<MessageStream>
的 for 循环,以从ui8FieldMask
中提取位以查看设置了哪些位。该逻辑的小问题是你需要编译时常量I
用于std::get<size_t I>(MessageStream)
,而for循环只给你运行时变量。
因此,您需要一个递归template <size_t I> extract(char const*& cpMessageStream, MessageStream&)
,当然还需要专门的extract<0>
。在extract<I>
中,您可以使用typename std::tuple_element<I, MessageStream>::type
将std::optional<T>
放在MessageStream
中的第 I 位置。
- 将结构字段的类型展开为可变模板参数
- 将位字段导出到数组
- 为了方便起见,我应该避免公开私有字段变量吗
- 如何将标准容器作为字段添加到 OMNet++ 消息中?
- 使用不同的字段解析消息
- protobuf,如何在protobuf消息中遍历所有集合字段,我不知道详细信息?(C++)
- 如何在消息文件中获取用户定义的字段
- 预序列化原型消息的某些字段
- 按字段过滤消息
- 如何在 protobuf 中使用反射将预分配的消息设置为字段C++
- 遍历所有字段并在 protobuf 消息中获取它们的值
- 使用快速修复指定 FIX 消息中字段的顺序
- 如何在解析文本格式的 protobuf 消息时忽略错误的字段
- 如何将重复字段部分合并到消息中
- 如何在协议缓冲区中设置嵌套消息的字段
- 使用Protocol Buffers检索带有字段描述符的父消息中的字段名
- 打印Protobuf消息时省略字段
- 消息扩展名不能有必需的字段
- 使用模板检查结构体中的字段,启用函数if,并在失败时给出一个漂亮的错误消息
- (普罗托布夫)无法解析类型 "data.Data" 的消息,因为它缺少必填字段:ID