这篇文章上次修改于 764 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

protobuf 的功能类似于 json,可以将要传送的内容进行序列化,提高传输效率,然后反序列化,得到传输内容。

注意:bazel 版本为 3.5.1,高版本会出问题。

1 项目结构

example
├── demo
│   ├── BUILD
│   ├── person_list.pb.txt
│   ├── person_main.cc
│   └── person.proto
└── WORKSPACE

2 WORKSPACE

WORKSPACE 下载第三方依赖:

workspace(name = "example")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "rules_proto",
    sha256 = "602e7161d9195e50246177e7c55b2f39950a9cf7366f74ed5f22fd45750cd208",
    strip_prefix = "rules_proto-97d8af4dc474595af3900dd85cb3a29ad28cc313",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz",
        "https://github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz",
    ],
)
load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
rules_proto_dependencies()
rules_proto_toolchains()

http_archive(
    name = "bazel_skylib",
    urls = [
        "https://release.mad.st.meituan.com/bazel/deps/bazel-skylib-1.0.2.tar.gz",
        "https://release.mad.st.sankuai.com/bazel/deps/bazel-skylib-1.0.2.tar.gz",
        "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz",
    ],
    sha256 = "97e70364e9249702246c0e9444bccdc4b847bed1eb03c5a3ece4f83dfe6abc44",
)
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
bazel_skylib_workspace()

http_archive(
    name = "com_google_protobuf",
    strip_prefix = "protobuf-3.10.1",
    sha256 = "d7cfd31620a352b2ee8c1ed883222a0d77e44346643458e062e86b1d069ace3e",
    urls = [
        "https://release.mad.st.meituan.com/bazel/deps/protobuf-all-3.10.1.tar.gz",
        "https://release.mad.st.sankuai.com/bazel/deps/protobuf-all-3.10.1.tar.gz"
    ]
)

3 定义 proto 结构

demo/person.proto 定义 proto 结构:

syntax = "proto2";

package demo;

message Person {
  optional string name = 1;
  optional int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    optional string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;
}

4 protobuf 使用示例

demo/person_main.cc 为 protobuf 使用示例:


#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <utility>

#include "google/protobuf/text_format.h"

#include "demo/person.pb.h"

void SetPerson(demo::Person* person) {
  person->set_id(1);

  std::string* name = person->mutable_name();
  *name = "Tom";

  person->set_email("123456@qq.com");

  demo::Person::PhoneNumber* phone1 = person->add_phones();
  phone1->set_number("18800110395");
  phone1->set_type(demo::Person::MOBILE);

  demo::Person::PhoneNumber* phone2 = person->add_phones();
  phone2->set_number("5805448");
  phone2->set_type(demo::Person::HOME);
}

void PrintPerson(const demo::Person* person) {
    std::cout << "id: " << person->id() << std::endl;

    if (person->has_email()) {
      std::cout << "email: " << person->email() << std::endl;
    }

    for (const demo::Person::PhoneNumber& phone : person->phones()) {
        switch (phone.type()) {
          case demo::Person::MOBILE:
            std::cout << "Mobile phone: ";
            break;
          case demo::Person::HOME:
            std::cout << "Home phone: ";
            break;
          case demo::Person::WORK:
            std::cout << "Work phone: ";
            break;
        }

        std::cout << phone.number() << std::endl;
    }
}

bool ReadFile(const std::string& file_name, demo::Person* person) {
  {
    std::fstream input(file_name, std::ios::in | std::ios::binary);
    if (!input) {
      std::cout << file_name << ": File not found.  Creating a new file." << std::endl;
    } else if (!person->ParseFromIstream(&input)) {
      std::cerr << "Failed to parse person." << std::endl;
      return false;
    }
  }

  return true;
}

bool WriteFile(const std::string& file_name, const demo::Person* person) {
  {
    std::fstream output(file_name, std::ios::out | std::ios::trunc | std::ios::binary);
    if (!person->SerializeToOstream(&output)) {
      std::cerr << "Failed to write person." << std::endl;
      return false;
    }
  }

  return true;
}

int main(int argc, char* argv[]) {
  demo::Person person;
  demo::Person person1;

  std::cout << "======== set & print person ========\n";
  SetPerson(&person);
  PrintPerson(&person);

  std::cout << "\n======== write & read file ========\n";
  WriteFile("person_output.txt", &person);
  ReadFile("person_output.txt", &person1);
  PrintPerson(&person1);

  std::cout << "\n======== parse from string ========\n";
  const std::string kPersonStr = R"(
    id: 2,
    phones {
      type: 1,
      number: "5805440"
    }
  )";
  demo::Person person2;
  google::protobuf::TextFormat::ParseFromString(kPersonStr, &person2);
  PrintPerson(&person2);

  std::cout << "\n======== merge two proto ========\n";
  person.MergeFrom(person2);
  PrintPerson(&person);

  return 0;
}

5 BUILD

demo/BUILD 为 bazel 构建文件:

package(default_visibility = ["//visibility:public"])

load("@rules_cc//cc:defs.bzl", "cc_proto_library")
load("@rules_proto//proto:defs.bzl", "proto_library")

proto_library(
    name = "person_proto",
    srcs = ["person.proto"],
)

cc_proto_library(
    name = "cc_person",
    deps = [
        ":person_proto",
    ],
)

cc_binary(
    name = "person_main",
    srcs = ["person_main.cc"],
    deps = [
        ":cc_person",
        "@com_google_protobuf//:protobuf",
    ],
)

6 编译运行

======== set & print person ========
id: 1
email: 123456@qq.com
Mobile phone: 18800110395
Home phone: 5805448

======== write & read file ========
id: 1
email: 123456@qq.com
Mobile phone: 18800110395
Home phone: 5805448

======== parse from string ========
id: 2
Home phone: 5805440

======== merge two proto ========
id: 2
email: 123456@qq.com
Mobile phone: 18800110395
Home phone: 5805448
Home phone: 5805440