protobuf 中的巢狀訊息的使用 主要對set_allocated_和mutable_的使用
protobuf的簡單的使用,不過還留下了一個問題,那就是之前主要介紹的都是對簡單資料的賦值,簡單資料直接採用set_xx()即可,但是如果不是簡單變數而是自定義的複合型別變數,就沒有簡單的set函式呼叫了,下面看一個簡單的例子。
最近使用protobuf,用set_allocated_pos(),編譯沒問題,但發現賦值不對。
在網路遊戲中,遊戲玩家之間的同步是一個最基本的功能,而同步是通過對座標的廣播進行的,因此我們假設一個簡單的模型,當一個玩家的位置發生變化時,將玩家的新位置發給地圖內所有玩家,根據這個情況寫出以下proto檔案。
1. message PlayerPos
2. {
3. required uint32 playerID = 1;
4. required float posX = 2 ;
5. required float posY = 3 ;
6. };
7.
8. file vector.protomessage vector3D
9. {
10. required float x = 1;
11. required float y = 2;
12. required float z = 3;
13. };
這樣就有一個問題,現在的遊戲都是3D遊戲,因此需要xyz來表示位置,還需要另一組xyz來表示朝向,如果用簡單變數的話就會顯的很亂,而且無論是位置還是朝向其實都是一組xyz,因此可以將xyz抽出來成為一個複合資料型別,單獨放在一個檔案中。這樣就構成以下檔案。
1. file Player.protoimport "vector.proto";
2. message PlayerPos
3. {
4. required uint32 playerID = 1;
5. required vector3D pos = 2;
6. };
編譯的時候先編譯vector檔案,採用import時需要注意路徑,本例中兩檔案在同一目錄下。
1. protoc --cpp_out=. vector.proto Player.proto
proto對應的檔案已經生成了,但是該怎麼賦值呢,查API查了半天有點不知所以,乾脆來看生成的類檔案的原始碼吧
1. // required uint32 playerID = 1;
2. inline bool has_playerid() const;
3. inline void clear_playerid();
4. static const int kPlayerIDFieldNumber = 1;
5. inline ::google::protobuf::uint32 playerid() const;
6. inline void set_playerid(::google::protobuf::uint32 value);
7. // required .vector3D pos = 2;
8. inline bool has_pos() const;
9. inline void clear_pos();
10. static const int kPosFieldNumber = 2;
11. inline const ::vector3D& pos() const;
12. inline ::vector3D* mutable_pos();
13. inline ::vector3D* release_pos();
14. inline void set_allocated_pos(::vector3D* pos);
上面列出了生成的部分原始碼,主要是PlayerPos的操作變數的函式,第一個playID很簡單,可以看到直接使用set_playerid ( ) 即可,但是對於巢狀的pos 發現沒有對應的set_pos方法,不過發現了一個set_allocated_pos()函式,這個函式也是set開頭的,看看這個函式是幹嘛的。
1. inline void PlayerPos::set_allocated_pos(::vector3D* pos)
2. {
3. delete pos_;
4. pos_ = pos;
5. if (pos)
6. {
7. set_has_pos();
8. }
9. else {
10. clear_has_pos();
11. }
12. }
看上去可以賦值,直接呼叫set_allocated_pos() 進行賦值看一看
1. PlayerPos player;
2. vector3D tmp;
3. tmp.x = 1;
4. tmp.y = 2;
5. tmp.z = 3;
6. player.set_allocated_pos(&tmp)
編譯沒問題,但是執行時出現錯誤,而且是很奇怪的錯誤,仔細了檢視一下PlayerPos的原始碼,發現一個問題
1. ::vector3D* pos_; ::google::protobuf::uint32 playerid_;
上面是PlayerPos中變數的儲存形式,發現pos是作為一個指標儲存的,如果按照之前的賦值 tmp 是一個區域性變數,函式返回時區域性變數自動銷燬,而pos_儲存的仍然是已被銷燬的tmp的位置,因此會出錯,如果採用new的話就可以解決這個問題,即賦值方法如下:
1. PlayerPos player;vector3D *tmp = new Vector3D;
2. tmp->x = 1;
3. tmp->y = 2;
4. tmp->z = 3;
5. player.set_allocated_pos(tmp)
這樣即可,編譯執行都沒有問題。
如此之外,還有一種賦值方法,就是呼叫mutable_pos()
1. inline ::vector3D* PlayerPos::mutable_pos()
2. {
3. set_has_pos();
4. if (pos_ == NULL)
5. pos_ = new ::vector3D;
6. return pos_;
7. }
mutable_pos ()
中自己new出了一個vector3D
物件,而vector3D中又實現了賦值的過載,因此可以這樣解決:
1. PlayerPos player;
2. vector3D *tmp = player.mutable_pos();
3. tmp->x = 1;
4. tmp->y = 2;
5. tmp->z = 3;
總結:protobuf 中的巢狀訊息的使用主要對set_allocated_和mutable_的使用
1 使用set_allocated_,賦值的物件需要new出來,不能用區域性的,這裡儲存的是物件的指標。
2 使用mutable_,賦值時候,可以使用區域性變數,因為在呼叫的時,內部做了new操作。
相關文章
- iterate的巢狀使用巢狀
- iOS block巢狀block中weakify的使用iOSBloC巢狀
- javafx和swing巢狀使用的方法Java巢狀
- Repeater中巢狀使用Repeater巢狀
- 自定義訊息和對訊息的理解
- ng-template和ng-container的巢狀使用AI巢狀
- PLSQL Language Referenc-巢狀表-巢狀表和陣列間的重要區別(正確地使用巢狀表)SQL巢狀陣列
- LESS巢狀中的Mixins和classes巢狀
- DataGrid中巢狀使用Repeater (轉)巢狀
- 使用swiper.js建立巢狀的swiperJS巢狀
- Angular 裡使用巢狀 Form 的步驟Angular巢狀ORM
- SQl CASE 語句的巢狀使用方式SQL巢狀
- 巢狀表在表定義中的使用:一個例子巢狀
- js中的函式巢狀和閉包JS函式巢狀
- 巢狀動畫如何使用巢狀動畫
- 巢狀使用 datalist (轉)巢狀
- es中如何使用巢狀物件查詢巢狀物件
- JavaScript中if巢狀assert的方法JavaScript巢狀
- 淺談Vue-router使用方法及動態路由和巢狀路由的使用Vue路由巢狀
- android listView巢狀gridview的使用心得AndroidView巢狀
- setTimeout和箭頭函式巢狀中的this指向函式巢狀
- python使用引數對巢狀字典進行取值Python巢狀
- google protocol buffer——protobuf的基本使用和模型分析GoProtocol模型
- Protobuf_動態訊息-反射反射
- 程式碼的縮排和巢狀巢狀
- Python的if語句多層巢狀怎麼使用Python巢狀
- js如何在引號中巢狀使用引號JS巢狀
- 集合的巢狀巢狀
- 盒子的巢狀巢狀
- Linux 下的程式間通訊:使用管道和訊息佇列Linux佇列
- ViewPager巢狀fragment簡單使用Viewpager巢狀Fragment
- oracle 巢狀表 索引表 使用Oracle巢狀索引
- Python中如何避免字典和元組的多重巢狀的方法Python巢狀
- RabbitMQ訊息佇列的小夥伴: ProtoBuf(Google Protocol Buffer) [轉]MQ佇列GoProtocol
- MQTT 遺囑訊息(Will Message)的使用MQQT
- 訊息佇列ActiveMQ的使用詳解佇列MQ
- 解析微信xml訊息使用的jar包XMLJAR
- 使用gRPC和protobuf建立高效能的APIRPCAPI