老板让研究下衍生品估值,其中有个库叫QuantLib,需要先安装boost。这么多年一直用java为主,对c++的认识还停留在古老的VC++6.0阶段。之前一直用JetBrains出的IDEA开发java程序,考虑到Visual Studio 2019实在太重(而且据说对C++最新标准支持不是太好),一时兴起就想用JetBrains公司出的CLion平台来写C++ 。由于弃用了msvc,所以编译器就选择了mingw。折腾快3天,终于将QuantLib在win10系统上编译成功,简单作一记录。
1、安装mingw
先从SourceForge上(https://sourceforge.net/projects/mingw-w64/files/)下载mingw64(mingw的fork版本)。
本机为win10 64位系统,注意要选x86_64。在编译boost时,由于默认带上了-std=c++11参数,起初下载的mingw是win32版(文件名x86_64-8.1.0-release-win32-seh-rt_v6-rev0.7z),结果结果报错('std::thread' has not been declared,原来是win32版本编译器自身不支持std::thread),重新下载了一个posix版本(文件名为x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z)问题解决。
备注:顾名思义,win32版本只能运行在windows上,而posix可以跨平台(windows、unix、linux...)
2、安装boost
先感叹下boost库的强大,其创始人自身就是C++标准委员会库工作组里的大佬,很多功能模块将会纳入下一代C++的标准库中,因此说它是C++的准标准库一点都不夸张,很多类库也都基于boost库,包括前面提到的QuantLib。
到boost官网(https://www.boost.org/)下载,目前最新版是1.73.0(本例下载了1.72.0),解压后开始安装。在cmd命令行模式下,进入boost根目录(比如e:\boost_1_72_0):
第一步:执行bootstrap.bat gcc。windows版的boost默认用msvc(因本机没有安装visual studio,会报错)编译,因此要指定toolset(估计是因为mingw是winows版的gcc,此处要写gcc,写mingw也报错)。
第二步:执行.\b2 --prefix=e:\libc\boost\ --build-type=complete toolset=gcc install 。指定toolset,原理同上。prefix指定boost安装目录,build-type指定为complete进行全量构建(即生成debug/release不同版本,各种静态库/动态库...)
执行成功后,在boost安装目录下,会新增include和lib两个目录。其中include是头文件目录,lib是各种模块包。
备注,也可以直接下载编译好的binary版本(https://sourceforge.net/projects/boost/files/boost-binaries/),但看了下都是msvc编译出来的版本,用mingw编译可能还得自己处理。
3、编译QuantLib
boost部署成功后,再到QuantLib官网(https://www.quantlib.org/)下载源代码,解压到一个指定目录(本例是e:\libc\QuantLib\)。为了方便后续操作,结合QuantLib社区里一篇文章,接下来就把QuantLib源代码导入到CLion平台中。选择New CMake Project from Sources...菜单,指定前述解压后的源代码目录就可以导入项目了。
注意CLion对项目的管理都是基于CMake工具的,因此默认会生成一个新项目对应的CMakeLists.txt文件。由于官网源代码中已经有预先写好的CMakeLists.txt文件,因此最好替换回原始的CMakeLists.txt文件。为了绑定boost,需要在该文件中指定Boost_ROOT:set(BOOST_ROOT e:/libc/boost)
本机CLion中Toolchains选用的是MinGW(第一步安装的mingw64),但在build过程中老是报错:
Could NOT find Boost (missing: unit_test_framework timer system) (found version "1.72.0")
在构建\test-suite\CMakeLists.txt提示boost库中有三个包找不到,最终定位问题出在下述语句上:
find_package (Boost REQUIRED COMPONENTS unit_test_framework timer system OPTIONAL_COMPONENTS chrono)
检查了下unit_test_framework三个包在boost安装目录下是存在的,于是网上各种搜索找不到办法,折腾快2天时间均无解。第3天,静下心来仔细研究问题,终于找到原因并解决。
首先CMake官网(https://cmake.org/cmake/help/latest/command/find_package.html#)对find_package的解释是,会去调用Find<PackageName>.cmake文件,此处就是FindBoost.cmake文件(bin\cmake\win\share\cmake-3.16\Modules\FindBoost.cmake,当然从CLion显示的出错调用栈也能看出来),再一点点分析错误。
为方便调试,先打开FindBoost.cmake文件的debug功能:set(Boost_DEBUG ON)。之后就可以结合debug输出信息来分析原因,发现在找COMPONENTS时,实际去搜索的文件名如下表第1列所示(以timer包为例):
FindBoost.cmake生成的包名 | boost完整编译后包名(build-type=complete) |
libboost_timer-mgw81-mt-1_72; libboost_timer-mgw81-mt-d-1_72; libboost_timer-mgw81-mt-d; libboost_timer-mgw81-mt-s-1_72; libboost_timer-mgw81-mt-s-d-1_72; libboost_timer-mgw81-mt-s-d; libboost_timer-mgw81-mt-s; libboost_timer-mgw81-mt; libboost_timer-mt-1_72; libboost_timer-mt-d-1_72; libboost_timer-mt-d; libboost_timer-mt-s libboost_timer-mt-s-1_72; libboost_timer-mt-s-d libboost_timer-mt-s-d-1_72; libboost_timer-mt-s-d; libboost_timer-mt-s; libboost_timer-mt; libboost_timer; | libboost_timer-mgw81-mt-d-x32-1_72.a libboost_timer-mgw81-mt-d-x64-1_72.a libboost_timer-mgw81-mt-d-x64-1_72.dll libboost_timer-mgw81-mt-d-x64-1_72.dll.a libboost_timer-mgw81-mt-s-x32-1_72.a libboost_timer-mgw81-mt-s-x64-1_72.a libboost_timer-mgw81-mt-sd-x32-1_72.a libboost_timer-mgw81-mt-sd-x64-1_72.a libboost_timer-mgw81-mt-x32-1_72.a libboost_timer-mgw81-mt-x64-1_72.a libboost_timer-mgw81-mt-x64-1_72.dll libboost_timer-mgw81-mt-x64-1_72.dll.a |
比较上诉表格中两个列,眼尖的同学可能已经发现问题了,两边文件名不能匹配,左边文件名中少了x32/x64,所以导致find_package函数报错。
再进一步分析FindBoost.cmake文件,发现该标识是通过_boost_ARCHITECTURE_TAG变量控制的,而其取值也会参照CMAKE_CXX_COMPILER_ARCHITECTURE_ID参数,查官网说明发现,它是一个内部变量。
那么这个内部变量能否通过给CMake.exe传参数来设置呢?找了下没看到具体方法,最终采取了暴力方法,直接在CMakeLists.txt文件进行设置:set(CMAKE_CXX_COMPILER_ARCHITECTURE_ID "X86")
之后,就可以成功绑定boost库了,困扰几天的问题得到解决!