Protocol Buffers(简称 Protobuf)是 Google 开发的一种高效、轻量级的数据序列化格式,用于跨系统传输和存储结构化数据。它比 JSON 或 XML 更紧凑、解析更快,广泛用于 C++ 项目中的 gRPC 和分布式系统。
Protobuf 使用 .proto
文件定义数据结构。以下介绍基本语法、嵌套类型、复杂类型、多文件组织和命名冲突解决方案。
以下是一个简单的 Student
消息,包含姓名、学号和成绩。
syntax = "proto3";
message Student {
string name = 1;
int32 id = 2;
repeated float grades = 3;
}
编译命令:
protoc --cpp_out=. student.proto
生成的 C++ API(student.pb.h):
class Student {
public:
// 姓名字段 (string name = 1)
const std::string& name() const; // 获取姓名
void set_name(const std::string& value); // 设置姓名
void set_name(std::string&& value); // 移动设置姓名
void set_name(const char* value); // C字符串设置姓名
std::string* mutable_name(); // 获取可修改的姓名指针
void clear_name(); // 清空姓名
// 学号字段 (int32 id = 2)
int32_t id() const; // 获取学号
void set_id(int32_t value); // 设置学号
void clear_id(); // 清空学号
// 成绩数组字段 (repeated float grades = 3)
int grades_size() const; // 获取成绩数组大小
float grades(int index) const; // 获取指定索引的成绩
void set_grades(int index, float value); // 设置指定索引的成绩
void add_grades(float value); // 添加一个成绩
void clear_grades(); // 清空所有成绩
const ::google::protobuf::RepeatedField<float>& grades() const; // 获取整个数组
::google::protobuf::RepeatedField<float>* mutable_grades(); // 获取可修改的数组
// 序列化/反序列化方法
bool SerializeToString(std::string* output) const; // 序列化到字符串
bool ParseFromString(const std::string& data); // 从字符串反序列化
bool SerializeToArray(void* data, int size) const; // 序列化到字节数组
bool ParseFromArray(const void* data, int size); // 从字节数组反序列化
};
syntax = "proto3";
使用 Protobuf 3。int32
(32位整数)、string
(字符串)、float
(浮点数)。std::vector
。1
, 2
, 3
),用于序列化,1-15 占用更少字节。Protobuf 支持在消息中定义嵌套消息,适合表示复杂的数据结构。例如,添加一个 Course
消息嵌套在 Student
中。
syntax = "proto3";
message Course {
string course_name = 1;
float grade = 2;
}
message Student {
string name = 1;
int32 id = 2;
repeated Course courses = 3;
}
编译命令:
protoc --cpp_out=. nested_student.proto
生成的 C++ API(nested_student.pb.h):
// Course 类
class Course {
public:
// 课程名字段 (string course_name = 1)
const std::string& course_name() const;
void set_course_name(const std::string& value);
void set_course_name(std::string&& value);
void set_course_name(const char* value);
std::string* mutable_course_name();
void clear_course_name();
// 成绩字段 (float grade = 2)
float grade() const;
void set_grade(float value);
void clear_grade();
};
// Student 类
class Student {
public:
// 姓名和学号字段(同上面基本示例)
const std::string& name() const;
void set_name(const std::string& value);
int32_t id() const;
void set_id(int32_t value);
// 课程数组字段 (repeated Course courses = 3)
int courses_size() const; // 获取课程数组大小
const Course& courses(int index) const; // 获取指定索引的课程
Course* mutable_courses(int index); // 获取可修改的指定索引课程
Course* add_courses(); // 添加新课程并返回指针
void clear_courses(); // 清空所有课程
const ::google::protobuf::RepeatedPtrField<Course>& courses() const; // 获取整个数组
::google::protobuf::RepeatedPtrField<Course>* mutable_courses(); // 获取可修改的数组
};
Course
定义在 Student
内部,Student
的 courses
字段是一个 Course
数组。Course
类。Protobuf 提供复杂类型,如 enum
(枚举)、map
(键值对)和 oneof
(联合类型)。
定义有限的状态或类型:
syntax = "proto3";
enum GradeLevel {
UNKNOWN = 0;
FRESHMAN = 1;
SOPHOMORE = 2;
JUNIOR = 3;
SENIOR = 4;
}
message Student {
string name = 1;
int32 id = 2;
GradeLevel level = 3;
}
编译命令:
protoc --cpp_out=. enum_student.proto
生成的 C++ API(enum_student.pb.h):
// 枚举定义
enum GradeLevel {
GradeLevel_UNKNOWN = 0,
GradeLevel_FRESHMAN = 1,
GradeLevel_SOPHOMORE = 2,
GradeLevel_JUNIOR = 3,
GradeLevel_SENIOR = 4
};
// 枚举工具函数
const std::string& GradeLevel_Name(GradeLevel value); // 枚举值转字符串
bool GradeLevel_Parse(const std::string& name, GradeLevel* value); // 字符串转枚举值
const GradeLevel GradeLevel_MIN = GradeLevel_UNKNOWN; // 最小值
const GradeLevel GradeLevel_MAX = GradeLevel_SENIOR; // 最大值
// Student 类
class Student {
public:
// 基本字段(姓名、学号同前面示例)
const std::string& name() const;
void set_name(const std::string& value);
int32_t id() const;
void set_id(int32_t value);
// 年级字段 (GradeLevel level = 3)
GradeLevel level() const; // 获取年级
void set_level(GradeLevel value); // 设置年级
void clear_level(); // 清空年级(设为默认值UNKNOWN)
};
enum
:定义一组固定值,UNKNOWN
通常设为 0 作为默认值。GradeLevel_
。表示键值映射,类似 C++ 的 std::map
:
syntax = "proto3";
message Student {
string name = 1;
int32 id = 2;
map course_grades = 3; // 课程名到成绩的映射
}
编译命令:
protoc --cpp_out=. map_student.proto
生成的 C++ API(map_student.pb.h):
class Student {
public:
// 基本字段(姓名、学号同前面示例)
const std::string& name() const;
void set_name(const std::string& value);
int32_t id() const;
void set_id(int32_t value);
// Map 字段 (map course_grades = 3)
int course_grades_size() const; // 获取map大小
void clear_course_grades(); // 清空map
// 访问map的方法
const ::google::protobuf::Map<std::string, float>& course_grades() const; // 获取只读map
::google::protobuf::Map<std::string, float>* mutable_course_grades(); // 获取可修改map
// Map 的常用操作示例(通过 mutable_course_grades() 获取后使用)
// (*student.mutable_course_grades())["Math"] = 95.0; // 设置值
// student.course_grades().at("Math"); // 获取值
// student.course_grades().find("Math") != student.course_grades().end(); // 检查键是否存在
};
map
:键为字符串,值为浮点数。std::map
,使用 at()
、find()
等方法。表示字段中只有一个可以被设置:
syntax = "proto3";
message Contact {
oneof contact_info {
string email = 1;
string phone = 2;
}
}
message Student {
string name = 1;
int32 id = 2;
Contact contact = 3;
}
编译命令:
protoc --cpp_out=. oneof_student.proto
生成的 C++ API(oneof_student.pb.h):
// Contact 类
class Contact {
public:
// oneof 字段的枚举类型
enum ContactInfoCase {
kEmail = 1,
kPhone = 2,
CONTACT_INFO_NOT_SET = 0
};
// oneof 状态查询
ContactInfoCase contact_info_case() const; // 获取当前设置的字段类型
void clear_contact_info(); // 清空oneof字段
// Email 字段 (string email = 1)
bool has_email() const; // 检查是否设置了email
const std::string& email() const; // 获取email
void set_email(const std::string& value); // 设置email(会清空phone)
void set_email(std::string&& value);
void set_email(const char* value);
std::string* mutable_email();
void clear_email();
// Phone 字段 (string phone = 2)
bool has_phone() const; // 检查是否设置了phone
const std::string& phone() const; // 获取phone
void set_phone(const std::string& value); // 设置phone(会清空email)
void set_phone(std::string&& value);
void set_phone(const char* value);
std::string* mutable_phone();
void clear_phone();
};
// Student 类
class Student {
public:
// 基本字段
const std::string& name() const;
void set_name(const std::string& value);
int32_t id() const;
void set_id(int32_t value);
// Contact 字段 (Contact contact = 3)
bool has_contact() const; // 检查是否有联系信息
const Contact& contact() const; // 获取联系信息
Contact* mutable_contact(); // 获取可修改的联系信息
void clear_contact(); // 清空联系信息
};
oneof
:确保 email
或 phone
只有一个被设置。has_*()
方法检查特定字段是否被设置,以及 *_case()
方法获取当前设置的字段类型。大型项目通常将消息定义拆分为多个 .proto
文件,通过 import
引用。
course.proto:
syntax = "proto3";
package school;
message Course {
string course_name = 1;
float grade = 2;
}
编译命令:
protoc --cpp_out=. course.proto
生成的 C++ API(course.pb.h):
namespace school {
class Course {
public:
// 课程名字段 (string course_name = 1)
const std::string& course_name() const;
void set_course_name(const std::string& value);
void set_course_name(std::string&& value);
void set_course_name(const char* value);
std::string* mutable_course_name();
void clear_course_name();
// 成绩字段 (float grade = 2)
float grade() const;
void set_grade(float value);
void clear_grade();
// 序列化/反序列化方法
bool SerializeToString(std::string* output) const;
bool ParseFromString(const std::string& data);
bool SerializeToArray(void* data, int size) const;
bool ParseFromArray(const void* data, int size);
};
} // namespace school
student.proto:
syntax = "proto3";
package school;
import "course.proto";
message Student {
string name = 1;
int32 id = 2;
repeated Course courses = 3;
}
编译命令:
protoc --cpp_out=. course.proto student.proto
生成的 C++ API(student.pb.h):
namespace school {
class Student {
public:
// 基本字段
const std::string& name() const;
void set_name(const std::string& value);
int32_t id() const;
void set_id(int32_t value);
// 课程数组字段 (repeated Course courses = 3)
int courses_size() const;
const Course& courses(int index) const; // 注意:这里的Course来自同一个namespace
Course* mutable_courses(int index);
Course* add_courses();
void clear_courses();
const ::google::protobuf::RepeatedPtrField<Course>& courses() const;
::google::protobuf::RepeatedPtrField<Course>* mutable_courses();
// 序列化/反序列化方法
bool SerializeToString(std::string* output) const;
bool ParseFromString(const std::string& data);
};
} // namespace school
.proto
文件,路径相对于当前文件。school
包,因此生成的类都在 school
命名空间下。当多个 .proto
文件定义了同名消息时,使用 package
区分命名空间。
department1.proto:
syntax = "proto3";
package dept1;
message Student {
string name = 1;
int32 id = 2;
}
编译命令:
protoc --cpp_out=. department1.proto
生成的 C++ API(department1.pb.h):
namespace dept1 {
class Student {
public:
// 学生姓名字段 (string name = 1)
const std::string& name() const;
void set_name(const std::string& value);
void set_name(std::string&& value);
void set_name(const char* value);
std::string* mutable_name();
void clear_name();
// 学生ID字段 (int32 id = 2)
int32_t id() const;
void set_id(int32_t value);
void clear_id();
// 序列化/反序列化方法
bool SerializeToString(std::string* output) const;
bool ParseFromString(const std::string& data);
};
} // namespace dept1
department2.proto:
syntax = "proto3";
package dept2;
message Student {
string department = 1;
int32 id = 2;
}
编译命令:
protoc --cpp_out=. department2.proto
生成的 C++ API(department2.pb.h):
namespace dept2 {
class Student {
public:
// 部门字段 (string department = 1)
const std::string& department() const;
void set_department(const std::string& value);
void set_department(std::string&& value);
void set_department(const char* value);
std::string* mutable_department();
void clear_department();
// 学生ID字段 (int32 id = 2)
int32_t id() const;
void set_id(int32_t value);
void clear_id();
// 序列化/反序列化方法
bool SerializeToString(std::string* output) const;
bool ParseFromString(const std::string& data);
};
} // namespace dept2
使用时的 C++ 代码:
#include "department1.pb.h"
#include "department2.pb.h"
int main() {
// 使用不同命名空间避免冲突
dept1::Student student1;
student1.set_name("张三");
student1.set_id(1001);
dept2::Student student2;
student2.set_department("计算机科学");
student2.set_id(2001);
return 0;
}
dept1
和 dept2
定义不同的命名空间。dept1::Student
和 dept2::Student
,避免冲突。protoc --cpp_out=. department1.proto department2.proto
以下是与 .proto
文件配套的 C++ 示例,展示如何使用嵌套类型、复杂类型和多文件定义。
course.proto:
syntax = "proto3";
package school;
message Course {
string course_name = 1;
float grade = 2;
}
student.proto:
syntax = "proto3";
package student;
import "course.proto";
enum Level {
NONE = 0;
YEAR1 = 1;
YEAR2 = 2;
YEAR3 = 3;
}
message Info {
oneof contact {
string email = 1;
string phone = 2;
}
}
message Student {
string name = 1;
int32 id = 2;
repeated school.Course courses = 3;
Level level = 4;
map course_scores = 5;
Info info = 6;
}
完整编译命令:
protoc --cpp_out=. course.proto student.proto
生成的完整 C++ API 概览:
course.pb.h:
namespace school {
class Course {
public:
const std::string& course_name() const;
void set_course_name(const std::string& value);
float grade() const;
void set_grade(float value);
// ... 其他标准方法
};
}
student.pb.h:
namespace student {
// 枚举
enum Level {
Level_NONE = 0,
Level_YEAR1 = 1,
Level_YEAR2 = 2,
Level_YEAR3 = 3
};
// Info 类(包含 oneof)
class Info {
public:
enum ContactCase { kEmail = 1, kPhone = 2, CONTACT_NOT_SET = 0 };
ContactCase contact_case() const;
bool has_email() const;
const std::string& email() const;
void set_email(const std::string& value);
bool has_phone() const;
const std::string& phone() const;
void set_phone(const std::string& value);
// ... 其他方法
};
// Student 类(综合示例)
class Student {
public:
// 基本字段
const std::string& name() const;
void set_name(const std::string& value);
int32_t id() const;
void set_id(int32_t value);
// 课程数组(来自其他package)
int courses_size() const;
const ::school::Course& courses(int index) const; // 注意跨package引用
::school::Course* add_courses();
// 枚举字段
Level level() const;
void set_level(Level value);
// Map字段
const ::google::protobuf::Map<std::string, float>& course_scores() const;
::google::protobuf::Map<std::string, float>* mutable_course_scores();
// 嵌套消息字段
bool has_info() const;
const Info& info() const;
Info* mutable_info();
// 序列化方法
bool SerializeToString(std::string* output) const;
bool ParseFromString(const std::string& data);
};
} // namespace student
course.proto
使用 school
,student.proto
使用 student
,区分命名空间。Student
引用 school::Course
作为嵌套数组。Level
:简化的枚举,选项为 NONE
、YEAR1
等。map
:课程名到成绩的映射。Info
:用 oneof
表示单一联系方式(邮箱或电话)。student.proto
导入 course.proto
。#include
#include
#include "student.pb.h"
#include "course.pb.h"
int main() {
// 创建 Student 对象
student::Student stu;
stu.set_name("张三");
stu.set_id(1001);
stu.set_level(student::Level::Level_YEAR1); // 修正:使用正确的枚举名
// 添加嵌套 Course
school::Course* course = stu.add_courses();
course->set_course_name("数学");
course->set_grade(90.0);
// 添加 map 数据
(*stu.mutable_course_scores())["物理"] = 85.5;
// 设置联系方式
stu.mutable_info()->set_email("[email protected]");
// 序列化为二进制
std::string serialized_data;
if (!stu.SerializeToString(&serialized_data)) {
std::cerr << "序列化失败!" << std::endl;
return 1;
}
std::cout << "序列化数据大小: " << serialized_data.size() << " 字节" << std::endl;
// 反序列化
student::Student new_stu;
if (!new_stu.ParseFromString(serialized_data)) {
std::cerr << "反序列化失败!" << std::endl;
return 1;
}
// 输出结果
std::cout << "姓名: " << new_stu.name() << std::endl;
std::cout << "学号: " << new_stu.id() << std::endl;
std::cout << "年级: " << new_stu.level() << std::endl;
std::cout << "课程: " << new_stu.courses(0).course_name() << ", 成绩: " << new_stu.courses(0).grade() << std::endl;
// 安全访问 map
auto& scores = new_stu.course_scores();
auto it = scores.find("物理");
if (it != scores.end()) {
std::cout << "物理成绩: " << it->second << std::endl;
}
// 检查联系方式类型
if (new_stu.info().contact_case() == student::Info::kEmail) {
std::cout << "邮箱: " << new_stu.info().email() << std::endl;
}
return 0;
}
编译 .proto
文件:
protoc --cpp_out=. course.proto student.proto
生成 course.pb.h/cc
和 student.pb.h/cc
。
编译 C++ 代码:
g++ -o student_example student.pb.cc course.pb.cc main.cpp -lprotobuf
运行:
./student_example
预期输出:
序列化数据大小: 约 40 字节
姓名: 张三
学号: 1001
年级: 1
课程: 数学, 成绩: 90
物理成绩: 85.5
邮箱: [email protected]
// 检查 repeated 字段是否为空
if (student.courses_size() > 0) {
std::cout << "第一门课程: " << student.courses(0).course_name() << std::endl;
}
// 安全访问 map
auto& scores = student.course_scores();
if (scores.find("数学") != scores.end()) {
std::cout << "数学成绩: " << scores.at("数学") << std::endl;
}
// 检查 oneof 字段
switch (student.info().contact_case()) {
case student::Info::kEmail:
std::cout << "邮箱: " << student.info().email() << std::endl;
break;
case student::Info::kPhone:
std::cout << "电话: " << student.info().phone() << std::endl;
break;
default:
std::cout << "无联系方式" << std::endl;
break;
}
// 移动语义(C++11)
std::string name = "李四";
student.set_name(std::move(name)); // 避免复制
// 直接修改 repeated 字段
for (int i = 0; i < student.courses_size(); ++i) {
student.mutable_courses(i)->set_grade(student.mutable_courses(i)->grade() + 5.0);
}
// 批量操作 map
auto* scores = student.mutable_course_scores();
(*scores)["数学"] = 95.0;
(*scores)["英语"] = 88.0;
(*scores)["物理"] = 92.0;
// 序列化到文件
std::ofstream output("student.bin", std::ios::binary);
student.SerializeToOstream(&output);
// 从文件反序列化
std::ifstream input("student.bin", std::ios::binary);
student.ParseFromIstream(&input);
// 序列化到字节数组
char buffer[1024];
int size = student.ByteSizeLong();
if (size <= sizeof(buffer)) {
student.SerializeToArray(buffer, size);
}
// JSON 格式序列化(需要额外库支持)
// 注意:这需要链接 protobuf 的 JSON 支持库
#include
std::string json_output;
google::protobuf::util::MessageToJsonString(student, &json_output);
message OptimizedStudent {
// 1-15 占用1字节,频繁使用的字段放在前面
string name = 1; // 最常用
int32 id = 2; // 次常用
Level level = 3; // 常用
// 16-2047 占用2字节
repeated Course courses = 16; // 不太常用
map scores = 17; // 不太常用
// 保留一些编号用于未来扩展
reserved 4 to 15;
reserved "old_field_name";
}
// 使用对象池避免频繁分配
class StudentPool {
private:
std::vector<std::unique_ptr<student::Student>> pool_;
std::mutex mutex_;
public:
std::unique_ptr<student::Student> acquire() {
std::lock_guard<std::mutex> lock(mutex_);
if (!pool_.empty()) {
auto obj = std::move(pool_.back());
pool_.pop_back();
obj->Clear(); // 清空但不释放内存
return obj;
}
return std::make_unique<student::Student>();
}
void release(std::unique_ptr<student::Student> obj) {
std::lock_guard<std::mutex> lock(mutex_);
pool_.push_back(std::move(obj));
}
};
// 流式处理大量数据
void processLargeDataset(const std::string& filename) {
std::ifstream input(filename, std::ios::binary);
student::Student student;
// 读取多个学生记录
google::protobuf::io::IstreamInputStream raw_input(&input);
google::protobuf::io::CodedInputStream coded_input(&raw_input);
uint32_t size;
while (coded_input.ReadVarint32(&size)) {
auto limit = coded_input.PushLimit(size);
if (student.ParseFromCodedStream(&coded_input)) {
// 处理单个学生数据
processStudent(student);
}
coded_input.PopLimit(limit);
student.Clear(); // 重用对象
}
}
#include
// 以可读文本格式输出
std::string debug_string = student.DebugString();
std::cout << "Student内容:\n" << debug_string << std::endl;
// 简化的调试输出
std::string short_debug = student.ShortDebugString();
std::cout << "简化输出: " << short_debug << std::endl;
// 转换为文本格式
std::string text_format;
google::protobuf::TextFormat::PrintToString(student, &text_format);
std::cout << "文本格式:\n" << text_format << std::endl;
// 检查必填字段(proto3中所有字段都是可选的,但可以自定义验证)
bool validateStudent(const student::Student& stu) {
if (stu.name().empty()) {
std::cerr << "错误:学生姓名不能为空" << std::endl;
return false;
}
if (stu.id() <= 0) {
std::cerr << "错误:学生ID必须为正数" << std::endl;
return false;
}
// 验证课程成绩
for (const auto& course : stu.courses()) {
if (course.grade() < 0 || course.grade() > 100) {
std::cerr << "错误:课程 " << course.course_name()
<< " 成绩超出范围 [0,100]" << std::endl;
return false;
}
}
return true;
}
// 完善的序列化错误处理
bool serializeStudentSafely(const student::Student& stu, std::string& output) {
if (!validateStudent(stu)) {
return false;
}
try {
if (!stu.SerializeToString(&output)) {
std::cerr << "序列化失败:可能是内存不足或数据损坏" << std::endl;
return false;
}
// 验证序列化结果
student::Student test_stu;
if (!test_stu.ParseFromString(output)) {
std::cerr << "序列化验证失败:生成的数据无法反序列化" << std::endl;
return false;
}
return true;
} catch (const std::exception& e) {
std::cerr << "序列化异常: " << e.what() << std::endl;
return false;
}
}
Linux (Ubuntu/Debian):
# 安装编译器和开发库
sudo apt-get update
sudo apt-get install protobuf-compiler libprotobuf-dev
# 验证安装
protoc --version
Linux (CentOS/RHEL):
# 使用 yum 或 dnf
sudo yum install protobuf-compiler protobuf-devel
# 或者
sudo dnf install protobuf-compiler protobuf-devel
macOS:
# 使用 Homebrew
brew install protobuf
# 使用 MacPorts
sudo port install protobuf3-cpp
从源码编译:
# 下载源码
git clone https://github.com/protocolbuffers/protobuf.git
cd protobuf
# 编译安装
./autogen.sh
./configure
make -j$(nproc)
make check
sudo make install
sudo ldconfig # Linux 需要
创建 CMakeLists.txt
:
cmake_minimum_required(VERSION 3.10)
project(ProtobufExample)
# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 查找 Protobuf
find_package(Protobuf REQUIRED)
include_directories(${Protobuf_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# 编译 .proto 文件
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS
course.proto
student.proto
)
# 创建可执行文件
add_executable(student_example
main.cpp
${PROTO_SRCS}
${PROTO_HDRS}
)
# 链接 Protobuf 库
target_link_libraries(student_example ${Protobuf_LIBRARIES})
构建项目:
mkdir build && cd build
cmake ..
make
CXX = g++
CXXFLAGS = -std=c++11 -Wall -O2
LIBS = -lprotobuf
PROTOC = protoc
# 源文件
PROTO_FILES = course.proto student.proto
PROTO_OBJS = $(PROTO_FILES:.proto=.pb.o)
CPP_OBJS = main.o
TARGET = student_example
# 默认目标
all: $(TARGET)
# 编译 .proto 文件
%.pb.cc %.pb.h: %.proto
$(PROTOC) --cpp_out=. $<
# 编译目标文件
%.pb.o: %.pb.cc
$(CXX) $(CXXFLAGS) -c $< -o $@
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
# 链接可执行文件
$(TARGET): $(PROTO_OBJS) $(CPP_OBJS)
$(CXX) $^ $(LIBS) -o $@
# 清理
clean:
rm -f *.pb.h *.pb.cc *.o $(TARGET)
# 伪目标
.PHONY: all clean
syntax = "proto3";
message StudentV2 {
string name = 1;
int32 id = 2;
repeated Course courses = 3;
// 新增字段放在后面,使用新的编号
string email = 4; // 新增:邮箱
int64 create_time = 5; // 新增:创建时间
// 保留已删除的字段编号
reserved 6, 7;
reserved "old_field1", "old_field2";
}
syntax = "proto3";
message Student {
// 使用小写字母和下划线
string full_name = 1; // 好:清晰的命名
int32 student_id = 2; // 好:避免简写
repeated Course course_list = 3; // 好:表明是列表
// 避免的命名方式
// string name = 1; // 不好:太简略
// int32 id = 2; // 不好:含义不明确
// string fName = 3; // 不好:使用了驼峰命名
}
syntax = "proto3";
// 定义通用的响应包装器
message Response {
enum Status {
SUCCESS = 0;
ERROR = 1;
NOT_FOUND = 2;
PERMISSION_DENIED = 3;
}
Status status = 1;
string error_message = 2;
oneof data {
Student student = 3;
StudentList student_list = 4;
}
}
message StudentList {
repeated Student students = 1;
int32 total_count = 2;
}
对应的 C++ 处理:
Response processStudentRequest(const std::string& request_data) {
Response response;
try {
// 处理请求逻辑
student::Student student;
// ... 业务逻辑 ...
response.set_status(Response::SUCCESS);
*response.mutable_student() = student;
} catch (const std::exception& e) {
response.set_status(Response::ERROR);
response.set_error_message(e.what());
}
return response;
}
.proto
文件:尝试嵌套、枚举和 map 类型。.proto
文件,测试向前/向后兼容性。