CMake构建实战:项目开发卷
上QQ阅读APP看书,第一时间看更新

1.5.4 链接Boost C++库

无论是自己构建库,还是下载安装预编译的库,我们现在总算已经安装好了Boost静态库或动态库的二进制文件。接下来将借助它们完成更复杂的功能!本小节将使用Boost Regex库提取一段文本中出现的所有URL。

使用Boost Regex库提取URL

主程序main.cpp如代码清单1.25所示。

代码清单1.25 ch001/链接Boost/main.cpp

#include <boost/regex.hpp>
#include <iostream>
#include <string>
 
using namespace std;
using namespace boost;
 
int main() {
    string s = R"(
Search Engines: http://baidu.com https://google.com
About Me: https://xuhongxu.com/about/
    )";
    regex e(R"(([a-zA-Z]*)://[a-zA-Z0-9./]+)");
 
    for (sregex_iterator m(s.begin(), s.end(), e), end; m != end; ++m) {
        cout << "URL: " << (*m)[0].str() << endl;
        cout << "Scheme: " << (*m)[1].str() << endl;
        cout << endl;
    }
 
    return 0;
}

其中,首先引用了头文件boost/regex.hpp,然后在主程序中初始化了一个boost::Regex类型的变量,即用于提取URL的正则表达式:

([a-zA-Z]*)://[a-zA-Z0-9./]+

注意:由于该表达式仅用于演示,刻意写得较为简短,并不能准确提取URL。

for循环起始条件中,初始化了sregex_iterator迭代器m,用于遍历字符串中匹配到的全部结果;还有一个空迭代器end,用于指示迭代器的终止位置。迭代器的值,也就是匹配结果,采用类似数组的形式,可以通过索引访问。第0项为完全匹配的结果,后续索引项则依次是各个捕获组的结果。

使用MSVC/NMake构建本例

这里将构建两次主程序main.cpp,分别演示对Boost库的静态链接和动态链接。其中,静态链接Boost库的可执行文件名为static_boost.exe,动态链接Boost库的可执行文件名为shared_boost.exe。 Makefile如代码清单1.26所示。

代码清单1.26 ch001/链接Boost/NMakefile

# 自行构建的Boost库
 
BOOST_DIR=C:\boost
BOOST_LIB_DIR=$(BOOST_DIR)\stage\lib
 
# 下载安装的预编译Boost库
 
# BOOST_DIR=C:\boost_prebuilt
# BOOST_LIB_DIR=$(BOOST_DIR)\lib64-msvc-14.2
 
CXXFLAGS=/I $(BOOST_DIR) /MD /EHsc
LINKFLAGS=/LIBPATH:$(BOOST_LIB_DIR)
 
all: static_boost.exe shared_boost.exe
 
static_boost.exe: main.cpp 
    cl libboost_regex-vc142-mt-x64-1_74.lib \
        main.cpp $(CXXFLAGS) /Fe"static_boost.exe" /link $(LINKFLAGS)
 
shared_boost.exe: main.cpp 
    cl boost_regex-vc142-mt-x64-1_74.lib /DBOOST_ALL_NO_LIB \
        main.cpp $(CXXFLAGS) /Fe"shared_boost.exe" /link $(LINKFLAGS)
 
clean:
    del *.obj *.exe

其中定义了BOOST_DIR和BOOST_LIB_DIR两个变量,分别代表Boost的根目录和库文件所在的目录。这里有两组变量的定义,其中第二组被注释掉了。第一组的目录是自行构建的Boost库所在的目录,第二组则是预编译库的安装目录。读者可以自行切换,构建结果是相同的。

CXXFLAGS变量用于向编译器传递公共参数。/I用于指定头文件搜索目录,这里直接设置为Boost的根目录即可。/MD参数代表程序将会动态链接C++运行时库,与之相对地,MSVC还有一个/MT参数,表示程序将会静态链接C++运行时库。由于Boost库的构建过程会默认指定/MD,这里引用Boost库的主程序也应该使用匹配的方式。

LINKFLAGS变量定义了向链接器传递的公共参数LIBPATH,即链接库的搜索目录。

下面是构建目标规则。由于本例将构建两个可执行文件,所以第一条规则将构建目标写为 all,同时依赖这两个可执行文件。这样,执行nmake all可以同时构建二者。另外,Makefile 的第一条规则是默认规则,当不提供目标参数执行nmake时会默认执行,因此执行nmake就相当于执行nmake all(不过对于本例来说,记得指定/F NMakefile参数)。

