由Google Protocol Buffer的小例子引起的g++编译问题

问题

学习 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}/include

glib_genmarshal=glib-genmarshal 
gobject_query=gobject-query 
glib_mkenums=glib-mkenums

Name: 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 工具提取库的编译和连接参数有两个基本的前提:

  1. 库本身在安装的时候必须提供一个相应的 .pc 文件。不这样做的库说明不支持 pkg-config 工具的使用。
  2. 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” 问题解决方法

参考文献

  1. linux下g++ 编译时动态库和静态库的链接和头文件问题
  2. gcc/g++使用第三方库时添加头文件路径和库文件路径的方法
  3. Google Protocol Buffer 的使用和原理
  4. “undefined reference to” 问题解决方法
  5. 使用GCC和pkg-config编译
  6. 2.2 使用GCC 和pkg-config编译
  7. 详解pkg-config –cflags –libs glib-2.0的作用[转]
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值