i007.cc

i007.cc

优先队列-降维打击

Protobuf C/C++实战笔记(1)

前言:
Protobuf作为数据交换格式, 被很多人喜欢. 数据压缩比高, 向后兼容性强, 性能优异, 而且对平台中性, 支持多语言(C/C++, JAVA, Python). 优点太多, 实在不胜枚举(居家旅行, 杀人放火必备良药, oh yeah! ^_^).
本篇文章着重记录Linux下对C/C++版Protobuf的编译/链接和API使用.

Protobuf下载和安装
让我们使用protobuf 3.6.1作为样例来展示.

https://github.com/protocolbuffers/protobuf/releases
下载链接: https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protobuf-all-3.6.1.tar.gz
以下是相关的命令和操作
1). 下载和解压
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protobuf-all-3.6.1.tar.gz
tar -jxvf protobuf-all-3.6.1.tar.gz
2). 编译和安装
./configure –disable-shared
make && make install
3). 目录结构
tree -L 2 # 两层的目录结构(bin/inculde/lib), 如下所示:

评注: bin/protoc 是pb生成工具, include, lib则是对应的头文件和相应的静态/动态库

实战演示
让我们来编辑一下msg.proto文件

message msg_t {
  required int32 id = 1;
}

评注: 简单定义了msg_t类
借助protoc来生成相应语言版本的序列/反序列代码

protoc --cpp_out=./ msg.proto

评注: –cpp_out指定了c/c++版本代码的输出路径
最终生成 msg.pb.cc msg.pb.h 两文件
编写如下测试代码:

#include "msg.pb.h"
#include <stdio.h>
#include <assert.h>

int main() {
  char buf[1024] = {'\0'};
  int buf_len = 0;

  msg_t msg1;
  msg1.set_id(1001);

  // *) serialize phrase => object to byte array
  msg1.SerializeToArray(buf, sizeof(buf));
  buf_len = msg1.ByteSize();

  msg_t msg2;
  // *) deserialize phrase => byte array to object
  msg2.ParseFromArray(buf, buf_len);

  assert(msg1.id() == msg2.id());
  return 0;
}

进行编译并运行
g++ -o app app.cpp msg.pb.cc -I/path/to/protobuf/include -L/path/to/protobuf/lib -lprotobuf -lpthread
./app
评注: /path/to为具体protobuf的安装目录

链接方式
静态链接还是动态链接? 这是个问题!
在指定的protobuf库路径中, 如果存在动态连接库, 则编译的程序优先选择动态链接, 否则则采用静态链接的方式.
让我们用图来对比说明
动态链接方式
在protobuf的lib目录中, 若存在动态连接库(so文件)

则编译后的app可执行程序
使用ldd app分析, 存在如下依赖项

评注: 红线区域标明了引用了动态连接库libprotobuf.so.7
直接执行二进制app文件, 遇到如下错误

1
./app: error while loading shared libraries: libprotobuf.so.7: cannot open shared object file: No such file or directory

显然这边需要设定LD_LIBRARY_PATH变量

1
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/path/to/protobuf/lib

评注: /path/to/protobuf/lib为实际的protobuf安装路径
静态链接方式
简单的在lib目录移除所有动态连接库(so)文件,
然后进行编译, 使用ldd分析
直接执行app就可以了

总结:
这边主要讲述了protobuf的编译/安装, 以及小demo编写, 重要的讲述了静态链接和动态链接的区别. 网上资料多以动态链接居多, 但实际上静态链接的方式的需要更直接些.

2 thoughts on “Protobuf C/C++实战笔记(1)

  • WillPost author

    ./configure –-disable-shared –prefix=$HOME

  • WillPost author

    protobuf在使用的时候使用静态链接库方式
    2018年05月17日 16:50:15 田老师作坊 阅读数:255 标签: protobuf caffe 更多
    个人分类: Ubuntu16.04
    转自 https://blog.csdn.net/dreamvyps/article/details/73224627

    protobuf默认安装的时候,configure使用的是使用动态链接库的方式进行安装和使用的,在使用过程中,会报出这个错误:

    [libprotobuf ERROR google/protobuf/descriptor_database.cc:57] File already exists in database: pro_projectasset.proto
    [libprotobuf FATAL google/protobuf/descriptor.cc:1164] CHECK failed: generated_database_->Add(encoded_file_descriptor, size):
    terminate called after throwing an instance of ‘google::protobuf::FatalException’
    what(): CHECK failed: generated_database_->Add(encoded_file_descriptor, size):

    当时自己并没有搞清楚问题的根源。在网上看到是可以通过将protobuf链接的时候改成静态链接库的方式进行。后续跟进

    这里先记录一下问题的解决方案:

    即是对protobuf重新编绎安装,步骤如下:

    编译google protobuff时,在configure 时加上选项:
    configrue –disable-shared
    即可编译成静态库:libprotobuf.a 但是默认的configure文件中,在编译时未加-fPIC ,导致在引用静态库的工程中编译链接时报错误:
    libs/assert.o: relocation R_X86_64_32 against `a local symbol’ can not be used when making a shared object; recompile with -fPIC .libs/assert.o: could not read symbols: Bad value
    解决该问题,需要重新编译google protobuff库,并添加编译选项:-fPIC
    以文本形式打开google buff代码目录下的configure文件,在把第2575至2578行修改为如下:
    if test “x${ac_cv_env_CFLAGS_set}” = “x”; then : CFLAGS=”-fPIC” fi if test “x${ac_cv_env_CXXFLAGS_set}” = “x”; then : CXXFLAGS=”-fPIC”
    再次执行configure:
    configure –disable-shared
    make
    make install
    编译完成后,使用libprotobuf.a文件

发表回复