首先, C++11以来, C++这门语言越来越强大, 并且好用. 所以使用boost库的机会是越来越少, 而且由于Boost库太大, 使用的时候, 也可能需要去进行适当的裁剪. 并且Boost 中也有很多是实验性质的东西, 在实际的开发中实用需要谨慎.
那么今天总结Boost的意义在于? 其实就工作开发而言, 个人认为 意义已经不大了
(除非你的编译器不支持C++11, 而只到C++03), 因为你可以直接上手学习C++11了, regex
, thread
, tuple
, unordered
, 智能指针等, 精华已经被抽取到了C++11标准中(实现上略有差别, 或者阉割). (但是就我个人而言, 当年在学习Boost的时候, 确实收获很多; 不过也未必, Boost很麻烦, 我碰到的大佬, 最多用用STL, 很少用Boost)
本文是对Boost常用库的总结. (长文, 慎入)
引子
本文是总结, 也是相关的工程实践(意思是说, 可能涉及到的实践细节比较多&篇幅比较长), 但是不讲安装配置之类的.
主要内容有:
- 通用库
- Smart_ptrs
- Conversion
- Utility
- Regex
- 数据结构(容器)
- Tuple
- Array
- Unordered容器
- 高级编程(重点)
- Thread
- Asio (网络编程)
- Bind
- Function
- Ref
- Lambda
- Signals(信号与槽)
- 数学/数字处理 (特别是金融里面用的比较多)
但是下面讲解可能不按照顺序, 按照我的熟悉程度讲解的. (越简单的越熟悉, 用的越多的越熟悉)
(需要一定的STL基础, 最好非常熟悉STL)
正文
TODO, 东西比较多, 有时间一点点儿写. (整理思路和理清头绪, 再书写出来, 比想象中的费时间)
安装和建议
这里简要说说安装环境问题, 不管你在哪个平台, 都要涉及这个问题. Boost很大, 先把工作中常用的那几个部分非常熟悉了, 之后慢慢在工作中积累.
下面是安装: (Linux平台, Ubuntu; )
如果你是用conan这种C++包管理器来管理库的话, 请自己玩.
下面我记录一下,按照Boost Jam的安装方式来完全安装
,借助其提供b2工具。
(安装的意思, 把头文件放在/usr/local/include, 把库文件放在/usr/local/lib目录)
- 第一步生成相关b2可执行文件:
- 第二步: 开始安装 (我是完全安装)
1
./b2 --buildtype=complete install
windows下安装:
请下载二进制工具(经过预编译的)
(在state目录下, 就是已经编译好的库lib或者dll了, 请直接配置
, 见下文)
当然我下载的是源码版本, 所以先编译吧.
- 解压文件,在cmd中打开到boost库的根目录下: (我的解压在
C:\local\boost_1_53_0
) 双击bootstrap.bat文件或者cmd执行
C:\local\boost_1_53_0>C:\local\boost_1_53_0\bootstrap.bat
运行结果如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17Building Boost.Build engine
Bootstrapping is done. To build, run:
.\b2
To adjust configuration, edit 'project-config.jam'.
Further information:
- Command line help:
.\b2 --help
- Getting started guide:
http://boost.org/more/getting_started/windows.html
- Boost.Build documentation:
http://www.boost.org/build/doc/html/index.html生成bjam.exe, 直接双击bjam.exe或者执行以下命令
C:\local\boost_1_53_0\bjam.exe -j4 --debug-symbols=on --build-type=complete toolset=msvc-14.0 threading=multi runtime-link=shared
. (msvc-14.0针对msvc2015, 如果是vs2017, 请选择 toolset=msvc-14.1; 之后不用在项目属性页面切换平台工具集
)1
2
3
4
5
6
7
8
9
10
11..updated 2219 targets...
The Boost C++ Libraries were successfully built!
The following directory should be added to compiler include paths:
C:\local\boost_1_53_0
The following directory should be added to linker library paths:
C:\local\boost_1_53_0\stage\lib等待程序编译完成, 大约要两个小时左右, 会在boost根目录下生成 bin.v2 和 stage 两个文件夹, 其中bin.v2下是生成的中间文件, 大小在2.7G左右, 可以直接删除. stage下才是生成的dll和lib文件. 补充选项的意义:
1
2
3
4
5
6
7
8
9
10
11
12
13Properties:
toolset=toolset Indicate the toolset to build with.
variant=debug|release Select the build variant
link=static|shared Whether to build static or shared libraries
threading=single|multi Whether to build single or multithreaded binaries
runtime-link=static|shared
Whether to link to static or shared C and C++
runtime.
下面进行配置:
1 | 路径是: 视图->属性管理器->当前项目->Debug|Win32->Microsoft.Cpp.Win32.user 双击 |
- 哪些库是必须编译才能使用的呢?(很多库不必编译就可以使用)
下面仔细说:
所有的Boost头文件都以.hpp为后缀名,要详细的了解Boost各种库,可以打开libs/index.html文件.
很多Boost库,只需要包含它的头文件即可, 头文件已经包含了模板和inline函数, 不需要编译成二进制库文件.
必须编译再使用的库:
Boost.Chrono
Boost.Context
Boost.Filesystem
Boost.GraphParallel
Boost.IOStreams
Boost.Locale
Boost.MPI
Boost.ProgramOptions
Boost.Python (see the Boost.Python build documentation before building and installing it)
Boost.Regex (注意)
Boost.Serialization
Boost.Signals (注意)
Boost.System (注意)
Boost.Thread (注意)
Boost.Timer
Boost.Wave
可选库: 必须要编译成二进制才能使用.
Boost.DateTime
Boost.Graph
Boost.Math
Boost.Random
Boost.Test
Boost.Exception
选择是否安装可以在用b2
工具编译的时候,使用参数选项with
和without
1 | ./b2 --with-atomic --buildtype=complete install |
表示只编译安装atmoic库.
其他参考./b2 --help
来看一下是否安装成功?
- 查看一下
/usr/local/include/boost
目录下是否有内容 - 写一段代码引用一下:
usr/local/include/boost/config.hpp
(这个文件会一直包含,直到定义了BOOST_STDLIB等宏)
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using namespace std;
int main(void)
{
cout << BOOST_PLATFORM << endl;
cout << BOOST_STDLIB << endl;
cout << BOOST_COMPILER << endl;
return 0;
}
/*
运行结果:
linux
GNU libstdc++ version 20150901
GNU C++ version 4.9.0 20150901
或者
Win32
Dinkumware standard library version 650
Microsoft Visual C++ version 14.1
*/- 查看一下
下面所有的代码都是在Widnows环境VS下书写(别问我为什么)
Ref
网上有人这么问答:
c++本身有引用(&),为什么C++11又引入了std::ref ?
主要是考虑函数式编程(如std::bind)在使用时,是对参数直接拷贝,而不是引用。
个人觉得这个回答就比较简单了, 或者说没有水准, 提问的人也问的没有水准. 已经有了指针, 那么为什么还会有智能指针呢?
ref其实可以称为智能指针, 实际上它是原始对象的封装体的工厂方法(你看下面就知道了), 并且已经引入C++ 11标准了.
具体你可以参考我的另一文章 ref-in-cpp , 详细说明了这些前因后果. 这里还是简单说一下Boost.ref
.
库 ref 在 boost/ref.hpp 中提供了模板工厂函数 boost::ref,boost::cref 分别对应包装引用和常引用。bind以及function的使用,而boost::ref可以用来助它们一臂之力。由于bind在绑定参数时,需要进行拷贝,因此当传入的引用对象无法进行拷贝时,编译器就会发生错误。那如何处理这种无法被拷贝的对象引用呢?这个时候ref就派上用场了。当然它的用处也不仅限于此,当在其它应用中,如果拷贝的代价过高时,就可以选择使用它。( 核心代码再 boost/core/ref.hpp
)
最常见的使用, 和 bind 一起用:
1 |
|
小结: 当在某些情况下需要拷贝对象参数时, 如果该对象无法进行拷贝或者拷贝代价过高, 这时候就可以选择ref.
btw: 当然上面讲解的时候, 非常潦草, 也没有概括它更多的用法, 详细请参考 std::ref, 或者我的博文 ref-in-cpp .
Bind
Function
Thread库
Boost并发领域有3个主要的库, Atomic, Thread, Asio, 它们达到并发的手段有一定的区别, 当然也有最佳适用场景了, 这里先谈线程(但是并发不仅仅只有多线程一种手段).
该库有多个头文件, 最常用的是 boost/thread.hpp
, 其他还有boost/atomic.hpp
. 相比传统的 pthread
线程库, 新的线程库是为了简化线程的开发工作(提供了很多方便的封装, 并且同一种性质的机制, 提供了多个选择, 例如mutex, 还有shared_mutex等)(并发操作在这个真多核时代越来越重要). 如果你有 pthread
相关基础, 这里接受起来会非常爽, 非常快, 话不多说, 我就叙述一下.
(仅仅一个 boost/thread 内容就不少)
该部分内容, 大部分已经移植到标准库, 这里还有一本专门的书去讲解 《C++并发编程实战》, 我的另一篇文章 thread-again 会有详细的讲解.
线程管理
一个非常简单的demo:
1 |
|
出现问题: 无法打开文件”libboost_thread-vc140-mt-gd-xxx.lib”, 请检查上面的安装步骤, 以及使用的平台工具集
是否和你编译的库版本一致. (即toolset=msvc-14.0对应vs2015)
上述程序的解释:
一个线程总是在一开始就绑定到一个类型为 boost::thread 的变量, 但是一旦创建, 就不在取决于它. 即使 t 越界或者析构了, 该线程也将继续执行.
线程同步
这里面有多种Mutex, Condition_Variable, Barrier等, 当然为了加锁解锁方便, 也使用raii手法做了很多封装, 比如unique_lock之类的.
- 递归锁
同一资源, 多次加锁. 由于在递归函数里可能碰到对同一个资源锁的获取, 所以需要使用递归锁, 方便递归函数的执行.
1 |
|
- shared_mutex
这个是c++11并发库中没有加入的(但是pthread中有). 其实和它的名字, 不太像, 我一般把它理解成读写锁.
读写锁, 一般就是 多个读者, 单个写者
, 也就是说适用于多读者, 少写者, 或者说多共享, 少独占
的锁的场景. 读写锁相关的问题, 已经在我的文章pthread中说了很多了, 下面直接来看使用.
一个简单的例子(计数器):
1 |
|
get方法, 由于是考虑到读写同时存在, 并且读次数也不少, 所以这里用的读写锁 shared_mutex
, 然后用shared_lock
或者unique_lock
或者lock_guard
进行封装, 对 shared_mutex 使用 lock_guard 或 unique_lock 就达到了写者独占的目的. 当然不用这些自动上锁解锁的工具, 你手动也行. shared_mutex 比一般的 mutex 多了函数 lock_shared() / unlock_shared(),允许多个(读者)线程同时加锁、解锁,而 shared_lock 则相当于共享版的 lock_guard.
测试一下上面的代码:
1 | boost::mutex g_io_mutex; |
输出结果:
1 | 2978 1 |
(计数器, 还是使用下面的atomic比较好, 无(显)锁编程)
假如一个线程,先作为读者用 shared_lock 加锁,读完后突然又想变成写者,该怎么办?
先解读者锁,再加写者锁。这种做法的问题是,一解一加之间,其他写者说不定已经介入并修改了数据,那么当前线程作为读者时所持有的状态(比如指针、迭代器)也就不再有效。最好是在不放锁的情况下进行升级, 可以使用 upgrade_lock
:
1 | { |
Asio
该部分属于网络编程部分, 也有很多封装API, 但是目前没有移植到标准库, 详细请参考我的另一篇文章 Asio .
Atomic
这个库对于常用类型的原子操作做了底层封装, 可以不用加锁就让多个线程间可以同步的操作一个共享变量. 可担当计数器或者标志位的角色, 该原子操作不可打断.
一般 bool
, int
类型的封装用的比较多, 特别是 Boost::atomic<int>
(其typedef的 Boost::atomic_int
), 简单的案例如下:
1 |
|
其成员函数load, store分别相当于读取和存储, exchange相当于交换(但是返回之前的值).
尾巴
参考资料
- Boost官方文档 (boost_1_53_pdf, N个pdf, 够你一看; 推荐给初学者)
- 《Beyond the C++ Standard Library: An Introduction to Boost》 (有中文版翻译, 还不错; 推荐初学者)
- 《Boost中文手册》 (非常推荐给初学者, 但是写的非常简陋, 还有一些错误)
- 《深入实践Boost:94个秘技》 机械工业出版社 (非常不错的一本, 推荐; 但不推荐初学者)
- 《Boost程序完全开发指南 3e》 罗剑锋电子工业出版社 (作者水平好, 讲的也不错, 就是讲的比较啰嗦, 不推荐)
- 我的文章asio 专门讲网络编程(同步IO和异步IO)