Google Protocol buffer 學習筆記.下篇-動態編譯

ciscopuke發表於2021-09-09

一 動態編譯

  • 前文介紹了Protobuf的安裝及編譯方法,不過protobuf不僅提供了使用protoc進行靜態編譯的方法,也提供了動態編譯的方法

  • 動態編譯 也就是說無須編譯.proto生成.pb.cc.pb.h

  • 需要使用protobuf中提供的兩個標頭檔案, protobuf中標頭檔案的位置/usr/local/include/google/protobuf

    • google/protobuf/compiler/importer.h 以及 google/protobuf/dynamic_message.h

  • 先看程式碼示例

#include <iostream>#include <string>#include <google/protobuf/compiler/importer.h>#include <google/protobuf/dynamic_message.h>class MyErrorCollector: public google::protobuf::compiler::MultiFileErrorCollector
{    virtual void AddError(const std::string & filename, int line, int column, const std::string & message){        // define import error collector
        printf("%s, %d, %d, %sn", filename.c_str(), line, column, message.c_str());
    }
};int main(){
    google::protobuf::compiler::DiskSourceTree sourceTree; // source tree
    sourceTree.MapPath("", "/home/szw/code/protobuf/tmp2"); // initialize source tree
    MyErrorCollector errorCollector; // dynamic import error collector
    google::protobuf::compiler::Importer importer(&sourceTree, &errorCollector); // importer
    
    importer.Import("test.proto");    
    // find a message descriptor from message descriptor pool
    const google::protobuf::Descriptor * descriptor = importer.pool()->FindMessageTypeByName("lm.helloworld");    if (!descriptor){
        perror("Message is not found!");        exit(-1);
    }    // create a dynamic message factory
    google::protobuf::DynamicMessageFactory * factory = new google::protobuf::DynamicMessageFactory(importer.pool());    // create a const message ptr by factory method and message descriptor 
    const  google::protobuf::Message * tmp = factory->GetPrototype(descriptor);    
    // create a non-const message object by const message ptr
    // define import error collector
    google::protobuf::Message * msg = tmp->New();    
    return 0;
}
  • 標頭檔案<google/protobuf/compiler/importer.h>包含了編譯器物件相關

  • 標頭檔案<google/protobuf/dynamic_message.h>包含了Message_DescriptorFactory相關

  1. 首先初始化一個DiskSourceTree物件, 指明目標.proto檔案的根目錄

  2. 建立一個MyErrorCollector物件, 用於收集動態編譯過程中產生的編譯bug, 該物件需要根據proto提供的純虛基類派生, 並需要重寫其中的純虛擬函式, 使之不再是一個抽象類

  3. 初始化一個Importer物件, 用於動態編譯, 將DiskSourceTreeMyErrorCollector的地址傳入

  4. 將目標.proto動態編譯, 並加入編譯器池中

  5. 可以透過FindMessageTypeName()和訊息名, 來獲取到目標訊息的Message_Descriptor

  6. 建立動態工廠, 並利用工廠方法GetPrototype和目標訊息的Message_Descriptor獲取一個指標const message *

  7. 利用訊息例項指標的New()方法獲取到non-const message ptr



二 型別反射

  • 型別反射即是根據欄位的名稱獲取到欄位型別的一種機制

  • protobuf自身便配備了型別反射機制

  • 需要標頭檔案<google/protobuf/descriptor.h><google/protobuf/message.h>

  • 下面的示例,結合了動態編譯以及型別反射機制

  • 程式碼解析:

  1. 首先使用動態編譯機制,將test1.proto與test2.proto兩個檔案動態編譯進編譯池

  2. 根據訊息名獲取到目標訊息的descriptor

  3. 建立一個動態工廠,並利用訊息的descriptor獲取到const Message * tmp

  4. 利用const Message * tmp的方法New()獲取Message * msg

  5. 根據msg獲取反射器Reflection物件

  6. 利用descriptor可以遍歷各個欄位, 並利用descriptor的cpp_type()方法獲取到每個欄位的型別, 並藉此進行反射填充

程式碼示例

proto檔案

// @file test1.protosyntax = "proto3";
package lm;
message Player
{
    int32 id = 1;    string name = 2;
    repeated string hero = 3;
}
// @file test2.protosyntax = "proto3";import "test1.proto";
package lm;
message Game{
    repeated Player player = 1;    string gameName = 2;
};

cpp

// @file test.cpp#include <iostream>#include <string>#include <google/protobuf/compiler/importer.h> // Importer DiskSourceTree MultiFileErrorCollector#include <google/protobuf/dynamic_message.h> // DynamicMessageFactory #include <google/protobuf/descriptor.h> // Desctriptor FiledDescriptor ... all descriptor#include <google/protobuf/message.h> // Message Reflection MessageFactoryclass MyErrorCollector: public google::protobuf::compiler::MultiFileErrorCollector
{    virtual void AddError(const std::string & filename, int line, int column, const std::string & message){        // define import error collector
        printf("%s, %d, %d, %sn", filename.c_str(), line, column, message.c_str());
    }
};int main(){
    google::protobuf::compiler::DiskSourceTree sourceTree; // source tree
    sourceTree.MapPath("", "./"); // initialize source tree
    MyErrorCollector errorCollector; // dynamic import error collector
    google::protobuf::compiler::Importer importer(&sourceTree, &errorCollector); // importer
    
    importer.Import("test1.proto");
    importer.Import("test2.proto");    
    // find a message descriptor from message descriptor pool
    const google::protobuf::Descriptor * descriptor = importer.pool()->FindMessageTypeByName("lm.Game");    if (!descriptor){
        perror("Message is not found!");        exit(-1);
    }    // create a dynamic message factory
    google::protobuf::DynamicMessageFactory * factory = new google::protobuf::DynamicMessageFactory(importer.pool());    // create a const message ptr by factory method and message descriptor 
    const google::protobuf::Message * tmp = factory->GetPrototype(descriptor);    
    // create a non-const message ptr object by const message ptr
    // define import error collector
    google::protobuf::Message * msg = tmp->New();    
    // get a reflection by msg
    const google::protobuf::Reflection * reflection = msg->GetReflection();    
    // travel the message by message_descriptor
    for (int i=0; i<descriptor->field_count(); ++i){        const google::protobuf::FieldDescriptor * fieldDescriptor = descriptor->field(i);        if (fieldDescriptor->is_repeated()){            switch (fieldDescriptor->cpp_type()){                case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
                {
                    reflection->AddInt32(msg, fieldDescriptor, 10);                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
                {
                    reflection->AddString(msg, fieldDescriptor, "abc");
                }                case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
                {                    printf("Here is a repeadted message: %sn", fieldDescriptor->full_name().c_str());
                }                case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
                {                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
                {                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
                {                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
                {                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
                {                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
                {                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
                {                    break;
                }
                
            }
        }else{            switch (fieldDescriptor->cpp_type()){                case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
                {
                    reflection->SetInt32(msg, fieldDescriptor, 10);                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
                {
                    reflection->SetString(msg, fieldDescriptor, "abc");
                }                case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
                {                    printf("Here is a message: %sn", fieldDescriptor->full_name().c_str());
                }                case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
                {                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
                {                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
                {                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
                {                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
                {                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
                {                    break;
                }                case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
                {                    break;
                }
            }
        }
    }    printf("nmsgn");
    msg->PrintDebugString();    return 0;
}



作者:neilzwshen
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4369/viewspace-2821348/,如需轉載,請註明出處,否則將追究法律責任。

相關文章