• C++比Python快50倍?如何让C++和Python优势互补?(BoostPython)


    目录

    1 为什么需要多语言联合编程?

    在这里插入图片描述
    在大型工程项目中,经常会遇到多语言联合编程的情况,举个例子:

    在一个远端控制系统中,前端Web使用html+css+js;后端采用python-flask作为服务端,底层控制采用C/C++

    这是因为不同编程语言有各自的适用场景和语法特性,联合编程可使得各种语言发挥自己的特长。本文主要比较Python和C++,先列举各自特点如下:

    对比项目

    C++

    Python

    本质

    编译型语言

    解释型语言

    编程难度

    难以掌握

    易于上手

    语法特性

    静态

    动态

    垃圾回收

    不支持

    支持

    安装

    难(需要专门打包)

    数据类型

    在编译时由关键字确定

    在运行时由数值确定

    函数

    输入参数和返回值类型有限制

    输入参数和返回值类型无限制

    执行速度

    性能

    国外有一个测试指出在相同复杂度算法中,C++约比Python快50倍左右。因此Python不适合用于底层算法的开发,应用在上层应用中作粘合剂或进行智能领域的研究比较占优;C/C++则适合用于底层控制算法编程。下面主要介绍Python调用C++,让C++和Python形成优势互补。

    2 Python调用C++的主要方式

    主要介绍三种C++/Python联合编程的方式:

    2.1 SWIG

    • 支持Python、Java、Ruby等语言调用C接口
    • 文档全面,易于学习
    • 绑定性能欠佳, 不支持属性和内部类封装
    • C++支持不好

    2.2 Boost::Python

    • 支持Python2与Python3调用C++接口

    • 大量使用C++ templates,明显提高编译时间

    • 非常可靠、稳定、经过充分测试

    • 语法较复杂,且文档不详细

      在这里插入图片描述

    本文采用Boost::Python进行C++/Python联合编程。

    2.3 ctypes

    • 灵活,完全兼容C语言
    • 使用较繁琐且不支持C++特性

    3 Boost::Python安装

    打开参考中的官方下载地址,根据不同的操作系统平台下载boost,UNIX和Windows的安装流程差不多,下面以Windows系统为例说明安装过程。

    在这里插入图片描述
    按下面步骤安装编译Boost::Python

    • 下载最新的boost_1_79_0.zip并解压到本地目录

    • 运行bootstrap.bat在目录下产生b2.exe可执行文件

    • 进入根目录新建user-config.jam用户配置文件,存放本地C++/Python信息

      using msvc : 14.2;
      using python : 3.7.5
      	: "D:/Anaconda/Anaconda/envs/test/python.exe"
      	: "D:/Anaconda/Anaconda/envs/test/include"
      	: "D:/Anaconda/Anaconda/envs/test/libs";
      
      • 1
      • 2
      • 3
      • 4
      • 5

      其中msvcVisual Studio对应的msvc toolset版本,具体对应关系如下:在这里插入图片描述
      python则定义了本地使用的python解释器相关路径和库

    • 命令行执行自动化安装:

      b2 --with-python install --prefix="D:/3rdLib/boost/boost_1_79_0/bin/lib64-msvc-14.2" toolset=msvc-14.2 link=static address-model=64 --user-config=user-config.jam
      
      • 1

      其中一些关键参数解释如下:

      • with- | without-:前者后接要编译的Boost库名,如本文中只需编译Boost下的Python库;后者即为编译除之外的所有库,缺省则为全部编译

      • stage | install:前者表示只生成库文件(.dll与.lib),后者会额外生成include目录包含库文件对应的头文件,推荐使用stage,因为安装完成后根目录下的boost与include目录文件完全一致,可直接作为头文件使用,节省编译时间

      • stagedir | prefix:表示编译生成文件的路径,前者对应stage安装模式,后者对应install安装模式。建议在根目录下新建bin目录管理生成的库文件

        # VS2019编译的x86库文件
        bin/lib32-msvc-14.2 
        # VS2019编译的x64库文件
        bin/lib64-msvc-14.2
        
        • 1
        • 2
        • 3
        • 4
      • toolset:表示编译器,可选gccmsvc-14.2(VS2019)等

      • link:指定生成动态链接库shared还是静态链接库static,推荐使用静态库方式编译,这样发布程序时无需连带发布Boost的.dll文件,本文采用静态编译。
        在这里插入图片描述
        在这里插入图片描述

      • address-model:指定编译版本,可选32 | 64,该参数必须和本地安装的Python位数相对应,否则会编译出错

      • user-config:使用的本地用户配置文件路径

    补充一下编译库文件的命名格式:

    libboost_python37-vc142-mt-gd-x64-1_79
    | ||   | |      | |   | || ||| | | |  |
     -  ---   ------   ---  -- - -  -   --
     1   2       3      4    5 6 7  8    9
    
    • 1
    • 2
    • 3
    • 4
    1. 静态库以lib开头,动态库没有lib前缀
    2. boost::python库名称和版本
    3. 编译器名称及版本
    4. mt代表threading=multi,没有则代表threading=single
    5. s代表runtime-link=static,没有则代表runtime-link=shared
    6. gd代表debug版本,没有则代表release版本
    7. x32代表32 位程序,x64代表64 位
    8. Boost库版本,1_79代表Boost 1.79版本。

    4 测试实例:python继承C++接口

    新建工程文件夹,包含三个文件helloworld.cpphelloworld.pyCMakeLists.txt

    helloworld.cpp中编写:

    // 因为采用静态编译boost库,因此必须定义此宏,否则编译出错
    #define BOOST_PYTHON_STATIC_LIB
    
    #include
    #include
    #include
    #include
    
    using namespace boost::python;
    using namespace std;
    
    struct Base
    {
        virtual ~Base() {}
        virtual int f() { return 0; };
    };
    
    struct BaseWrap : Base, wrapper
    {
        int f()
        {
            if (override f = this->get_override("f"))
                return f(); //如果函数进行重载了,则返回重载
            return Base::f(); //否则返回基类
        }
        int default_f() { return this->Base::f(); }
    };
    
    BOOST_PYTHON_MODULE(hello)
    {
        class_("Base")
            .def("f", &Base::f, &BaseWrap::default_f);
    }
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    CMakeLists.txt中编写编译规则

    project(Boost_Test)
    cmake_minimum_required(VERSION 2.8.3)
    
    if(MSVC)
        # set(Boost_USE_STATIC_LIBS ON)
        set(Boost_DIR D:/3rdLib/boost/boost_1_79_0/stage/lib/cmake/Boost-1.79.0)
        set(PYTHON_INCLUDE_DIRS D:/Anaconda/Anaconda/envs/test/include)
        set(PYTHON_LIBRARIES D:/Anaconda/Anaconda/envs/test/libs/python37.lib)
        find_package(Boost 1.79.0 CONFIG COMPONENTS python REQUIRED)
        include_directories(${Boost_INCLUDE_DIR} ${PYTHON_INCLUDE_DIRS})
    endif(MSVC)
    
    set(MODULE_NAME hello)
    add_library(${MODULE_NAME} SHARED
        helloword.cpp
    	)
    
    if (UNIX)
      set_target_properties(${MODULE_NAME}
        PROPERTIES
        PREFIX ""
      )
    elseif (WIN32)
      set_target_properties(${MODULE_NAME}
      PROPERTIES
      SUFFIX ".pyd"
      )
    endif()
    
    target_link_libraries(${MODULE_NAME}
      ${Boost_LIBRARIES}
      ${PYTHON_LIBRARIES}
    )
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在工程目录下执行以下命令行:

    mkdir build
    cd build
    cmake ..
    make
    
    • 1
    • 2
    • 3
    • 4

    即可编译出hello.pyd二进制文件,将该文件置于工程目录下(与helloworld.py在同一个目录),在helloworld.py中导入接口,测试多态:

    import hello 
    base = hello.Base()
    # 定义派生类,继承C++类
    class Derived(hello.Base):
        def f(self):
            return 42
    
    derived = Derived()
    print( base.f())
    print (derived.f())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    输出以下内容,证明实验成功

    >>> 0
    >>> 42
    
    • 1
    • 2

    5 常见问题

    1. #include 无法打开源文件'pyconfig.h'

      解决方案:首先确保当前虚拟环境下有pyconfig,否则需要pip install。接着对于vscode,在c_cpp_properties.json中添加python的include目录

    2. error LNK2019: 无法解析的外部符号 "__declspec(dllimport) class boost::python::xxx

      解决方案:库链接出错,对于静态编译的Boost::python库需要在C++文件中声明静态编译宏

      #define BOOST_PYTHON_STATIC_LIB
      
      • 1

    6 参考文档


    ?? 更多精彩专栏

    ??源码获取 · 技术交流 · 抱团学习 · 咨询分享 请联系??

  • 相关阅读:
    抖音小店最新入驻费用,2023具体费用分析!
    表空间的空间管理算法
    如何基于Next.js、Prisma、Postgres和Fastfy构建全栈APP?
    A40I工控主板(SBC-X40I)CAN接口测试
    DDS协议介绍
    图像标签的使用2
    [附源码]计算机毕业设计JAVA学生信息管理系统
    每日一学————基本配置和管理
    可怕的红黑树
    特种劳动防护用品安全标志证书
  • 原文地址:https://blog.csdn.net/m0_67401270/article/details/126031258