C++ Class Mapped Google Protocol Buffer Message
摘要 Google Protocol Buffer 是一個優秀的基於二進位制的網路訊息編解碼框架。應用於專案時,可以節省不少的人力資源、開發時間和程式BUG。但其不足之處是protobuf編譯器生成的C++訊息類(或者Java等其他語言的訊息類)冗餘資料過多,需要依賴於protobuf的編解碼庫,一般情況下都不能用於作為業務邏輯物件。因此大部分情況下,程式都需要另外獨立定義業務邏輯物件,並且使用protobuf定義相應(不一定100%相同)的訊息,並手寫程式碼,在protobuf訊息物件和C++/Java業務物件之間進行轉換。
protobuf訊息還有另外一個缺點,是資料型別不夠豐富,特別是指標,map集合不支援,你支援繼承。通過required、optional屬性的擴充套件可以讓其支援指標,通過多欄位,也可以讓其支援map集合。
因在工程中,有太多的,太過於相似的轉換程式碼,因此才有想法寫一個C++類與google protobuf訊息直接進行轉換的工具。當然一次為藍本,還可以轉換為Java、C#等其他語言。當然轉換後的程式碼不可能100%的可以直接工作,但是可以肯定的是已經完成了99%的工作,就剩下1%一點點的手動修改即可。
關於繼承的對映。基類訊息作為子類訊息的第一個欄位,明白欄位名稱為base;
資料型別對映規則 編號 C++資料型別 限制 protobuf資料型別 備註 1 bool required bool 2 (unsigned) char required uint32 or int32 protobuf 不支援char型別,建議使用int替代 3 (unsigned) short required uint32 or int32 protobuf 不支援short型別,建議使用int替代 4 (unsigned) int required sfixed32 or fixed32 5 (unsigned) long required sfixed32 or fixed32 建議使用 int替代 6 std::string required bytes bytes可以更好的支援中文,protobuf string只支援asscii 不要使用char * ,char[] 等,使用std::string 替代 7 類物件 required 子message 自定義類,而不是系統類物件,需要包含在同一個檔案裡面, 再次沒有遞迴處理其他包含的標頭檔案。後續可能支援 類被對映為一個訊息(只對映一次) 並再次對映為訊息的成員(子訊息) 8 指標型別,支援2種指標: shared_ptr<T> weak_ptr<T> optional 根據T的實際型別進行對映 T的型別為上面 編號1~7中的任意一種。 9 集合型別,包含一下幾種: std::vector<T> std::list<T> std::set<T> std::multiset<T> required repeated bool include_${filename}欄位 根據T的實際型別進行對映 包含一個bool型別的include欄位,用於指示訊息傳遞的過程中是 否包含本欄位(protobuf repeated 欄位存在二義性 ,在訊息不包含repeated欄位時,究竟時刪除還是保留不變) T的型別為上面 編號1~8中的任意一種。 10 map型別,包含2種: std::map<KEY,VALUE> std::multimap<KEY,VALUE> optional repeated repeated boo include_${filename} reptead KEY ${filename}_key reptead VALUE ${filename}_value KEY和VALUE各自對映為一個欄位。在傳輸過程中, 通過下標一一匹配. KEY,VALUE的型別為上面 編號1~8中的任意一種。
3. 自動化工具 工具使用Scala BNF語法進行構建,對C++標頭檔案進行詞法語法分析(主要分析類的宣告和列舉的定義),並提取類的相關資訊用於生成程式碼。 4.工具測試結果。 4.1 測試內容 #ifndef __FOCUS_ENTITY_H__ #define __FOCUS_ENTITY_H__ #include <string> #include <memory> #include <set> #include <odb/database.hxx> #include <odb/tr1/memory.hxx> #include <odb/callback.hxx> #ifdef N_ODB #include <Mutex.h> #include <functional> #include "Focus3800B.h" #include "EntityBuild.h" #endif using std::tr1::shared_ptr; using std::tr1::enable_shared_from_this; using std::tr1::weak_ptr; using std::tr1::dynamic_pointer_cast; class CFocusEntity; class CFocusZone; class CFocusTerminal; class CFocusUser; inline shared_ptr<CFocusZone> toZone( const shared_ptr<CFocusEntity> & en) { return dynamic_pointer_cast<CFocusZone,CFocusEntity>(en); } inline shared_ptr<CFocusTerminal> toTerminal( const shared_ptr<CFocusEntity> & en) { return dynamic_pointer_cast<CFocusTerminal,CFocusEntity>(en); } inline shared_ptr<CFocusUser> toUser( const shared_ptr<CFocusEntity> & en) { return dynamic_pointer_cast<CFocusUser,CFocusEntity>(en); } enum MsgNumPCtoTer { terminal_config = 1, network_basic_config = 2, timenow_config = 3, video_source_config = 16, video_output_config = 17, video_option_config = 18, network_advanced_config = 19, compatibility_config = 20, network_dial_config = 21, fire_wall_config = 22, communication_config = 23, video_config = 24, e1_config = 25, web_config = 26, command_config = 27, camera_select = 256, camera_advance = 257, camera_advance_active = 258, camera_up = 259, camera_down = 260, camera_left = 261, camera_right = 262, camera_near = 263, camera_far = 264, floor_apply = 512, chair_apply = 513, chair_release = 514, chair_viewed = 515, timer_preview = 516, auto_switch_time = 517, broacast_local = 518, force_exit = 519, volume_set = 520, mute_in_set = 521, mute_out_set = 522, dualstream_set = 523, conf_call_set = 524, accept_in_call = 525, reject_in_call = 526, conf_drop = 527, phone_modify = 528, phone_add = 529, feec_apply = 530, phone_histroy_del = 531, picture_show = 532, near_key = 533, far_key = 534, OK_key = 535, Ping = 768, video_loop = 769, restore = 770, out_test = 771, login_console = 1024, logout_console = 1025, restart = 1281 }; #pragma db object polymorphic callback(dbevent) table("tblEntity") class CFocusEntity : public enable_shared_from_this<CFocusEntity> { friend class odb::access; public: CFocusEntity(); virtual ~CFocusEntity(); unsigned int GetId() const { return id_; } void SetId(unsigned int val) { id_ = val; } const std::string & GetName() const { return name_; } void SetName(const std::string & val) { name_ = val; } shared_ptr<CFocusZone> GetParent() const; void SetParent(const shared_ptr<CFocusZone> & val) { parent_ = val; } virtual void dbevent( odb::callback_event e, odb::database& db ); virtual void dbevent( odb::callback_event e, odb::database& db ) const; bool persist(); bool update(); bool erase(); #ifdef N_ODB virtual shared_ptr<CProtobufEntityBuild> CreateBuild() { return nullptr;} #endif void SendIndication(); protected: void InternalSendIndcation(); private: #pragma db id auto unsigned int id_; std::string name_; weak_ptr<CFocusZone> parent_; }; #pragma db object callback(dbevent) table("tblZone") class CFocusZone : public CFocusEntity { friend class odb::access; public: CFocusZone(); ~CFocusZone(); void AddEntity( const shared_ptr<CFocusEntity> & en); void DelEntity( const shared_ptr<CFocusEntity> & en ); void dbevent( odb::callback_event e, odb::database& db ); void dbevent( odb::callback_event e, odb::database& db ) const; shared_ptr<CFocusEntity> FindEntity( unsigned int id, bool recursive = false) ; shared_ptr<CFocusEntity> FindEntity( const std::string & name, bool recursive = false) ; #ifdef N_ODB void foreach( const std::function<void( const shared_ptr<CFocusEntity> & ) > & callback , bool recursive = false, bool isCallItself = false); virtual shared_ptr<CProtobufEntityBuild> CreateBuild(); #endif static void SetRootZone( const shared_ptr<CFocusZone> & rootZone_); static const shared_ptr<CFocusZone> & GetRootZone(); protected: static shared_ptr<CFocusZone> rootZone; private: #pragma db inverse(parent_) std::set< shared_ptr<CFocusEntity> > members_; std::set< int > int_members_; std::set< CFocusEntity > raw_members_; std::map< int ,shared_ptr<CFocusEntity> > map_members_; std::map< int ,int > map_int_members_; std::map< int ,CFocusEntity > map_raw_members_; #ifdef N_ODB mutable dv::CMutex members_mutex; #endif }; #pragma db object callback(dbevent) table("tblTerminal") class CFocusTerminal : public CFocusEntity { friend class odb::access; public: CFocusTerminal(); ~CFocusTerminal(); const std::string & GetAddress() const { return address_; } void SetAddress(const std::string & val) { address_ = val; } unsigned short GetPort() const { return port_; } void SetPort(unsigned short val) { port_ = val; } const std::string & GetUsername() const { return username_; } void SetUsername(const std::string & val) { username_ = val; } const std::string & GetPassword() const { return password_; } void SetPassword(const std::string & val) { password_ = val; } void dbevent( odb::callback_event e, odb::database& db ); void dbevent( odb::callback_event e, odb::database& db ) const; void keepalive_init(); void clear_status(); void send_keepalive(); void remote_login(); void release_connect(); bool IsOnline() const { return recv_service_ok_;} #ifdef N_ODB void OnRecvMessage( T_Message &recvMsg ); // bool IsDualEnable() const { return IsOnline () && dual_stream_status_ ;} bool IsLogin() const { return remote_login_ok_; } //bool IsAdmin() const { return remote_login_ok_ && adminstartor_ok_;} virtual shared_ptr<CProtobufEntityBuild> CreateBuild(); void SetDualEnable( DualLevel level ); //bool IsInConfing() const { return conf_status_.ConfStatus > 0 ? true : false;} void SetCommand( unsigned long ulCommand); void DisConnect(); const T_InformTerConfig & GetTerConfig() const { return ter_config_;} const T_InformSysStatus & GetSysStatus() const { return sys_status_;} const T_InformConfStatus & GetConfStatus() const { return conf_status_;} //const T_InformVideoConfig & GetVideoConfig() const { return video_config_; } const T_VideoOption & GetVideoOption() const { return video_option_; } const T_SetupSoftware & GetSoftwareVersion() const { return software_version_; } void clear_resource(); bool CanSendMediaInfo() const { return is_recv_sys_status_;} unsigned int GetValveRecvRate() const { return valveRecvRate_; } void SetValveRecvRate(unsigned int val) { valveRecvRate_ = val; } unsigned int GetValveSendRate() const { return valveSendRate_; } void SetValveSendRate(unsigned int val) { valveSendRate_ = val; } unsigned int GetValveRecvFrame() const { return valveRecvFrame_; } void SetValveRecvFrame(unsigned int val) { valveRecvFrame_ = val; } unsigned int GetValveSendFrame() const { return valveSendFrame_; } void SetValveSendFrame(unsigned int val) { valveSendFrame_ = val; } unsigned int GetValveLostRate() const { return valveLostRate_; } void SetValveLostRate(unsigned int val) { valveLostRate_ = val; } #endif const std::string & GetH323id() const { return h323id_; } // void SetH323id(std::string val) { h323id_ = val; } const std::string & GetE164() const { return e164_; } //void SetE164(std::string val) { e164_ = val; } unsigned int GetNo() const { return No_; } void SetNo(unsigned int val) { No_ = val; } const std::string & GetContacter() const { return contacter_; } void SetContacter(const std::string & val) { contacter_ = val; } const std::string & GetPhone() const { return phone_; } void SetPhone(const std::string & val) { phone_ = val; } private: std::string address_; unsigned short port_; std::string username_; std::string password_; unsigned int No_; std::string contacter_; std::string phone_; unsigned int valveRecvRate_ ; unsigned int valveSendRate_; unsigned int valveRecvFrame_; unsigned int valveSendFrame_; unsigned int valveLostRate_; #pragma db transient unsigned int connect_id_; #pragma db transient unsigned int timer_id_; #pragma db transient bool recv_service_ok_; #pragma db transient bool remote_login_ok_; #pragma db transient bool adminstartor_ok_; #pragma db transient bool dual_stream_status_; #pragma db transient std::string h323id_; #pragma db transient std::string e164_; #ifdef N_ODB T_InformTerConfig ter_config_; T_InformSysStatus sys_status_; T_InformConfStatus conf_status_; T_VideoOption video_option_; T_Message lastRecvMsg_; T_SetupSoftware software_version_; bool is_recv_sys_status_; #endif }; #pragma db object callback(dbevent) table("tblUser") class CFocusUser : public CFocusEntity { friend class odb::access; public: CFocusUser(); ~CFocusUser(); const std::string & GetPassword() const { return password_; } void SetPassword(const std::string & val) { password_ = val; } void dbevent( odb::callback_event e, odb::database& db ); void dbevent( odb::callback_event e, odb::database& db ) const; #ifdef N_ODB virtual shared_ptr<CProtobufEntityBuild> CreateBuild(); #endif protected: private: std::string password_; }; #endif
4.2輸出結果 package pbmsg; enum MsgNumPCtoTer { terminal_config = 1; network_basic_config = 2; timenow_config = 3; video_source_config = 16; video_output_config = 17; video_option_config = 18; network_advanced_config = 19; compatibility_config = 20; network_dial_config = 21; fire_wall_config = 22; communication_config = 23; video_config = 24; e1_config = 25; web_config = 26; command_config = 27; camera_select = 256; camera_advance = 257; camera_advance_active = 258; camera_up = 259; camera_down = 260; camera_left = 261; camera_right = 262; camera_near = 263; camera_far = 264; floor_apply = 512; chair_apply = 513; chair_release = 514; chair_viewed = 515; timer_preview = 516; auto_switch_time = 517; broacast_local = 518; force_exit = 519; volume_set = 520; mute_in_set = 521; mute_out_set = 522; dualstream_set = 523; conf_call_set = 524; accept_in_call = 525; reject_in_call = 526; conf_drop = 527; phone_modify = 528; phone_add = 529; feec_apply = 530; phone_histroy_del = 531; picture_show = 532; near_key = 533; far_key = 534; OK_key = 535; Ping = 768; video_loop = 769; restore = 770; out_test = 771; login_console = 1024; logout_console = 1025; restart = 1281; }; message CFocusEntity { optional fixed32 id = 1; required bytes name = 2; optional CFocusEntity parent = 3; }; message CFocusZone { required CFocusEntity base = 1; optional bool include_members = 2; repeated CFocusEntity members =3; optional bool include_int_members = 4; repeated sfixed32 int_members =5; optional bool include_raw_members = 6; repeated CFocusEntity raw_members =7; optional bool include_map_members = 8; repeated sfixed32 map_members_key =9; repeated CFocusEntity map_members_value =10; optional bool include_map_int_members = 11; repeated sfixed32 map_int_members_key =12; repeated sfixed32 map_int_members_value =13; optional bool include_map_raw_members = 14; repeated sfixed32 map_raw_members_key =15; repeated CFocusEntity map_raw_members_value =16; }; message CFocusTerminal { required CFocusEntity base = 1; required bytes address = 2; optional uint32 port = 3; required bytes username = 4; required bytes password = 5; optional fixed32 No = 6; required bytes contacter = 7; required bytes phone = 8; optional fixed32 valveRecvRate = 9; optional fixed32 valveSendRate = 10; optional fixed32 valveRecvFrame = 11; optional fixed32 valveSendFrame = 12; optional fixed32 valveLostRate = 13; optional fixed32 connect_id = 14; optional fixed32 timer_id = 15; optional bool recv_service_ok = 16; optional bool remote_login_ok = 17; optional bool adminstartor_ok = 18; optional bool dual_stream_status = 19; required bytes h323id = 20; required bytes e164 = 21; }; message CFocusUser { required CFocusEntity base = 1; required bytes password = 2; }; Process finished with exit code 0
--------------------- 原文:https://blog.csdn.net/love_newzai/article/details/9013183