免费扑克网站,国内优秀wordpress主题,wordpress安全配置文件,洛阳网站公司哪家好本教程提供了使用协议缓冲区的基本介绍。通过逐步创建一个简单的示例应用程序#xff0c;介绍以下内容#xff1a; 1.在.proto文件中定义消息格式。 2.使用 protocol buffer 编译器。 3.使用c protocol buffer API来写入和读取消息。
一、问题描述 将要使用的示例是… 本教程提供了使用协议缓冲区的基本介绍。通过逐步创建一个简单的示例应用程序介绍以下内容 1.在.proto文件中定义消息格式。 2.使用 protocol buffer 编译器。 3.使用c protocol buffer API来写入和读取消息。
一、问题描述 将要使用的示例是一个非常简单的“地址簿”应用程序它可以从文件中读取和写入人们的联系详细信息。地址簿中的每个人都有姓名、ID、电子邮件地址和联系电话号码。
二、定义协议格式 要创建地址簿应用程序需要从.proto文件开始。proto文件中的定义很简单:为要序列化的每个数据结构添加消息然后为消息中的每个字段指定名称和类型。下面是定义消息的.proto文件addressbook.proto。
syntax proto2;package tutorial;message Person {optional string name 1;optional int32 id 2;optional string email 3;enum PhoneType {PHONE_TYPE_UNSPECIFIED 0;PHONE_TYPE_MOBILE 1;PHONE_TYPE_HOME 2;PHONE_TYPE_WORK 3;}message PhoneNumber {optional string number 1;optional PhoneType type 2 [default PHONE_TYPE_HOME];}repeated PhoneNumber phones 4;
}message AddressBook {repeated Person people 1;
} proto文件以包声明开始这有助于防止不同项目之间的命名冲突。在c中生成的类将放在与包名匹配的命名空间中。 接下来您有您的消息定义。消息只是包含一组类型化字段的聚合。许多标准的简单数据类型都可以作为字段类型使用包括bool、int32、float、double和string。您还可以通过使用其他消息类型作为字段类型来为消息添加进一步的结构—在上面的示例中Person消息包含PhoneNumber消息而AddressBook消息包含Person消息。您甚至可以定义嵌套在其他消息中的消息类型—如您所见PhoneNumber类型是在Person中定义的。如果您希望某个字段具有预定义值列表中的一个也可以定义枚举类型—这里您希望指定电话号码可以是以下电话类型之一:PHONE_TYPE_MOBILE、PHONE_TYPE_HOME或PHONE_TYPE_WORK。 每个元素上的“ 1”、“ 2”标记标识该字段在二进制编码中使用的唯一字段号。字段数1-15比更高的数字需要少一个字节编码因此作为优化您可以决定将这些数字用于常用或重复的元素将字段数16或更高的数字用于不太常用的可选元素。repeated字段中的每个元素都需要重新编码字段号因此repeated字段特别适合进行此优化。 optional:该字段可以设置也可以不设置。如果未设置可选字段值则使用默认值。对于简单类型您可以指定自己的默认值就像在示例中为电话号码类型所做的那样。否则使用系统默认值:数字类型为零字符串为空字符串bool为false。对于嵌入式消息默认值始终是消息的“默认实例”或“原型”它没有设置任何字段。调用访问器获取未显式设置的可选(或必需)字段的值总是返回该字段的默认值。 repeated:该字段可以重复任何次数(包括零)。重复值的顺序将保留在protocol buffer中。可以将重复字段视为动态大小的数组。 Required:必须提供该字段的值否则消息将被视为“未初始化”。如果libprotobuf在调试模式下编译序列化未初始化的消息将导致失败。除此之外Required字段的行为与optional字段完全相同。 注意非常小心地将字段标记为Required。如果在某些时候您希望停止写入或发送Required字段将该字段更改为可选字段将会出现问题-旧的阅读器会认为没有此字段的消息是不完整的并且可能会拒绝或无意中丢弃它们。您应该考虑为缓冲区编写特定于应用程序的自定义验证例程。在谷歌内部Required字段是非常不受欢迎的;在proto2语法中定义的大多数消息只使用可选的和重复的。(Proto3根本不支持必填字段。)
三、编译Protocol Buffers 现在有了.proto接下来需要做的就是生成读取和写入AddressBook(因此还有Person和PhoneNumber)消息所需的类。要做到这一点你需要在.proto上运行协议缓冲区编译器协议: 1.如果您还没有安装编译器请下载该包并按照README中的说明进行操作。 2. 现在运行编译器指定源目录(应用程序源代码所在的位置——如果不提供值则使用当前目录)目标目录(生成的代码存放的位置;通常与$SRC_DIR相同)以及.proto的路径。
protoc -I$SRC_DIR --cpp_out$DST_DIR $SRC_DIR/addressbook.proto 因为需要c类所以使用--cpp_out选项——为其他受支持的语言提供了类似的选项。 这将在指定的目标目录中生成以下文件: addressbook.pb.h声明生成类的头文件。 addressbook.pb.cc其中包含类的实现。
四、Protocol Buffers的API 让我们看一下生成的一些代码看看编译器创建了哪些类和函数。如果查看addressbook.pb.h可以看到在addressbook.proto中指定的每条消息都有一个类。仔细观察Person类您可以看到编译器已经为每个字段生成了访问器。例如对于name、id、email和phones字段您可以使用这些方法: // nameinline 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();// idinline bool has_id() const;inline void clear_id();inline int32_t id() const;inline void set_id(int32_t value);// emailinline 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();// phonesinline 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_方法如果该字段已设置则返回true。最后每个字段都有一个clear_方法用于将字段取消设置为空状态。 虽然数字id字段只有上面描述的基本访问器集但name和email字段有两个额外的方法因为它们是字符串——一个mutable_getter让您可以直接获得指向字符串的指针还有一个额外的setter。注意你可以调用mutable_email()即使email还没有设置;它将自动初始化为空字符串。如果在这个示例中有一个重复的消息字段那么它也会有一个mutable_方法而不是set_方法。 repeated字段也有一些特殊的方法——如果你看一下repeated电话字段的方法你会发现有以下方法 1.检查重复字段的_size(换句话说有多少个电话号码与这个Person相关联)。 2.使用其索引获取指定的电话号码。 3.更新指定索引处的现有电话号码。 4.将另一个电话号码添加到消息中然后可以对其进行编辑(重复的标量类型有一个add_它只允许您传入新值)。
五、枚举和嵌套类 生成的代码包括一个PhoneType枚举它对应于您的.proto枚举。您可以将此类型引用为Person::PhoneType将其值引用为Person::PHONE_TYPE_MOBILE、Person::PHONE_TYPE_HOME和Person::PHONE_TYPE_WORK(实现细节稍微复杂一些但您不需要理解它们就可以使用enum)。 编译器还生成了一个名为Person::PhoneNumber的嵌套类。如果查看代码可以看到“真正的”类实际上被称为Person_PhoneNumber但是在Person内部定义的typedef允许您将其视为嵌套类。
六、标准消息方法 每个消息类还包含许多其他方法可以让您检查或操作整个消息包括: 1.bool IsInitialized() const;:检查是否设置了所有必需的字段。 2.string DebugString() const;:返回消息的人类可读的表示形式对调试特别有用。 3.void CopyFrom(const Person from);:用给定消息的值覆盖消息。 4.void Clear();:将所有元素清除回空状态。
七、解析和序列化 每个协议缓冲区类都有使用协议缓冲区二进制格式写入和读取所选类型消息的方法。这些包括: 1.bool SerializeToString(string* output) const;:序列化消息并将字节存储在给定的字符串中。请注意字节是二进制的而不是文本;我们仅将string类用作方便的容器。 2.bool ParseFromString(const string data);:从给定字符串中解析消息。 3.bool serializetostream (ostream* output) const;:将消息写入给定的c ostream。 4.bool parsefroerror (istream* input);解析来自给定c istream的消息。
八、写入message 现在让我们尝试使用协议缓冲类。你希望你的地址簿应用能够做的第一件事是将个人详细信息写入你的地址簿文件。为此您需要创建并填充协议缓冲区类的实例然后将它们写入输出流。 下面是一个程序它从一个文件中读取一个地址簿根据用户输入添加一个新的Person并将新的地址簿再次写回文件。直接调用或引用 protocol编译器生成的代码的部分突出显示。
#include iostream
#include fstream
#include string
#include addressbook.pb.h
using namespace std;// This function fills in a Person message based on user input.
void PromptForAddress(tutorial::Person* person) {cout Enter person ID number: ;int id;cin id;person-set_id(id);cin.ignore(256, \n);cout Enter name: ;getline(cin, *person-mutable_name());cout Enter email address (blank for none): ;string email;getline(cin, email);if (!email.empty()) {person-set_email(email);}while (true) {cout Enter a phone number (or leave blank to finish): ;string number;getline(cin, number);if (number.empty()) {break;}tutorial::Person::PhoneNumber* phone_number person-add_phones();phone_number-set_number(number);cout Is this a mobile, home, or work phone? ;string type;getline(cin, type);if (type mobile) {phone_number-set_type(tutorial::Person::PHONE_TYPE_MOBILE);} else if (type home) {phone_number-set_type(tutorial::Person::PHONE_TYPE_HOME);} else if (type work) {phone_number-set_type(tutorial::Person::PHONE_TYPE_WORK);} else {cout Unknown phone type. Using default. endl;}}
}// Main function: Reads the entire address book from a file,
// adds one person based on user input, then writes it back out to the same
// file.
int main(int argc, char* argv[]) {// Verify that the version of the library that we linked against is// compatible with the version of the headers we compiled against.GOOGLE_PROTOBUF_VERIFY_VERSION;if (argc ! 2) {cerr Usage: argv[0] ADDRESS_BOOK_FILE endl;return -1;}tutorial::AddressBook address_book;{// Read the existing address book.fstream input(argv[1], ios::in | ios::binary);if (!input) {cout argv[1] : File not found. Creating a new file. endl;} else if (!address_book.ParseFromIstream(input)) {cerr Failed to parse address book. endl;return -1;}}// Add an address.PromptForAddress(address_book.add_people());{// Write the new address book back to disk.fstream output(argv[1], ios::out | ios::trunc | ios::binary);if (!address_book.SerializeToOstream(output)) {cerr Failed to write address book. endl;return -1;}}// Optional: Delete all global objects allocated by libprotobuf.google::protobuf::ShutdownProtobufLibrary();return 0;
} 注意GOOGLE_PROTOBUF_VERIFY_VERSION宏。在使用c协议缓冲区库之前执行这个宏是一个很好的实践(尽管不是严格必要的)。它验证程序没有意外地链接到与您所编译的头文件版本不兼容的库版本。如果检测到版本不匹配程序将中止。请注意每个.pb。Cc文件在启动时自动调用此宏。 还要注意在程序末尾对ShutdownProtobufLibrary()的调用。这样做的目的是删除由Protocol Buffer库分配的所有全局对象。这对大多数程序来说是不必要的因为进程无论如何都会退出操作系统会负责回收所有的内存。但是如果您使用内存泄漏检查器要求释放每个最后的对象或者如果您正在编写一个可能由单个进程多次加载和卸载的库那么您可能希望 Protocol Buffers清理所有内容。
九、读取message 当然如果你不能从中获取任何信息地址簿就没有多大用处了!这个示例读取上面示例创建的文件并打印其中的所有信息。
#include iostream
#include fstream
#include string
#include addressbook.pb.h
using namespace std;// Iterates though all people in the AddressBook and prints info about them.
void ListPeople(const tutorial::AddressBook address_book) {for (int i 0; i address_book.people_size(); i) {const tutorial::Person person address_book.people(i);cout Person ID: person.id() endl;cout Name: person.name() endl;if (person.has_email()) {cout E-mail address: person.email() endl;}for (int j 0; j person.phones_size(); j) {const tutorial::Person::PhoneNumber phone_number person.phones(j);switch (phone_number.type()) {case tutorial::Person::PHONE_TYPE_MOBILE:cout Mobile phone #: ;break;case tutorial::Person::PHONE_TYPE_HOME:cout Home phone #: ;break;case tutorial::Person::PHONE_TYPE_WORK:cout Work phone #: ;break;}cout phone_number.number() endl;}}
}// Main function: Reads the entire address book from a file and prints all
// the information inside.
int main(int argc, char* argv[]) {// Verify that the version of the library that we linked against is// compatible with the version of the headers we compiled against.GOOGLE_PROTOBUF_VERIFY_VERSION;if (argc ! 2) {cerr Usage: argv[0] ADDRESS_BOOK_FILE endl;return -1;}tutorial::AddressBook address_book;{// Read the existing address book.fstream input(argv[1], ios::in | ios::binary);if (!address_book.ParseFromIstream(input)) {cerr Failed to parse address book. endl;return -1;}}ListPeople(address_book);// Optional: Delete all global objects allocated by libprotobuf.google::protobuf::ShutdownProtobufLibrary();return 0;
}
十、扩展Protocol Buffer 在发布了使用协议缓冲区的代码之后迟早会想要“改进”协议缓冲区的定义。如果希望新缓冲区向后兼容而旧缓冲区向前兼容(肯定希望这样)那么需要遵循一些规则。在新版本的Protocol Buffer中: 1.不能更改任何现有字段的字段号。 2.不能添加或删除任何required字段。 3.可以删除optional或repeated的字段。 4.可以添加新的optional字段或repeated字段但必须使用新的字段号(即在此Protocol Buffer中从未使用过的字段号甚至没有被删除的字段)。