我有一个字节流,表示我的应用程序中的一条消息。消息中有5个字段用于演示。流中的第一个字节指示当前流中存在哪些消息字段。例如,字节0中的0x2表示当前流仅存在Field-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)))。这是创建结构的一种技巧,该结构可以反射(reflect)打包的有线格式,但是无论如何,这不是您要处理的格式。

接下来,我将MessageStream设置为std::tuple字段的std::optional<T>

您现在知道掩码中存在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>::typestd::optional<T>中的第I个位置获取MessageStream

关于c++ - 解析具有变化字段的消息,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/62210953/

10-10 01:44