protobuf 中的嵌套消息的使用

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

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

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

 

message PlayerPos{
	required  uint32  playerID = 1;
	required  float   posX = 2 ;
	required  float   posY = 3 ;
}

这样就有一个问题,现在的游戏都是3D游戏,因此需要xyz来表示位置,还需要另一组xyz来表示朝向,如果用简单变量的话就会显的很乱,而且无论是位置还是朝向其实都是一组xyz,因此可以将xyz抽出来成为一个复合数据类型,单独放在一个文件中。这样就构成以下文件。

 

 

file  vector.proto

message  vector3D{
required float x = 1;
required float y = 2;
required float z = 3;
};


file  Player.proto

import "vector.proto";

message PlayerPos {
required uint32 playerID = 1;
required vector3D  pos = 2;
};

 

 

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

 

protoc --cpp_out=.  vector.proto  Player.proto


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

  // required uint32 playerID = 1;
  inline bool has_playerid() const;
  inline void clear_playerid();
  static const int kPlayerIDFieldNumber = 1;
  inline ::google::protobuf::uint32 playerid() const;
  inline void set_playerid(::google::protobuf::uint32 value);

  // required .vector3D pos = 2;
  inline bool has_pos() const;
  inline void clear_pos();
  static const int kPosFieldNumber = 2;
  inline const ::vector3D& pos() const;
  inline ::vector3D* mutable_pos();
  inline ::vector3D* release_pos();
  inline void set_allocated_pos(::vector3D* pos);

 

上面列出了生成的部分源代码,主要是PlayerPos的操作变量的函数,第一个playID很简单,可以看到直接使用set_playerid ( ) 即可,但是对于嵌套的pos 发现没有对应的set_pos方法,不过发现了一个set_allocated_pos() 函数,这个函数也是set开头的,看看这个函数是干嘛的。 

 

inline void PlayerPos::set_allocated_pos(::vector3D* pos) {
  delete pos_;
  pos_ = pos;
  if (pos) {
    set_has_pos();
  } else {
    clear_has_pos();
  }
}


看上去可以赋值,直接调用set_allocated_pos() 进行赋值看一看

 

 

PlayerPos player;
vector3D  tmp;
tmp.x = 1;
tmp.y = 2;
tmp.z = 3;
player.set_allocated_pos(&tmp)


编译没问题,但是运行时出现错误,而且是很奇怪的错误,仔细了查看一下PlayerPos的源码,发现一个问题

 

 

  ::vector3D* pos_;
  ::google::protobuf::uint32 playerid_;


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

 

 

PlayerPos player;
vector3D  *tmp = new Vector3D;
tmp->x = 1;
tmp->y = 2;
tmp->z = 3;
player.set_allocated_pos(tmp)


这样即可,编译运行都没有问题。

 

如此之外,还有一种赋值方法,就是调用mutable_pos()

 

inline ::vector3D* PlayerPos::mutable_pos() {
  set_has_pos();
  if (pos_ == NULL) pos_ = new ::vector3D;
  return pos_;
}


mutable_pos () 中自己new出了一个vector3D 对象,而vector3D中又实现了赋值的重载,因此可以这样解决

 

 

PlayerPos player;
vector3D  *tmp = player.mutable_pos();
tmp->x = 1;
tmp->y = 2;
tmp->z = 3;
相关文章
|
编解码 Java 编译器
【Protobuf】Protobuf中的Message语法规范
在Message中定义一个或者多个字段,FieldType是字段的数据类型,可以是基本类型(如int32、string、bool等)或其他定义的Message类型。fieldName是字段的名称,可以根据需求自定义。fieldNumber是字段的唯一标识号,用于在消息的二进制编码中标识字段。
365 0
|
2月前
|
JSON Go 数据格式
Golang语言结构体链式编程与JSON序列化
这篇文章是关于Go语言中结构体链式编程与JSON序列化的教程,详细介绍了JSON格式的基本概念、结构体的序列化与反序列化、结构体标签的使用以及如何实现链式编程。
39 4
|
5月前
|
开发工具 git
protobuf的复杂结构
protobuf的复杂结构
76 1
|
5月前
|
JSON 负载均衡 安全
对gRPC中常见的 grpc::CreateChannel()这个类所创建的对象所包含的属性做详细介绍
对gRPC中常见的 grpc::CreateChannel()这个类所创建的对象所包含的属性做详细介绍
60 0
|
6月前
gRPC三种流和消息格式(一)
gRPC三种流和消息格式
229 0
|
存储 安全 Go
Golang 语言 method 接收者使用值类型和指针类型的区别
Golang 语言 method 接收者使用值类型和指针类型的区别
53 0
|
6月前
|
C++
[序列化协议] --- protobuf
[序列化协议] --- protobuf
62 0
|
6月前
|
存储 Java Go
|
6月前
|
消息中间件 JSON 监控
Kafka 的消息格式:了解消息结构与序列化
Kafka 作为一款高性能的消息中间件系统,其消息格式对于消息的生产、传输和消费起着至关重要的作用。本篇博客将深入讨论 Kafka 的消息格式,包括消息的结构、序列化与反序列化,以及一些常用的消息格式选项。通过更丰富的示例代码和深入的解析,希望能够帮助大家更好地理解 Kafka 消息的内部机制。
|
6月前
gRPC三种流和消息格式(二)
gRPC三种流和消息格式
90 0