protobuf 中的嵌套消息的使用 主要对set_allocated_和mutable_的使用

简介: protobuf的简单的使用,不过还留下了一个问题,那就是之前主要介绍的都是对简单数据的赋值,简单数据直接采用set_xx()即可,但是如果不是简单变量而是自定义的复合类型变量,就没有简单的set函数调用了,下面看一个简单的例子。

protobuf的简单的使用,不过还留下了一个问题,那就是之前主要介绍的都是对简单数据的赋值,简单数据直接采用set_xx()即可,但是如果不是简单变量而是自定义的复合类型变量,就没有简单的set函数调用了,下面看一个简单的例子。

在网络游戏中,游戏玩家之间的同步是一个最基本的功能,而同步是通过对坐标的广播进行的,因此我们假设一个简单的模型,当一个玩家的位置发生变化时,将玩家的新位置发给地图内所有玩家,根据这个情况写出以下proto文件。

 

[cpp]  view plain  copy
 
  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抽出来成为一个复合数据类型,单独放在一个文件中。这样就构成以下文件。

 

 

[cpp]  view plain  copy
 
  1. file  Player.protoimport "vector.proto";  
  2. message PlayerPos   
  3. {  
  4.     required uint32 playerID = 1;  
  5.     required vector3D  pos = 2;  
  6. };  

编译的时候先编译vector文件,采用import时需要注意路径,本例中两文件在同一目录下。

 

 

[cpp]  view plain  copy
 
  1. protoc --cpp_out=.  vector.proto  Player.proto  

 

proto对应的文件已经生成了,但是该怎么赋值呢,查API查了半天有点不知所以,干脆来看生成的类文件的源代码吧

 

[cpp]  view plain  copy
 
  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开头的,看看这个函数是干嘛的。

[cpp]  view plain  copy
 
  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() 进行赋值看一看

[cpp]  view plain  copy
 
  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的源码,发现一个问题

 

[cpp]  view plain  copy
 
  1. ::vector3D* pos_;  ::google::protobuf::uint32 playerid_;  

上面是PlayerPos中变量的保存形式,发现pos是作为一个指针存储的,如果按照之前的赋值 tmp 是一个局部变量,函数返回时局部变量自动销毁,而pos_保存的仍然是已被销毁的tmp的位置,因此会出错,如果采用new的话就可以解决这个问题,即赋值方法如下:

 

 

[cpp]  view plain  copy
 
  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()

 

[cpp]  view plain  copy
 
  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中又实现了赋值的重载,因此可以这样解决:

 

 

[cpp]  view plain  copy
 
    1. PlayerPos player;  
    2. vector3D  *tmp = player.mutable_pos();  
    3. tmp->x = 1;  
    4. tmp->y = 2;  
    5. tmp->z = 3;  
目录
相关文章
|
开发工具 数据安全/隐私保护 git
Github新的认证方式
Github新的认证方式
538 0
|
编解码 Java 编译器
【Protobuf】Protobuf中的Message语法规范
在Message中定义一个或者多个字段,FieldType是字段的数据类型,可以是基本类型(如int32、string、bool等)或其他定义的Message类型。fieldName是字段的名称,可以根据需求自定义。fieldNumber是字段的唯一标识号,用于在消息的二进制编码中标识字段。
1191 0
yum install、localinstall和groupinstall区别
yum install、localinstall和groupinstall区别
833 0
|
8月前
|
人工智能 Rust IDE
计算机相关的软硬件开发工具分类
本文系统梳理了现代开发工具图谱,涵盖软件、硬件、AI等六大领域。软件开发部分对比了传统工具(如IntelliJ IDEA、SpringBoot)与新兴工具(如AI代码助手Cursor、边缘计算框架Workers),并列出国产替代方案(华为CodeArts、阿里OpenSumi)。硬件开发突出开源EDA工具KiCad和物联网OS Zephyr。AI领域对比了TensorFlow与JAX框架,推荐本地LLM工具Ollama。文章特别设置工具选型指南,针对不同场景推荐方案,如国产化需求建议PaddlePaddle
|
10月前
|
存储 算法 数据可视化
用Python开发猜数字游戏:从零开始的手把手教程
猜数字游戏是编程入门经典项目,涵盖变量、循环、条件判断等核心概念。玩家通过输入猜测电脑生成的随机数,程序给出提示直至猜中。项目从基础实现到功能扩展,逐步提升难度,适合各阶段Python学习者。
767 0
|
关系型数据库 分布式数据库 数据库
沉浸式学习PostgreSQL|PolarDB 4: 跨境电商场景, 快速判断商标|品牌侵权
很多业务场景中需要判断商标侵权, 避免纠纷. 例如 电商的商品文字描述、图片描述中可能有侵权内容. 特别是跨境电商, 在一些国家侵权查处非常严厉. 注册公司名、产品名时可能侵权. 在写文章时, 文章的文字内容、视频内容、图片内容中的描述可能侵权. 例如postgresql是个商标, 如果你使用posthellogresql、postgresqlabc也可能算侵权. 以跨境电商为力, 为了避免侵权, 在发布内容时需要商品描述中出现的品牌名、产品名等是否与已有的商标库有相似. 对于跨境电商场景, 由于店铺和用户众多, 商品的修改、发布是比较高频的操作, 所以需要实现高性能的字符串相似匹配功能.
481 0
|
机器学习/深度学习 编解码 自然语言处理
文生图大模型
DALL·E 是由 OpenAI 开发的基于深度学习的图像生成模型,能够根据文本描述生成原创图像。从 2021 年初的 DALL·E 到 2022 年的 DALL·E 2,再到最新的 DALL·E 3,其功能不断升级,包括生成、扩展、修改图像及生成变体图像。DALL·E 3 在提示优化、清晰度和多风格支持方面进行了增强,广泛应用于定制图像生成、虚拟设定、产品设计和广告营销等领域。
|
安全 Linux 数据处理
Linux命令strip详解
`strip`命令在Linux中用于移除可执行文件和库的符号表及调试信息,减小文件大小,提升运行效率。它的工作原理是删除文件中包含的函数名、变量名等信息。主要参数包括`-s`(移除所有符号)、`-g`(仅移除调试信息)等。在应用时要注意文件备份,因为该操作不可逆。最佳实践是在发布版本中使用,并结合构建流程自动化。
|
开发框架 小程序
用uniapp实现微信小程序的电子签名效果
用uniapp实现微信小程序的电子签名效果
1363 0
用uniapp实现微信小程序的电子签名效果
|
编解码 JavaScript UED
Vue中如何实现动态改变字体大小
Vue中如何实现动态改变字体大小
707 2