• 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操作。

  • 相关阅读:
    NPOI导出Excel时出现错误“Maximum column number is 255”
    如何快速访问MSDN某一个类或方法的帮助文档
    beyond compare 比较Xls文件时只显示有差异的列
    Java并发编程:Callable、Future和FutureTask
    JAVA多线程实现的三种方式
    ReentrantReadWriteLock类和ReentrantLock类的区别
    Java多线程之Lock的使用
    MySQL中select * for update锁表的问题
    锁表 for update
    spring事物配置,声明式事务管理和基于@Transactional注解的使用
  • 原文地址:https://www.cnblogs.com/fire909090/p/7609742.html
Copyright © 2020-2023  润新知