Protocol Buffer C++实例教程: Protocol Buffer API
让我们看看编译器为.proto文件创建了哪些类和函数。如果你查看addressbook.pb.h,你会发现你在addressbook.proto中指定的每条消息都有一个类。仔细查看Person类,您可以看到编译器已经为每个字段生成了访问器。例如,对于name, id, email和phones字段,并且生成了相应的方法:
// name inline bool has_name() const; inline void clear_name(); inline const ::std::string& name() const; inline void set_name(const ::std::string& value); inline void set_name(const char* value); inline ::std::string* mutable_name(); // id inline bool has_id() const; inline void clear_id(); inline int32_t id() const; inline void set_id(int32_t value); // email inline bool has_email() const; inline void clear_email(); inline const ::std::string& email() const; inline void set_email(const ::std::string& value); inline void set_email(const char* value); inline ::std::string* mutable_email(); // phones inline int phones_size() const; inline void clear_phones(); inline const ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phones() const; inline ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phones(); inline const ::tutorial::Person_PhoneNumber& phones(int index) const; inline ::tutorial::Person_PhoneNumber* mutable_phones(int index); inline ::tutorial::Person_PhoneNumber* add_phones();
如您所见,getter与字段的小写名称完全相同,setter方法以set_.开头。每个单数(必填或可选)字段也有has_ methods,如果该字段已设置,则返回true。最后,每个字段都有一个clear_ method,用于将字段设置为空状态。
虽然数字id字段只有上面描述的基本访问器集,但是name和email字段有几个额外的方法,因为它们是字符串:一个mutable_ getter,可以让您直接指向字符串,还有一个额外的设置器。请注意,即使尚未设置email,您也可以调用mutable_email() ,它将自动初始化为空字符串。如果在这个例子中有一个单独的消息字段,它也将有一个mutable_ method,但不是set_ method。
重复字段也有一些特殊的方法:如果你查看重复phone字段的方法,你会发现你可以:
-检查重复字段的_size(换句话说,有多少个电话号码与此人相关联)。
-使用索引获取指定的电话号码。
-更新指定索引处的现有电话号码。
-向消息中添加另一个电话号码,然后您可以编辑它(重复的标量类型有一个add_只允许您传递新值)。
有关协议编译器为任何特定字段定义生成的确切成员的更多信息,请参见C++生成的代码参考手册。
枚举和嵌套类
生成的代码包括对应于.proto枚举的PhoneType枚举。您可以将此类型称为Person::PhoneType,将其值称为Person::MOBILE、Person::HOME和Person::WORK (实现细节稍微复杂一点,但使用枚举时不需要理解它们)。
编译器还为您生成了一个名为Person::PhoneNumber的嵌套类。如果您查看代码,您可以看到“真实”类实际上被称为Person_PhoneNumber,但是Person内部定义的typedef允许您将其视为嵌套类。唯一有区别的情况是,如果您想在另一个文件中转发声明类,您不能在C++中转发声明嵌套类型,但是您可以转发声明Person_PhoneNumber。
标准消息方法
每个消息类还包含许多允许您检查或操作整个消息的其他方法,包括:
bool IsInitialized() const;: 检查是否所有必填字段都已设置。
string DebugString() const;: 返回消息的人类可读表示,对调试特别有用。
void CopyFrom(const Person& from);: 用给定消息的值覆盖消息。
void Clear();: 将所有元素清除回空状态。
下一节描述的这些和输入/输出方法实现了所有C++protocol buffer类共享的消息接口。有关更多信息,请参见完整的消息应用编程接口文档。
解析和序列化
最后,每个protocol buffer类都有使用protocol buffer二进制格式写入和读取所选类型消息的方法。其中包括:
bool SerializeToString(string* output) const;: 序列化消息并将字节存储在给定的字符串中。请注意,字节是二进制的,不是文本;我们只使用string类作为一个方便的容器。
bool ParseFromString(const string& data);: 解析给定字符串中的消息。
bool SerializeToOstream(ostream* output) const;: 将消息写入给定的C++ ostream。
bool ParseFromIstream(istream* input);: 解析来自给定C++ istream的消息。
这些只是为解析和序列化提供的几个选项。同样,有关完整列表,请参见消息应用编程接口参考。
Protocol Buffers 和面向对象设计:Protocol buffer类基本上是哑数据持有者(就像C语言中的结构);在实物模型中,他们不是优秀的一等公民。如果您想向生成的类添加更丰富的行为,最好的方法是将生成的rotocol buffer类包装在特定于应用程序的类中。如果您不能控制.proto文件(例如,如果您正在重用另一个项目中的一个文件)。在这种情况下,您可以使用包装类来创建一个更适合您的应用程序的独特环境的接口:隐藏一些数据和方法,公开方便的函数,等等。永远不要通过继承生成的类来给它们添加行为。这将打破内部机制,无论如何都不是好的面向对象实践。