С++ protobuf, как установить значения для вложенной структуры

Я изучаю С++ ProtoBuf.

У меня есть следующая структура, которую мне нужно сериализовать:

enum phonetype
{
    DESKPHONE,
    MOBILE,
    WIRELESSPHONE
};

struct phonenumber
{
    int ptype;
    string number;
};

struct address
{
    string addr1;
    string addr2;
};

struct college
{
    string collegename;
    string collegeaddress;
};

struct student
{
    int id;
    string name;
    double age;
    string email;

    struct phonenumber phoneN;
    struct address addr;
    struct college col;
};

Я инициализировал структуру следующим образом:

student stud = {123, "Stud_1", 30, "none",
                    {MOBILE, "123456789"}, 
                    {"Boston, US", "None"}, 
                    {"Boston college", "Boston"}};

Теперь я хотел бы создать сериализованную строку этой структуры, для которой я написал следующий файл .proto:

syntax = "proto2";

message studentP
{
  required int32 id = 1;
  required string name = 2;
  required double age = 3;
  optional string email = 4;

  message phonenumberP
  {
    required int32 ptype = 1;
    required string number = 2;
  }
  
  message addressP {
    required string addr1 = 1;
    optional string addr2 = 2;
  }

  message collegeP {
    required string collegename = 1;
    optional string collegeaddress = 2;
  }  
}

В моем коде C++ я устанавливаю значения proto obj следующим образом:

studentP studObj;
studObj.set_name(stud.name);
studObj.set_eid(stud.id);
studObj.set_age(stud.age);

studentP::phonenumberP *phone;
phone->set_ptype(stud.phoneN.ptype);
phone->set_number(stud.phoneN.number);

studentP::addressP *addr;
addr->set_addr1(stud.addr.addr1);
addr->set_addr2(stud.addr.addr2);

studentP::collegeP *coll;
coll->set_collegename(stud.col.collegename);
coll->set_collegeaddress(stud.col.collegeaddress);


string student_str;
studObj.SerializeToString(&student_str);

Выше я отдельно установил значения для внутренних структур класса studentP.

Как установить значения для внутренней структуры studentP объекта studObj?

Нужно ли вызывать SerializeToString для каждой внутренней структуры?


person RKum    schedule 31.07.2020    source источник
comment
Изменить set_eid()set_id(). И указатели phone, addr и coll не инициализированы и не указывают на допустимые типы в памяти. Использование этих указателей приведет к Неопределенное поведение.   -  person Azeem    schedule 01.08.2020


Ответы (1)


На данный момент ваша схема ProtoBuf содержит только определения для вложенных сообщений (телефон, адрес и колледж); но не их соответствующие поля в типе сообщения Student. И вам не нужно иметь отдельные структуры в вашем коде. Они уже объявлены и определены в файлах .pb.h и .pb.cc, сгенерированных protoc. Используйте те. В противном случае будет обречено вручную поддерживать эти типы в коде при использовании библиотеки/фреймворка сериализации/десериализации, которые уже делают это за вас, если у вас нет достаточно веских причин для этого.

Обновленная схема ProtoBuf будет (обратите внимание на поля 5, 6 и 7 ниже):

studentinfo.proto

syntax = "proto2";

package test;

message Student
{
    required int32 id = 1;
    required string name = 2;
    required double age = 3;
    optional string email = 4;

    enum PhoneType
    {
        DESK = 1;
        MOBILE = 2;
        WIRELESS = 3;
    };

    message Phone
    {
        required PhoneType type = 1;
        required string number = 2;
    }

    message Address {
        required string address1 = 1;
        optional string address2 = 2;
    }

    message College {
        required string name = 1;
        optional string address = 2;
    }

    required Phone phone = 5;
    required Address address = 6;
    optional College college = 7;
}

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

Вот полный рабочий пример:

main.cpp

#include <iostream>
#include <string>
#include "studentinfo.pb.h"

int main()
{
    using namespace test;

    // Serialization

    Student s;
    s.set_name("Test");
    s.set_id(123);
    s.set_age(24);

    s.mutable_phone()->set_type(Student_PhoneType_DESK);
    s.mutable_phone()->set_number("+00 123 1234567");

    s.mutable_address()->set_address1("House # 1, Street # 1");
    s.mutable_address()->set_address2("House # 2, Street # 2");

    s.mutable_college()->set_name("XYZ College");
    s.mutable_college()->set_address("College Address Here");

    std::cout << "Serialization:\n\n" << s.DebugString() << "\n\n";
    //s.PrintDebugString();

    std::string serialized;
    if ( !s.SerializeToString( &serialized ) )
    {
        std::cerr << "ERROR: Unable to serialize!\n";
        return -1;
    }

    // Deserialization

    Student deserialized;
    if ( !deserialized.ParseFromString( serialized ) )
    {
        std::cerr << "ERROR: Unable to deserialize!\n";
        return -1;
    }

    std::cout << "Deserialization:\n\n";
    deserialized.PrintDebugString();

    // deserialized.name();
    // deserialized.id();
    // ...
    // deserialized.phone().type();
    // deserialized.phone().number()
    // ...

    return 0;
}

Вывод:

Serialization:

id: 123
name: "Test"
age: 24
phone {
  type: DESK
  number: "+00 123 1234567"
}
address {
  address1: "House # 1, Street # 1"
  address2: "House # 2, Street # 2"
}
college {
  name: "XYZ College"
  address: "College Address Here"
}


Deserialization:

id: 123
name: "Test"
age: 24
phone {
  type: DESK
  number: "+00 123 1234567"
}
address {
  address1: "House # 1, Street # 1"
  address2: "House # 2, Street # 2"
}
college {
  name: "XYZ College"
  address: "College Address Here"
}

Взгляните на файл .pb.h на наличие мутаторов и средств доступа, которые могут вам понадобиться для управления вашими сообщениями.

person Azeem    schedule 01.08.2020
comment
Большое спасибо, Азим, за образец кода. Это помогло лучше понять функции protobuf. - person RKum; 02.08.2020