美文网首页C++
C++与Python混合编程:Boost.python的安装与使

C++与Python混合编程:Boost.python的安装与使

作者: Henry606 | 来源:发表于2018-09-30 16:51 被阅读22次

    Boost库是一个可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的开发引擎之一。Boost库由C++标准委员会库工作组成员发起,其中有些内容有望成为下一代C++标准库内容。在C++社区中影响甚大,是不折不扣的“准”标准库。

    Boost由于其对跨平台的强调,对标准C++的强调,与编写平台无关。大部分boost库功能的使用只需包括相应头文件即可,少数(如正则表达式库,文件系统库等)需要链接库。但Boost中也有很多是实验性质的东西,在实际的开发中使用需要谨慎。

    Boost库是为C++语言标准库提供扩展的一些C++程序库的总称。

    ——百度百科

    简单来说,Boost 是一系列通用的 C++ 扩展库的集合。而 Boost.python 则是这众多扩展库中的其中一个,它基于 C++ 代码提供了一套 Python 接口,可作为 C++ 与 Python 混合编程的桥梁。

    Boost 库的安装

    方式一:Boost 源码包

    首先去 Boost 官网下载 Boost 库:下载地址

    官方默认只提供 Boost 的源码包。大多数情况下(纯 C/C++ 开发),源码包就足够了,我们只需要在编译的时候引入相应的头文件即可。但如果你的程序需要以静态/动态链接库的形式引入某些包,就需要自己编译了。恰好 Boost.python 就是这样的需求,毕竟,你总不能指望直接在 python 代码中引用 C++ 源码吧!

    我们一般会把 python 代码之外的所有 C/C++ 外挂编译成一个 .pyd 文件,这样就能直接在 python 代码中调用了。事实上,.pyd 文件就是一个拥有 python 接口的 .dll 文件。

    Boost 源码包的目录结构如下:

    boost_1_68_0
    +---boost
    +---doc
    +---libs
    +---more
    +---status
    +---tools
    +---INSTALL
    +---Jamroot
    +---LICENSE_1_0.txt
    +---boost.css
    +---boost.png
    +---boost-build.jam
    +---boostcpp.jam
    +---bootstrap.bat
    +---bootstrap.sh
    +---index.htm
    +---index.html
    +---rst.css
    

    其中,boost 目录下是所有库的头文件(.hpp文件),libs 目录下则是所有库的具体实现(.cpp文件)。

    方式二:Boost 预编译包

    Boost 源码包如果要在本地完全编译,可能需要几个小时的时间。因此,为了方便使用,Boost 也推出了 Windows 下的 预编译版本。预编译包比起源码包多出一个子目录,用于存放已经编译好的静态库和动态库。

    安装预编译版本 boost_1_68_0-msvc-14.0-32.exe 之后的目录结构如下:

    boost_1_68_0
    +---boost
    +---doc
    +---libs
    +---lib32-msvc-14.0       # new
    +---more
    +---status
    +---tools
    +---INSTALL
    +---Jamroot
    +---LICENSE_1_0.txt
    +---b2.exe                # new
    +---bjam.exe              # new
    +---boost.css
    +---boost.png
    +---boost-build.jam
    +---boostcpp.jam
    +---bootstrap.bat
    +---bootstrap.log         # new
    +---bootstrap.sh
    +---index.htm
    +---index.html
    +---project-config.jam    # new
    +---rst.css
    

    其中,lib32-msvc-14.0 目录保存了所有编译好的静态库和动态库。需要注意的是,预编译版本对编译器种类、版本和目标位数(32/64)都有要求。在上述例子中,要使用这些库,必须用 msvc-14.0(即 Visual Studio 2015)且设置目标位数为 32 位。

    不同版本的预编译包可以安装在同一个目录下,以支持不同的编译环境,这样就比较方便了。比如在上述例子中,如果还要支持 64 位,可以再下载一个 boost_1_68_0-msvc-14.0-64.exe,同样安装在当前安装目录下。这样安装目录下将多出一个 lib64-msvc-14.0 子目录,存放编译好的 64 位的库,其余目录和文件均不受影响。

    预编译版本满足了偷懒的需求,但很难满足所有要求,因为有些库必须配合本地环境才能使用。比如 Boost.python 就必须配合本地的 Python 版本才能使用,因此本地编译 Boost 库这个基本技能还是必要的。下面我们还是以 Boost 源码包的使用为例进行介绍。

    本地编译 Boost.python

    在编译之前,需要确保本机已经安装了 Visual Studio 和 Python。

    首先,我们使用命令行进入 Boost 源码包的安装目录,执行 bootstrap.bat 脚本,将会在当前目录下生成 b2.exebjam.exeproject-config.jambootstrap.log 四个文件。其中,b2.exebjam.exe 就是我们编译时要用到的命令了。这两个命令的作用是一样的,bjam 是老版本,b2 是升级版本。

    下面就可以开始编译 Boost.python 了,笔者在本机所使用的命令如下:

    b2.exe --with-python stage --stagedir="./bin/lib32-msvc-14.0" link=static address-model=32
    

    编译完成后,将在 ./bin/lib32-msvc-14.0/lib 目录下产生 4 个文件:

    libboost_numpy36-vc140-mt-gd-x32-1_68.lib
    libboost_numpy36-vc140-mt-x32-1_68.lib
    libboost_python36-vc140-mt-gd-x32-1_68.lib
    libboost_python36-vc140-mt-x32-1_68.lib
    

    其中,python 和 numpy 各 2 个,带 gd 的对应 debug 版本,反之对应 release 版本。

    默认情况下,编译时调用的编译器和 Python 版本是 b2/bjam 自动搜索的。如果要指定不同的 Python 版本,就需要在你的 home 目录下新建一个配置文件 user-config.jam(路径为 C:\Users\xxx\user-config.jam)。推荐直接使用 Boost 根目录下的 tools/build/example/user-config.jam 作为模板,稍加修改即可。例如,笔者的 user-config.jam 中对 Python 的配置如下:

    using python
        : 3.5
        : "D:/App/Python35/python.exe"
        : "D:/App/Python35/include"
        : "D:/App/Python35/libs"
        : <define>BOOST_ALL_NO_LIB=1
        ;
    

    b2/bjam 参数说明:

    b2 命令的功能强大,用起来也比较复杂,因此在使用之前,最好先查看一下该命令的帮助:

    b2.exe --help
    

    以下是一些比较重要的参数说明:

    • stage/install:

      stage 表示只生成库(dll 和 lib),install 还会生成包含头文件的 include 目录。推荐使用 stage,因为 install 生成的 include 目录实际就是源码包下的 boost 目录,需要 include 的时候可以直接使用,不需要再次生成,这样可以节省大量的编译时间。

    • toolset:

      指定编译器,可选的如 borland、gcc、msvc-14.0(VS2015)等。如果不指定,会自动搜索本地可用的编译器(可查看 ./project-config.jam 文件以确认)。

    • without/with:

      选择不编译/编译哪些库(类似于黑名单/白名单)。--with-python 的含义是仅编译 python,其他的都不编译。反过来,如果用 --without-python,意思就是除了 python, 其他的都编译。with/without 参数可以多次出现,以限定多个库。如果不设置 with/without 参数,默认全部编译,可能需要几个小时的时间!

      需要注意,编译 Boost.python 需要确保本地安装了 Python,并且 python 命令已加入环境变量。

      要查看 Boost 包含的所有库,可使用以下命令:

      b2.exe --show-libraries
      
    • stagedir/prefix:

      stage 时使用 stagedir,install 时使用 prefix,表示编译生成文件的路径。推荐给不同的编译环境指定不同的目录,如 Visual Studio 2015 的 x86 应用对应的是 bin/lib32-msvc-14.0,x64 应用对应的是 bin/lib64-msvc-14.0。如果都生成到一个目录下,将没有任何益处,徒增管理难度。如果使用了 install 参数,那么还将在上述指定的目录下生成 include 目录,用于保存头文件。

    • build-dir:

      编译生成的中间文件的路径,默认是 Boost 根目录下的 bin.v2 目录,一般无需设置。

    • link:

      指定生成动态链接库还是静态链接库,取值为 static|shared。生成静态链接库使用 static,生成动态链接库需使用 shared。如不指定,默认使用 static。静态库的缺点是占用空间比较大,优点是程序发布的时候无需附带 Boost 库的 dll,比较整洁。推荐使用静态库的方式编译 Boost.python,这样发布程序的时候就不用 Boost 的 dll 了,并且也多占用不了太多空间。

    • runtime-link:

      指定运行时是动态还是静态链接其他库。同样有 shared 和 static 两种方式。如果不指定,默认是 shared,一般无需设置。

    • threading:

      要编译的库是单线程还是多线程,可取值 single|multi。如果不指定,默认是 multi,一般无需设置。

    • variant

      debug|release,编译 debug 版本还是 release 版本。一般与最终发布的程序是 debug 还是 release 版相对应。如果不指定,默认两个都编译,一般无需设置。

    • address-model

      编译成 32 位版本还是 64 位版本,可取值 32|64。如果不指定,默认两个版本都编译。如果是编译 Boost.python,该参数就要与本地安装的 Python 位数相对应,否则编译会出错,因此最好设置一下。

    Boost 静态库/动态库的命名规则

    以 Boost.python 为例,如果编译的是静态库(link=static),将会生成单个 .lib 文件:

    libboost_python36-vc140-mt-gd-x32-1_68.lib
    

    而如果编译的是动态库(link=shared),将会生成两个文件(.lib.dll):

    boost_python36-vc140-mt-gd-x32-1_68.lib
    boost_python36-vc140-mt-gd-x32-1_68.dll
    

    动态库虽然也生成 .lib 文件,但它与静态库的 .lib 文件差别很大。动态库的 .lib 更像是对 .dll 的声明,二者的关系类似于 .h.cpp 的关系。因此,动态库中的 .lib 文件要比静态库的 .lib 文件小得多。

    下面以静态库的命名规则为例进行分析:

    libboost_python36-vc140-mt-sgd-x32-1_68.lib
    | ||   | |      | |   | || ||| | | |  |
     -  ---   ------   ---  -- - -  -   --
     1   2       3      4    5 6 7  8    9
    
    1. 静态库以 lib 开头,动态库开头没有 lib
    2. 所有的库都含有 boost 前缀。
    3. Boost 库名称,本例中为 python36
    4. 编译器名称及其版本,vc140 指的是 msvc-14.0,对应 Visual Studio 2015。
    5. mt 代表 threading=multi,没有则代表 threading=single
    6. s 代表 runtime-link=static,没有则代表 runtime-link=shared
    7. gd 代表 debug 版本,没有则代表 release 版本。
    8. 目标位数,x32 代表 32 位,x64 代表 64 位。
    9. Boost 库的版本号,1_68 代表 Boost 1.68 版本。

    Boost 库的使用

    如果要在项目中使用 Boost 库,需要做以下两项配置:

    1. 包含 Boost 头文件(本例中为:D:\ProgramFiles\boost_1_68_0);
    2. 链接 Boost 库文件(本例中为:D:\ProgramFiles\boost_1_68_0\bin\lib32-msvc-14.0\lib)。

    Boost 官方推荐使用 b2/bjam 命令进行自动化编译、链接,只需要编写一个 .jam 配置文件,这种方式类似于 makeMakefile。但考虑到还要学习 jam 语法,暂时还是用 Visual Studio 手动编译吧。

    在 Visual Studio 中的具体操作如下:

    1. 选中当前项目,点击属性按钮,依次选择“配置属性->C/C++->常规->附加包含目录”,编辑并添加一条路径:D:\ProgramFiles\boost_1_68_0
    2. 选中当前项目,点击属性按钮,依次选择“配置属性->链接器->常规->附加库目录”,编辑并添加一条路径:D:\ProgramFiles\boost_1_68_0\bin\lib32-msvc-14.0\lib

    Boost.python 的使用

    要使用 Boost.python,除了上述两项配置之外,还需要再添加两项配置:

    1. 包含 Python 头文件(本例中为:D:\Program Files (x86)\Python36-32\include);
    2. 包含 Python 静态库文件(本例中为:D:\Program Files (x86)\Python36-32\libs\python36.lib)。

    在 Visual Studio 中的具体操作如下:

    1. 选中当前项目,点击属性按钮,依次选择“配置属性->C/C++->常规->附加包含目录”,编辑并添加一条路径:D:\Program Files (x86)\Python36-32\include
    2. 选中当前项目,点击属性按钮,依次选择“配置属性->链接器->输入->附加依赖项”,编辑并添加一个文件:"D:\Program Files (x86)\Python36-32\libs\python36.lib",注意要有双引号,否则可能识别不正确。

    测试

    准备 hello world 代码

    使用官网给出的 hello world 示例,代码文件位于 Boost 安装目录下:D:\ProgramFiles\boost_1_68_0\libs\python\example\tutorial,包括 hello.cpphello.py 两个文件,如下:

    hello.cpp:

    
    // 当引入 #include <boost/python/xxx> 时,Boost 会默认链接 boost_python 动态链接库,
    // 如果我们想要链接静态链接库,就需要在 include 之前加上 #define BOOST_PYTHON_STATIC_LIB
    #define BOOST_PYTHON_STATIC_LIB
    
    #include <boost/python/module.hpp>
    #include <boost/python/def.hpp>
    
    char const* greet()
    {
       return "hello, world";
    }
    
    BOOST_PYTHON_MODULE(hello_ext)
    {
        using namespace boost::python;
        def("greet", greet);
    }
    
    

    hello.py:

    import hello_ext
    print(hello_ext.greet())
    
    

    我们的目的是将 hello.cpp 编译成一个 .pyd 文件(其实就是一个包含 Python 接口的 .dll 文件),然后用 hello.py 调用它。

    注意hello.cpp 中的宏定义 #define BOOST_PYTHON_STATIC_LIB 非常重要,它可以指定链接 boost_python 的静态库,而不是默认的动态库。

    如果你在编译过程中遇到了这样的错误:LINK : fatal error LNK1104: cannot open file "boost_python36-vc140-mt-x32-1_68.lib",但是你明明已经引入了静态库:libboost_python36-vc140-mt-x32-1_68.lib,你可能会纳闷:我明明已经导入了静态库,为什么还找我要动态库?那么最可能的原因是,你的 C++ 代码中漏掉了这句宏定义。

    使用 Visual Studio 构建 hello world

    Boost 官方推荐使用 b2/bjam 命令进行自动化编译、链接,只需要编写一个 .jam 配置文件,这种方式类似于 makeMakefile。但考虑到还要学习 jam 语法,暂时还是用 Visual Studio 手动编译吧。

    1. 首先使用 Visual Studio 创建一个空项目。
    2. 打开项目属性,在“配置属性->常规”中,进行以下修改:
      • 配置类型设为:“动态库(.dll)”。
      • 目标文件名设为:hello_ext
        注意.pyd 文件的名称必须与要导出的 python module 名称一致,否则 python 的 import 语句将会报错:ImportError: dynamic module does not define module export function
      • 目标文件扩展名设为:.pyd
    3. hello.cpp 文件导入项目。
    4. 参照上一节 [Boost 库的使用] 中的说明,将 Boost 头文件目录、Boost 库文件目录、Python 头文件目录、Python 静态库文件都导入到项目中。
    5. 选择菜单“生成->生成 Project”,如果一切无误,将会生成一个 hello_ext.pyd 文件。由于生成器的默认配置是 Debug-x86,因此生成目录为项目下的 Debug 目录。
    6. hello_ext.pydhello.py 放在同一目录下,执行 hello.py,将会看到输出:hello, world

    参考

    1. Boost 百度百科
    2. boost 1.56.0 编译及使用
    3. Boost.python QuickStart
    4. Boost.build

    相关文章

      网友评论

        本文标题:C++与Python混合编程:Boost.python的安装与使

        本文链接:https://www.haomeiwen.com/subject/yuiwoftx.html