Protocol Buffers 反射及相關 API
阿新 • • 發佈:2019-01-26
利用 Protocol Buffers 的反射(Reflection)和相關機制能夠實現一些靈活的功能。
通過 message 名稱構建 message 物件
Protocol buffers 提供了一套通過名字來建立 message 物件的方法:
- 獲取 MessageFactory 物件
MessageFactory 類提供了一個 generated_factory() 的靜態函式,此靜態函式可以獲取一個 MessageFactory 物件,此 MessageFactory 物件能夠用來建立被編譯入程式的所有的 message 物件。注意,此 Factory 是一個 Singleton,因此重複多次呼叫 generated_factory 函式不會建立多個 MessageFactory 物件,另外呼叫者也不能通過呼叫 delete 刪除此物件。 - 獲取 DescriptorPool 物件
通過 DescriptorPool 類的 generated_pool() 靜態函式能夠獲取 DescriptorPool 的指標。此 DescriptorPool 中包含了被編譯入程式的 message 的 descriptor。generated_pool 類似於 generated_factory 函式,可以被重複呼叫多次而不會建立多個 DescriptorPool 物件。 - 獲取 message descriptor
有了 DescriptorPool 物件就可以獲取到 message 的 descriptor 了。常見的一個函式是 const Descriptor * FindMessageTypeByName(const string & name) const,此函式可以通過 message 名字獲取到頂層 message 的 descriptor。當然除此之外還有一些 API 可以用來獲取 message discriptor,可以參考相關文件,這裡就不一一詳述了。 - 獲取 message prototype 並構建 message 物件
前面已經講述了獲取 MessageFactory 物件的方法,有了 MessageFactory 物件就可以通過函式 MessageFactory::GetPrototype(const Descriptor * type) 獲取 message prototype(實質上就是一個 message 物件)。通過呼叫 message prototype 的 New 函式則可以構造此型別的 message。
對同一個 Descriptor 多次呼叫 MessageFactory::GetPrototype 函式將返回同一個物件。通過呼叫 prototype 的 New 函式構造的 message 物件必須在 MessageFactory 銷燬前銷燬。
具體編碼如下:
- #include<google/protobuf/message.h>
- #include<google/protobuf/descriptor.h>
- // 這樣使用:
- // createMessageByName("tutorial.AddressBook");
- // 這裡的 tutorial 為 package 名,AddressBook 為 message 名
- google::protobuf::Message* createMessageByName(const std::string& name)
- {
- const google::protobuf::Descriptor* descriptor
- = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(name);
- if(!descriptor)
- return NULL;
- const google::protobuf::Message* prototype
- = google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
- if(!prototype)
- return NULL;
- return prototype->New();
- }
遍歷被編譯的 message
遍歷某個 .proto 檔案中的所有的頂層 message 可以通過直接解析 .proto 檔案來完成(利用 google::protobuf::compiler 相關介面)。如果這個 .proto 檔案被編譯的 C++ 檔案被編譯進了程式,那麼則無需解析 .proto 檔案:
- #include<google/protobuf/message.h>
- #include<google/protobuf/descriptor.h>
- voidIterateProtoFile(const std::string& name)
- {
- // 在 DescriptorPool 中通過 .proto 檔名獲取到 FileDescriptor
- // FileDescriptor 物件描述了整個 .proto 檔案
- const google::protobuf::FileDescriptor* fileDescriptor
- = google::protobuf::DescriptorPool::generated_pool()->FindFileByName(name);
- if(!fileDescriptor)
- return;
- // 遍歷整個 .proto 檔案中定義的頂層 message
- for(int i=0; i<fileDescriptor->message_type_count();++i)
- {
- // message_type 函式引數取值範圍在 0 <= index < message_type_count()
- // 索引值 i 對應 message 在 .proto 檔案中的定義的順序
- const google::protobuf::Descriptor* descriptor = fileDescriptor->message_type(i);
- // ...
- }
- }
一個值得注意的問題是,(我所用到的)反射的 API 需要在 main 函式執行之後呼叫,例如:
- classTest()
- {
- public:
- Test(){
- const google::protobuf::FileDescriptor* fileDescriptor
- = google::protobuf::DescriptorPool::generated_pool()->FindFileByName("name");
- }
- };
- Test g_test;
此例中 fileDescriptor 可能為 NULL。