Можно ли воссоздать функциональность десериализации Protobuf oneof с помощью Cereal?

Я могу написать код для десериализации прото-сообщения, содержащего раздел oneof, без необходимости заранее знать, что содержит раздел oneof. Я не могу понять, как написать аналогичный набор определений структур, которые я могу десериализовать таким же образом, используя Cereal.

Я использовал Protobufs для сериализации / десериализации некоторых данных, но столкнулся с та же проблема, что и у piaoxu. Поэтому я перешел на использование Cereal.

Мне удалось преобразовать все исходные определения прототипов в определения структур C ++, которые можно сериализовать с помощью Cereal, за исключением определений прототипов, использующих функциональность oneof.

Вот пример набора прототипов определений, которые я хотел бы преобразовать в структуры:

syntax = "proto3";
package Messages;

message A {}

message B {}

message Message {
    oneof contents {
        A a = 1;
        B b = 2;
    }
}

Вот соответствующий код C ++, который я написал для десериализации и анализа полученного Message. Используя сгенерированный код protobuf, я смог десериализовать Message, не зная предварительно, содержит ли он A или B:

void ParseData(const string& data) {
{
    auto message = new Messages::Message();
    message->ParseFromString(data);

    switch (message->contents_case()) {
        case Messages::Message::kA:
            std::cout << "I got an A!" << std::endl;
            break;
        case Messages::Message::kB:
            std::cout << "I got a B!" << std::endl;
            break;
        default:
            break;
    }
}

И вот моя попытка создать эквивалентный набор определений структур:

struct A {};

struct B {};

template <class Contents>
struct Message {
    enum Type {
        A,
        B,
    };

    Type type;

    Contents contents;

    template <class Archive>
    void serialize(Archive& archive) {
        archive(type, contents);
    }
};

И я использую эти структуры для сериализации и отправки такого сообщения:

bool SendA() {
    Message<A> message{};
    ostringstream stream;

    message.type = Message::Type::A;
    message.contents = new A{};

    {
        cereal::PortableBinaryOutputArchive archive(stream);
        archive(message);
    }

    return SendData(stream.str());
}

Этот подход работает до тех пор, пока я не попытаюсь десериализовать полученный Message. Я хотел бы иметь возможность десериализовать Message без предварительного знания, содержит ли он A или B, но, AFAIK, это невозможно.

Неужели это невозможно при моем нынешнем подходе? И если да, то есть ли другой подход, который позволит мне десериализовать аналогичный набор структур, не зная предварительно, что он содержит?


person Evan    schedule 07.07.2019    source источник
comment
Я не знаком с Cereal. В случае, если это помогает, oneof никаким образом не кодируется на проводе. Проводное представление Message, как показано в вашем примере, эквивалентно message Message {A a = 1; B b = 2;}, с согласием джентльменов, что всегда присутствует только один из a и b. Напомним, что любое поле в прото-сообщении, если явно не отмечено required, может быть опущено.   -  person Igor Tandetnik    schedule 08.07.2019
comment
Спасибо, Игорь! Это очень помогло. Как только у меня будет 15 очков репутации и я смогу сделать это, я проголосую за ваш комментарий ????   -  person Evan    schedule 09.07.2019


Ответы (1)


С помощью комментария Игоря, Я смог изменить структуру Message на десериализацию, не зная предварительно, что в ней содержится:

struct A {};

struct B {};

struct Message {
    enum Type {
        A,
        B,
    };

    Type type;

    unique_ptr<::A> a = nullptr;
    unique_ptr<::B> b = nullptr;

    template <class Archive>
    void serialize(Archive& archive) {
        archive(type, contents);
    }
};

Уловка заключалась в том, чтобы не превращать структуру Message в шаблон.

Моим первым подходом к написанию структуры Message было включение храмовой переменной-члена contents для хранения каждой возможной внутренней структуры (A или B). Для этого вам необходимо знать, что содержит сериализованная структура Message , прежде чем вы сможете ее десериализовать, потому что вам нужно сначала создать специализированную переменную Message для десериализации данных.

Вместо этого вы можете создать unique_ptr для каждой возможной внутренней структуры данных. Это позволяет вам создавать Message структуру без шаблонов, и вам не нужно создавать специальную Message переменную для десериализации данных.

person Evan    schedule 08.07.2019