问题
学习 Google Protocol Buffer 的使用和原理时,提供了一个小例子,讲述了protobuf的使用方法。
假如已经有了如下文件:
其中writer.cpp如下:
#include "lm.helloworld.pb.h"
#include<iostream>
#include<fstream>
using namespace std;int main(void) { lm::helloworld msg1; msg1.set_id(101); msg1.set_str("hello"); // Write the new address book back to disk. fstream output("./log", ios::out | ios::trunc | ios::binary); if (!msg1.SerializeToOstream(&output)) { cerr << "Failed to write msg." << endl; return -1; } return 0; }
reader.cpp如下:
#include "lm.helloworld.pb.h"
#include<iostream>
#include<fstream>
using namespace std;void ListMsg(const lm::helloworld & msg)
{ cout << msg.id() << endl; cout << msg.str() << endl; } int main(int argc, char* argv[]){ lm::helloworld msg1; { fstream input("./log", ios::in | ios::binary); if (!msg1.ParseFromIstream(&input)){ cerr << "Failed to parse address book." << endl; return -1; } } ListMsg(msg1); return 0;}
可以看到writer.cpp与reader.cpp都用到了lm.helloworld.pb.h,它的实现文件也在该目录下。
那么如何编译writer同样reader呢?
方法
解析
这里有两个问题,一个是writer与reader都引用到了lm.helloworld.pb.h,故实际上先需要编译lm.helloworld.pb.cc才能被前者使用。 第二个是编译lm.helloworld.pb.cc时需要用到第三方库protobuf。
下面我们从后往前解决。
g++链接用到第三方库
有两种方法,
一种就是直接使用-L,-I,-l等参数直接告诉g++需要链接到哪些,比如:
gcc -o hello hello.c -I /home/hello/include -L /home/hello/lib -lworld
可以参考1. linux下g++ 编译时动态库和静态库的链接和头文件问题
2. gcc/g++使用第三方库时添加头文件路径和库文件路径的方法
第二种方法就是使用pkg-config 。
pkg-config 是通过库提供的一个 .pc 文件获得库的各种必要信息的,包括版本信息、编译和连接需要的参数等。这些信息可以通过 pkg-config 提供的参数单独提取出来直接供编译器和连接器使用。
在默认情况下,每个支持 pkg-config 的库对应的 .pc 文件在安装后都位于安装目录中的 lib/pkgconfig 目录下。例如,我们安装一个叫Glib 的库,且将其安装在 /opt/gtk 目录下了,那么这个 Glib 库对应的 .pc 文件是 /opt/gtk/lib/pkgconfig 目录下一个叫 glib-2.0.pc 的文件:
prefix=/opt/gtk/
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/includeglib_genmarshal=glib-genmarshal
gobject_query=gobject-query
glib_mkenums=glib-mkenumsName: GLib
Description: C Utility Library
Version: 2.12.13
Libs: -L${libdir} -lglib-2.0
Cflags: -I${includedir}/glib-2.0 -I${libdir}/glib-2.0/include
使用 pkg-config 的 –cflags 参数可以给出在编译时所需要的选项,而 –libs 参数可以给出连接时的选项。例如,假设一个 sample.c 的程序用到了 Glib 库,就可以这样编译,得到sample.o:
$ gcc -c `pkg-config --cflags glib-2.0` sample.c
然后这样连接:
$ gcc sample.o -o sample `pkg-config --libs glib-2.0`
或者上面两步也可以合并为以下一步:
$ gcc sample.c -o sample `pkg-config --cflags --libs glib-2.0`
从上面的pkgconfig 可以看到cflags, libs 分别指头文件与库的路径。
可以看到:由于使用了 pkg-config 工具来获得库的选项,所以不论库安装在什么目录下,都可以使用相同的编译和连接命令,带来了编译和连接界面的统一。
使用 pkg-config 工具提取库的编译和连接参数有两个基本的前提:
- 库本身在安装的时候必须提供一个相应的 .pc 文件。不这样做的库说明不支持 pkg-config 工具的使用。
- pkg-config 必须知道要到哪里去寻找此 .pc 文件。
后者可以在~/.bashrc中设置:
export PKG_CONFIG_PATH=/opt/gtk/lib/pkgconfig:$PKG_CONFIG_PATH
g++链接用到另外一个源文件
可以使用两种方法,一种就是先编译lm.helloworld.pb.cc,再编译writer,reader,即:
$ g++ -c lm.helloworld.pb.cc
$ g++ -c writer.cpp
$ g++ writer.o lm.helloworld.pb.o -o writer `pkg-config --cflags --libs protobuf`
$ ./writer
或者
$ g++ -c lm.helloworld.pb.cc `pkg-config --cflags protobuf`
$ g++ -c reader.cpp
$ g++ reader.o lm.helloworld.pb.o -o reader `pkg-config --libs protobuf`
$ ./reader
101
hello
第二种方法就是:
$ g++ -o reader reader.cpp lm.helloworld.pb.cc `pkg-config --cflags --libs protobuf`
$ ./reader
101
hello
可以参考“undefined reference to” 问题解决方法
参考文献
- linux下g++ 编译时动态库和静态库的链接和头文件问题
- gcc/g++使用第三方库时添加头文件路径和库文件路径的方法
- Google Protocol Buffer 的使用和原理
- “undefined reference to” 问题解决方法
- 使用GCC和pkg-config编译
- 2.2 使用GCC 和pkg-config编译
- 详解pkg-config –cflags –libs glib-2.0的作用[转]