在大型工程项目中,经常会遇到多语言联合编程的情况,举个例子:
在一个远端控制系统中,前端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";
其中msvc是Visual 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
toolset:表示编译器,可选gcc、msvc-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
静态库以lib开头,动态库没有lib前缀
boost::python库名称和版本
编译器名称及版本
mt代表threading=multi,没有则代表threading=single
s代表runtime-link=static,没有则代表runtime-link=shared
gd代表debug版本,没有则代表release版本
x32代表32 位程序,x64代表64 位
Boost库版本,1_79代表Boost 1.79版本。
4 测试实例:python继承C++接口
新建工程文件夹,包含三个文件helloworld.cpp、helloworld.py与CMakeLists.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_
.def("f", &Base::f, &BaseWrap::default_f);
}
在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}
)
在工程目录下执行以下命令行:
mkdir build
cd build
cmake ..
make
即可编译出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())
输出以下内容,证明实验成功
>>> 0
>>> 42
5 常见问题
#include
解决方案:首先确保当前虚拟环境下有pyconfig,否则需要pip install。接着对于vscode,在c_cpp_properties.json中添加python的include目录
error LNK2019: 无法解析的外部符号 "__declspec(dllimport) class boost::python::xxx
解决方案:库链接出错,对于静态编译的Boost::python库需要在C++文件中声明静态编译宏
#define BOOST_PYTHON_STATIC_LIB
1
6 参考文档
Boost::Python官方文档
Boost::Python下载官网
————————————————
版权声明:本文为CSDN博主「Mr.Winter`」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/FRIGIDWINTER/article/details/124377965