Window+Protobuf使用说明

C++WindowCmakeProtocbuf

介绍

起因

由于项目中要用到二进制存储数据,之前使用的方式是按照字节数依次将数据写入字节流中, 但是这样做起来做文件的协议兼容比较难做,所以我们考虑使用 protobuf 来做格式定义, 便于不同版本的文件兼容, 这边使用用起来十分方便且后续添加参数之后, 版本之间的改动很好处理,多出来的参数或者未找到的参数可以使用默认处理即可,十分方便

常用配置文件介绍

程序的配置文件是一个很常用的手段, 每次读取配置文件的信息, 容纳后根据参数决定我们的执行顺序, 是程序的一个很好的设计方式, 常用的配置文件我们会选择使用自己可读的文件格式,进行一定的程序注释之类的,然后在通过配置接口,将我们需要的参数依次读取到内存中,进行读写.

常用的配置文件格式有:

  • xml
  • yaml
  • json
  • ini
  • properties
  • ......

具体的参数区别与优劣比较自己可以查阅 常用配置文件格式

protocbuf 介绍

Protocol buffers 是一种语言中立,平台无关,可扩展的序列化数据的格式,可用于通信协议,数据存储等。

Protocol buffers 在序列化数据方面,它是灵活的,高效的。相比于 XML 来说,Protocol buffers 更加小巧,更加快速,更加简单。一旦定义了要处理的数据的数据结构之后,就可以利用 Protocol buffers 的代码生成工具生成相关的代码。甚至可以在无需重新部署程序的情况下更新数据结构。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻松读写。

Protocol buffers 很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

使用说明

环境列表

本次使用的环境列表如下

本机配置开源库编译

由于是在 Windows 下面编译, 所以这边使用了 cmake-gui 的编译方式, 按照如下步骤配置

0. 假设已经新建了一个文件夹,后续的操作都在此文件夹下 假设操作的所有文件夹都在 D:/Soft/Protobuf 文件夹下面, 新建三个文件夹,便于后续操作, 我们使用 $() 代替在你自己设备中的路径, 不要弄错

* source $(SOURCE) 原始文件

* build $(BUILD) 工程文件和编译文件

* install $(INSTALL) 软件最后安装的路径

  1. 下载原始项目,如果压缩包,将全部文件解压到 $(SOURCE) 文件夹下面
  2. 使用 cmake-gui 配置相应的原始文件夹, 一般打开到 原始文件夹下有 CMakeLists.txt的文件目录即可, 如: $(SOURCE)/protobuf-3.11.0/cmake
  3. 目标目录打开到 $(BUILD)
  4. 点击左下角,Configure, 选择 自己本机的 VS版本 可以选择架构 例如: VS2015 x64 架构
  5. 再次点击 Configure ,程序会开始读取 CMakeLists 里面的参数, 输出窗口会输出log 信息, 如果有红色会报错,我们需要排查相应的库是否完成依赖项,如果有重要依赖, 可能无法完成编译
  6. 如果一切无措,我们可以看到 如图的cmake 编译参数, 这是简略参数, 根据具体内容, 我们勾选相应的参数并设置相应的安装路径即可完成配置,
  7. 再次点击配置, 程序会进行配置, 选项会取消红色, 等生成完成即可,
  8. 点击 Generate 生成 VS2015 工程, 我们可以选择 open Project 快速打开,或者到 $(BUILD) 文件夹 点击对应的 sln 文件打开项目

Cmake 编译参数

使用 VS2015 编译库

  1. 一般 我们打开之后, 选择 Debug 或者 Release 选择 生成-生成解决方案, VS就开始了编译过程, 开始进行了项目编译过程
  2. 可能存在编译错误, 一般不会出现, 不建议更改源项目, 可以查看是否自己错误
  3. 编译好的之后的安装文件夹可能存在如下的文件夹结构, 将 $(INSTALL)/bin 加入到 path 环境中, 里面有 dll 文件和 exe 文件便于使用
    ├─bin
├─examples
├─include
└─lib

使用编译好的库 lib

  1. 假设我们需要测试 lib 文件, 建立 VS2015 工程, 新建 main.cpp addressbook.proto 的文件

  2. 在我们使用编译好的库的时候, 需要引用 include 文件才能进行编译, $(INSTALL)/include

  3. 我们使用 protobuf 的例程文件里面的 addressbook.proto, 配置相应的消息格式,

自定义 message 格式
// See README.txt for information and build instructions.
//
// Note: START and END tags are used in comments to define sections used in
// tutorials. They are not part of the syntax for Protocol Buffers.
//
// To get an in-depth walkthrough of this file and the related examples, see:
// https://developers.google.com/protocol-buffers/docs/tutorials // [START declaration]
syntax = "proto3";
package tutorial; import "google/protobuf/timestamp.proto";
// [END declaration] // [START java_declaration]
option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";
// [END java_declaration] // [START csharp_declaration]
option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
// [END csharp_declaration] // [START messages]
message Person {
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3; enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
} message PhoneNumber {
string number = 1;
PhoneType type = 2;
} repeated PhoneNumber phones = 4; google.protobuf.Timestamp last_updated = 5;
} // Our address book file is just one of these.
message AddressBook {
repeated Person people = 1;
}
// [END messages]
  1. 在工作目录下打开命令行, 使用bash 执行命令 将 .proto 文件编译成 对应的 .h.cpp 文件
