問題
學習 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的作用[轉]