静态链接Boost库的主程序static_boost.exe的构建规则中,除了将CXXFLAGS和LINKFLAGS变量中定义的参数传递给编译器和链接器外,还向编译器传递了Boost Regex静态库的文件名。这与之前在代码清单1.12中静态链接自己编写的静态库几乎是一样的,仅仅是增加了指定搜索目录的参数。

动态链接Boost库的主程序shared_boost.exe的构建规则稍微复杂。与静态库相似但不同的是它所链接的.lib库是动态库对应的导入库。另外还多了一个宏的定义:BOOST_ALL_NO_LIB。这个宏用于指示Boost库不要试图寻找静态库进行链接,当动态链接Boost库时,都应该定义这个宏。

执行NMake构建该项目:

> cd CMake-Book\src\ch001\链接Boost
> nmake /F Makefile
> static_boost
URL: http://baidu.com
Scheme: http
 
URL: https://google.com
Scheme: https
 
URL: https://xuhongxu.com/about/
Scheme: https
 
> shared_boost # 无法启动

静态链接Boost库的主程序一切正常!但是,动态链接Boost库的主程序在运行时会抱怨找不到Boost 的动态库。这也是意料之中的事情,毕竟Boost的动态库与主程序并不在同一目录,而且Windows中也没有RUNPATH和RPATH,我们需要先复制动态库boost_regex-vc142-mt- x64-1_74.dll再运行。

使用GCC/GNU make构建本例

为了更好地对比,在Linux操作系统中,这里仍然以静态和动态两种链接Boost库的形式来构建本例。 Makefile如代码清单1.27所示。

代码清单1.27 ch001/链接Boost/Makefile

# 自行构建的Boost库
 
BOOST_DIR = $${HOME}/boost
BOOST_LIB_DIR = ${BOOST_DIR}/stage/lib
 
CXXFLAGS = -I $(BOOST_DIR)
LDFLAGS = -L $(BOOST_LIB_DIR) -Wl,-R$(BOOST_LIB_DIR)
 
# 将以上几行全部注释,即可使用安装的预编译Boost库
 
all: static_boost shared_boost
 
static_boost: main.cpp
    g++ main.cpp $(CXXFLAGS) $(LDFLAGS) -l:libboost_regex.a -o static_boost
 
shared_boost: main.cpp
    g++ main.cpp $(CXXFLAGS) $(LDFLAGS) -lboost_regex -o shared_boost
 
clean:
    rm *_boost

首先定义与Boost库目录相关的变量。BOOST_DIR是自行构建的Boost库所在的根目录,也就是~/boost;但由于RUNPATH需要使用绝对路径,我们将它写作$${HOME}。两个$代表$的转义,因此这里实际上引用了${HOME},它是代表Home目录绝对路径的环境变量。BOOST_LIB_DIR变量,与Windows中一样,定义了Boost库的库文件目录。

不过这里为什么不像NMake Makefile中一样,提供预编译库的路径变量呢?答案很简单,因为GCC会主动搜索系统的头文件目录和库文件目录,而系统包管理器安装的Boost预编译库正是安装在系统目录中。如果想让构建的程序直接链接它们,只需将Makefile 中前面这四个变量的定义注释掉,让GCC自动去默认的目录搜索头文件和库文件。

CXXFLAGS和LDFLAGS变量分别代表公共的编译和链接参数。编译参数-I指定了头文件库搜索目录,链接参数-L指定了链接库文件搜索目录,链接参数-Wl,-R 指定了RUNPATH的值。

最后,构建主程序的规则:无论是静态链接Boost库,还是动态链接Boost库,调用GCC的方式都是一样的,区别仅仅在于链接库的名称。由于链接库时,-l参数默认接受的是库的名称,而非文件名。所以,链接静态库libboost_regex.a或动态库libboost_regex.so时,应该指定参数-lboost_regex,这就冲突了,此时 GCC会优先链接动态库。为了能够实现对Boost静态库的链接,这里需要使用-l:加静态库文件全名的参数形式。

执行make构建该项目:

$ cd CMake-Book/src/ch001/链接Boost
$ make
$ ./static_boost
...
$ ./shared_boost
...

Linux中的程序可以指定RUNPATH,因此无须复制Boost动态库文件就可以运行shared_boost。