protoc ./addressbook.proto --cpp_out=./
  1. 将样例里面的 列出人员和新加人员复制到自定义文件中
根据测试样例改写的 Main.cpp
// See README.txt for information and build instructions.

#include <ctime>
#include <fstream>
#include <google/protobuf/util/time_util.h>
// #include <google/protobuf/>
#include <iostream>
#include <string> #include "addressbook.pb.h" using namespace std; using google::protobuf::util::TimeUtil; // Iterates though all people in the AddressBook and prints info about them.
void ListPeople(const tutorial::AddressBook& address_book) {
for (int i = 0; i < address_book.people_size(); i++)
{
const tutorial::Person& person = address_book.people(i); cout << "Person ID: " << person.id() << endl;
cout << " Name: " << person.name() << endl;
if (person.email() != "")
{
cout << " E-mail address: " << person.email() << endl;
} for (int j = 0; j < person.phones_size(); j++)
{
const tutorial::Person::PhoneNumber& phone_number = person.phones(j); switch (phone_number.type())
{
case tutorial::Person::MOBILE:
cout << " Mobile phone #: ";
break;
case tutorial::Person::HOME:
cout << " Home phone #: ";
break;
case tutorial::Person::WORK:
cout << " Work phone #: ";
break;
default:
cout << " Unknown phone #: ";
break;
}
cout << phone_number.number() << endl;
}
if (person.has_last_updated())
{
cout << " Updated: " << TimeUtil::ToString(person.last_updated()) << endl;
}
}
} // This function fills in a Person message based on user input.
void PromptForAddress(tutorial::Person* person) {
cout << "Enter person ID number: ";
int id;
cin >> id;
person->set_id(id);
cin.ignore(256, '\n'); cout << "Enter name: ";
getline(cin, *person->mutable_name()); cout << "Enter email address (blank for none): ";
string email;
getline(cin, email);
if (!email.empty()) {
person->set_email(email);
} while (true) {
cout << "Enter a phone number (or leave blank to finish): ";
string number;
getline(cin, number);
if (number.empty()) {
break;
} tutorial::Person::PhoneNumber* phone_number = person->add_phones();
phone_number->set_number(number); cout << "Is this a mobile, home, or work phone? ";
string type;
getline(cin, type);
if (type == "mobile") {
phone_number->set_type(tutorial::Person::MOBILE);
} else if (type == "home") {
phone_number->set_type(tutorial::Person::HOME);
} else if (type == "work") {
phone_number->set_type(tutorial::Person::WORK);
} else {
cout << "Unknown phone type. Using default." << endl;
}
}
*person->mutable_last_updated() = TimeUtil::SecondsToTimestamp(time(nullptr));
} // Main function: Reads the entire address book from a file,
// adds one person based on user input, then writes it back out to the same
// file.
int main(int argc, char* argv[]) {
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION; if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
} tutorial::AddressBook address_book; {
// Read the existing address book.
fstream input(argv[1], ios::in | ios::binary);
if (!input) {
cout << argv[1] << ": File not found. Creating a new file." << endl;
} else if (!address_book.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
} } // 列出文件 读取的人员
ListPeople(address_book); // 重新添加一个人员
PromptForAddress(address_book.add_people()); {
// Write the new address book back to disk.
fstream output(argv[1], ios::out | ios::trunc | ios::binary);
if (!address_book.SerializeToOstream(&output)) {
cerr << "Failed to write address book." << endl;
return -1;
}
} // Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary(); return 0;
}
  1. 配置 main 函数的输入参数

    项目--右键--属性--调试--命令参数----加入 ./test.bin

  2. 配置 C++ 项目 include 文件夹和附加库

    项目--右键--属性--VC++目录--包含目录--编辑-----加入 $(INSTALL)/include

    项目--右键--属性--链接器--输入--附加依赖项-- 编辑-------加入 $(INSTALL)/lib/libprotobuf.lib 和 $(INSTALL)/lib/libprotobufd.lib

  3. 如果编译了 共享dll 文件

    项目--右键--属性--C/C++--预处理器--预处理器定义--编辑----加入 PROTOBUF_USE_DLLS

最终编译成功, 可以列出来 文件中的参数, 最终生成的二进制文件hex 文件内容


测试成功
  Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000: 0A 15 0A 01 32 10 01 1A 01 33 22 03 0A 01 34 2A ....2....3"...4*
00000010: 06 08 B2 AD F9 EE 05 0A 15 0A 01 32 10 01 1A 01 ..2-yn.....2....
00000020: 33 22 03 0A 01 34 2A 06 08 D2 AD F9 EE 05 3"...4*..R-yn.

更多

由于我们需要才自定义文件的后续添加大量的数据文件, 所以无法使用这种方式, 可能还是要使用更多的文件版本的方式来处理文件兼容性问题吧, 后续会将自己的参数设计思路以及参数实现方法

