C/C++脚本化: 使用Python作为现有项目的扩展
本文来源:Zero’s Donkey Den
原文地址:7-0.cc/cpp_scripting_exploration_2
转载许可:未经授权,禁止转载
在之前的探索篇中我们已经讨论了脚本化的意义、方式以及脚本语言的选择。本篇我们主要讨论:如何优雅地通过Python来扩展C++,结合脚本语言的优势,提高现有项目的适应性及开发效率,从而达到降本增效的目的。[1]
1 C++与Python
这里引用一段《Building Hybrid Systems with Boost.Python》中的一段描述:
Python and C++ are in many ways as different as two languages could be: while C++ is usually compiled to machine-code, Python is interpreted. Python’s dynamic type system is often cited as the foundation of its flexibility, while in C++ static typing is the cornerstone of its efficiency. C++ has an intricate and difficult compile-time meta-language, while in Python, practically everything happens at runtime.
Yet for many programmers, these very differences mean that Python and C++ complement one another perfectly. Performance bottlenecks in Python programs can be rewritten in C++ for maximal speed, and authors of powerful C++ libraries choose Python as a middleware language for its flexible system integration capabilities.
Python和C++存在很多差异: C++被编译成机器码,而Python是解释执行的。Python的动态类型系统常常被认为是它灵活性的基础,而在C++的静态类型是C++效率的基石。C++有一种复杂晦涩的编译时元语言,而在Python中,几乎一切都发生在运行时。
然而对很多程序员来说, 这些差异意味着Python和C++可以完美互补。Python程序的性能瓶颈可以用C++重写, 强大的C++库的作者选择Python作为中间件语言, 以实现其灵活的系统集成能力。
简单总结一下就是:“表达同一个意思,中文要比英文短” (★ᴗ★)
。
Python具有简洁、灵活、高效的语法,丰富多样的生态,C++与其结合可以完美的互补双方的缺点、发挥自身优势,可谓是天作之合。
而在C++准标准库Boost中,Boost.Python的出现为这种结合提供了一个强力粘合剂。
2 Boost.Python
Boost.Python是一个用于连接Python和C++的框架,它允许开发人员快速无缝地将c++类、函数和对象暴露给Python,反之亦然,其特点如下[2]:
- 无缝集成
Boost.Python提供了一个简单而灵活的接口,使得在C++和Python之间进行函数调用、数据交换以及对象创建非常方便。可以轻松地将现有的C++代码包装成Python模块,无需对原有代码做太多修改(当然也可以快速的将Python嵌入到C++中)。 - 完整的类型支持
Boost.Python支持将几乎所有的C++类型(包括基本类型、自定义类、指针、引用等)导出到Python中,并能够自动处理类型转换。这使得在C++和Python之间传递数据变得简单,无需手动编写繁琐的转换代码。 - 支持C++到Python的异常转换
当Python调用C++时,产生的C++异常可以自动转换到Python,也可以添加异常转换处理器,实现自定义的异常转换规则。 - 支持C++中操作Python对象
Boost.Python提供一系列的Python对象封装,允许开发者可以方便的操作诸如:字典、列表、元组、字符串、数值等内建对象。 - 支持C++虚拟函数, 并能在Python中重写
这意味着我们可以暴露C++抽象类,并能在Python中继承、重写,消除语言接口的差异。 - 支持C++迭代器导出为Python迭代器
2.1 开发环境需求
通常Boost.Python提供了一个编译好的二进制版本,以libboost_python38…命名,其中的数字38表示版本号。如果Python版本或者二进制版本与我们期望的不符合,则需要编译自己的二进制版本。
下面简述如何在Windows平台编译Boost[.Python]
组件 [3]:
-
我们假设Boost源码目录位于
boost_1_69_0
-
编辑配置文件
user-config.jam
修改Python依赖 [4]-
编辑如下文件
boost_1_69_0\tools\build\src\user-config.jam
-
若不存在则从如下位置位置拷贝
boost_1_69_0\tools\build\example\user-config.jam
-
修改如下内容(文件路径改成自己的)
# --------------------- # Python configuration. # --------------------- # Configure specific Python version. # using python : 3.1 : /usr/bin/python3 : /usr/include/python3.1 : usr/lib ; using python : 3.8 : H:/local/Python-3.8.3 : H:/local/Python-3.8.3/include : H:/local/Python-3.8.3 ;
-
-
编译Boost.Python(需要在Visual Studio命令提示中执行) [5]
$cd boost_1_69_0 # 1. 进入boost目录
$bootstrap # 2. 生成构建工具:b2.exe,bjam.exe
# 3. 编译静态运行时版本
$b2 stage --toolset=msvc-14.1 --with-python runtime-link=static threading=multi debug release
# 4. 编译动态运行时版本
$b2 stage --toolset=msvc-14.1 --with-python runtime-link=shared threading=multi debug release
3 Python embeddable package
在Windows平台下,Python官方主要提供了两种二进制包:[6]
前者是完整的安装程序,除了提供了Python解释器、标准库、工具和各种第三方库的集合外,还会配置环境变量和注册表项,方便系统范围内的Python使用,这也是通常情况下安装及使用的包。
而后者通常用于将Python作为嵌入式脚本语言集成到其他软件中,或者用于构建独立的可执行文件。它的特点是文件体积较小,只包含了核心的Python组件(通常是python310.zip
,*pyd
),没有额外的工具和库。你可以将Python的解释器和相关文件复制到你的应用程序目录中,并从你的应用程序中调用Python解释器来执行Python代码。
在这里我们重点关注Windows embeddable package
发行包,通常包文件结构如下:
.
|-- python.exe
|-- pythonw.exe
|-- python3.dll (64.2KB)
|-- python310.dll (3.95MB)
|-- python310._pth
|-- python310.zip
|-- python.cat
|-- LICENSE.txt
|-- *.pyd
`-- *.dll
-
python.exe、pythonw.exe
Python解释器可执行文件,两者的区别是后者无命令行界面,通常用于在后台运行Python脚本。 -
python310.zip
Python标准库:仅包含预编译并优化后的.pyc
文件。 -
python310.dll、python3.dll
Python解释器的动态链接库,提供Python解释器的核心功能,两者的区别是:后者是前者的壳(备用名),即把所有接口都转发到前者。 -
*