If C ++ classes contain or are created / populated from protocol buffer messages

For C ++ protocol buffers, I'm wondering if it is better to keep the protobuf message in my class, or create it from and populate the external protobuf message.

I couldn't find examples describing best practices for this case. I am especially worried about the performance differences between the two projects.

In my processing I will have some cases where I will only read a few fields from my message and then route the message to another process (possibly manipulating the message before sending it back) and other times where my objects will have a long lifespan and will be used many times before being serialized again. In the first case, I'll most likely be working directly with the protobuf message and don't even need my class, execpt, to fit into the existing interface.

Here's a sample post:

package example;
message Example {
  optional string name = 1;
  optional uint32 source = 2;
  optional uint32 destination = 3;
  optional uint32 value_1 = 4;
  optional uint32 value_2 = 5;
  optional uint32 value_3 = 6;
}

      

I could see one of the following projects for my class. I know these classes don't do anything other than data access, but that's not what I'm trying to focus on in this question.


Composition

class Widget
{
 public:
  Widget() : message_() {}
  Widget(const example::Example& other_message)
   : message_(other_message) {}


  const example::Example& getMessage() const
  { return message_; }

  void populateMessage(example::Example& message) const
  { message = message_; }

  // Some example inspectors filled out...
  std::string getName() const
  { return message_.name(); }

  uint32_t getSource() const;
  { return message_.source(); }

  uint32_t getDestination() const;
  uint32_t getValue1() const;
  uint32_t getValue2() const;
  uint32_t getValue3() const;

  // Some example mutators filled out...
  void setName(const std::string& new_name)
  { message_.set_name(new_name); }

  void setSource(uint32_t new_source);
  { message_.set_source(new_source); }

  void setDestination(uint32_t new_destination);
  void setValue1(uint32_t new_value);
  void setValue2(uint32_t new_value);
  void setValue3(uint32_t new_value);

 private:
  example::Example message_;
};

      


Standard data members

class Widget
{
 public:
  Widget();
  Widget(const example::Example& other_message)
   : name_(other_message.name()),
     source_(other_message.source()),
     destination_(other_message.destination()),
     value_1_(other_messsage.value_1()),
     value_2_(other_messsage.value_2()),
     value_3_(other_messsage.value_3())
  {}

  example::Example getMessage() const
  {
    example::Example message;
    populateMessage(message);
    return message;
  }

  void populateMessage(example::Example& message) const
  {
    message.set_name(name_);
    message.set_source(source_);
    message.set_value_1(value_1_);
    message.set_value_2(value_2_);
    message.set_value_3(value_3_);
  }

  // Some example inspectors filled out...
  std::string getName() const
  { return name_; }

  uint32_t getSource() const;
  { return source_; }

  uint32_t getDestination() const;
  uint32_t getValue1() const;
  uint32_t getValue2() const;
  uint32_t getValue3() const;

  // Some example mutators filled out...
  void setName(const std::string& new_name)
  { name_ = new_name; }

  void setSource(uint32_t new_source);
  { source_ = new_source; }

  void setDestination(uint32_t new_destination);
  void setValue1(uint32_t new_value);
  void setValue2(uint32_t new_value);
  void setValue3(uint32_t new_value);

 private:
  std::string name_;
  uint32_t source_;
  uint32_t destination_;
  uint32_t value_1_;
  uint32_t value_2_;
  uint32_t value_3_;
};

      


+3


source to share


1 answer


There is no recognized "best practice" here. I saw many examples of both two and even written programs that worked both ways. Some people have very strong opinions on this, but in my opinion it depends on the use case. For example, as you say, if you plan on sending most of the data to another server, then it makes sense to keep the protobuf object. But in other cases you have a nicer internal view - for example, before protobufs added built-in support for maps, if you had a protobuf that represented a map as a repeated list of key / value pairs, you can convert it std::map

upfront.



+1


source







All Articles