参考链接

  1. C++ 程序员 Protocol Buffers 基础指南
  2. Protocol Buffer Basics: C++
  3. Google Protocol Buffer 的使用和原理
  4. 高效的数据压缩编码方式 Protobuf
  5. Protobuf 终极教程
  6. protobuf【1】详细的安装和使用(windows cpp)

Window+Protobuf使用说明的更多相关文章

  1. protobuf使用说明

    1..proto文件为要生成.java文件的模板文件,其中包含名称空间.文件名等信息2.cmd中进入当前目录D:\JAVA\protoc-2.5.0-win323.运行 protoc.exe --ja ...

  2. C 简易基础开发框架 - simple c

    引言   一个为 简单高效而生的 简易跨平台的 纯C开发框架.   githup上源码   https://github.com/wangzhione/sconsole_project 请容我细说 s ...

  3. 移动端布局 - REM方式

    默认以宽度为640px的设计稿为基准页面,然后通过JS获取当前显示设备的尺寸,对应的调整 html 标签的font-size大小,从而实现通过以rem为单位的移动端布局适配. 具体代码 (functi ...

  4. window下编译并使用google protobuf

    参考网址: http://my.oschina.net/chenleijava/blog/261263 http://www.ibm.com/developerworks/cn/linux/l-cn- ...

  5. window 安装 Protobuf

    环境安装 1:下载CMake 2:打开VS Command Prompt 3:修改工作目录到目标目录 cd C:\Path\to 4:创建编译完后 protobuf headers/libraries ...

  6. window.location.hash 使用说明

    本文给大家详细汇总了关于window.location.hash的知识点,属性以及用法等等,非常的实用,并附上了例子,有需要的小伙伴可以参考下.   location是javascript里边管理地址 ...

  7. window go protobuf

    http://studygolang.com/articles/8804 protoc --go_out=. protocol.proto E:\TEST\TESTGRPC\src\google.go ...

  8. window下golang包管理glide使用说明

    golang是一门简洁高效的开发 语言,但是包管理一直是一个痛点,如图 : 很多开源项目特别是github.com中的大量应用golang.org和google.golang.org中的源码,且由于被 ...

  9. (转)window.location.hash 属性使用说明

    location是javascript里边管理地址栏的内置对象,比如location.href就管理页面的url,用location.href=url就可以直接将页面重定向url.而location. ...

随机推荐

  1. 详解Springboot中自定义SpringMVC配置

    详解Springboot中自定义SpringMVC配置 WebMvcConfigurer接口 ​ 这个接口可以自定义拦截器,例如跨域设置.类型转化器等等.可以说此接口为开发者提前想到了很多拦截层面的需 ...

  2. D - Super Jumping! Jumping! Jumping!

    Nowadays, a kind of chess game called "Super Jumping! Jumping! Jumping!" is very popular i ...

  3. java 根据图片文字动态生成图片

    今天在做热敏打印机打印二维码,并有文字描述,想到的简单的方法就是根据热敏打印机的纸张宽度和高度,生成对应的图片,如下: package com.orisdom.utils; import lombok ...

  4. LeetCode | 力扣周赛C题 5370. 设计地铁系统

    请你实现一个类 UndergroundSystem ,它支持以下 3 种方法: checkIn(int id, string stationName, int t) 编号为 id 的乘客在 t 时刻进 ...

  5. .Net微服务实践(五)[服务发现]:Consul介绍和环境搭建

    目录 介绍 服务发现 健康检查.键值存储和数据中心 架构 Consul模式 环境安装 HTTP API 和Command CLI 示例API介绍 最后 在上篇.Net微服务实践(四)[网关]:Ocel ...

  6. uni-app商城项目(01)

    1.项目准备: 1.新建项目,清理项目结构 2.完成项目初始化配置. 2.项目开始阶段: 1.完成tabBar配置,新建需要的页面 2.在 '/utis'封装需要的发送请求api,有利于功能的实现. ...

  7. kafka的基本体系结构

    使用场景 大数据:数据量和速率激增,数据类型越来越复杂 应用开发:消息引擎,应用解耦,分布式存储,流处理 Kafka的体系结构 topic : 主题(消息的逻辑分类) 客户端: 细分为生产者(朝主题发 ...

  8. 关于node中两个模块相互引用却不会死循环的问题

    关于node中两个模块相互引用却不会死循环的问题 node中是通过require来导入加载模块的,require有两个作用: 1.加载文件模块并执行里面的代码 2.拿到被加载文件模块导出的接口对象 现 ...

  9. L18 批量归一化和残差网络

    批量归一化(BatchNormalization) 对输入的标准化(浅层模型) 处理后的任意一个特征在数据集中所有样本上的均值为0.标准差为1. 标准化处理输入数据使各个特征的分布相近 批量归一化(深 ...

  10. D. AB-string

    https://codeforces.com/contest/1238/problem/D 题目大意:统计good string的个数,good string的定义,给定的字符串中含有回文段落, 题解 ...