游乐游手机版
首页/编程语言/文章详情

使用Boost.Python库实现C++与Python相互调用的详细步骤与指南

时间:2026-06-22 10:23
Boost Python简化了C++扩展Python模块的开发,封装了引用计数与类型转换。通过handle和call_method,可便捷地嵌入Python模块、调用函数及操作类对象。支持使用BOOST_PYTHON_MODULE导出C++函数供Python调用,但需手动添加模块路径并调用初始化函数。

Boost.Python是Boost库家族中一个非常实用的组件,旨在帮助C++开发者更便捷地为Python编写扩展模块。坦白说,以往使用纯Python C API编写扩展相当繁琐,涉及大量引用计数和类型转换,稍有不慎就会导致内存泄漏。Boost.Python将这些复杂细节封装起来,显著提升了开发效率。目前,它对将Python嵌入C++的支持尚不全面,但仅其提供的工具集就已经能节省大量时间。此外,华宇煜编写的《Boost.Python简明教程》内容扎实,可作为补充阅读材料。

1 Boost.Python 安装简介

在正式开始编写代码之前,需要先编译Boost库。首先从Boost官网下载源码包,并将其解压到合适的目录。需要注意的是,在安装Boost.Python之前,请确保Python已正确安装,这是必要的前提条件。

1.1 Linux 下的编译

首先进入Boost源码目录,运行 ./configure 脚本。配置时需要传递Python相关参数,例如Python解释器路径、版本号和根目录:

./configure --with-python=/usr/bin/python --with-python-version=2.4 --with-python-root=/usr

随后,如同大多数Linux程序一样,执行 make 开始编译。编译完成后,切换到root权限并运行 make install,Boost的头文件和库文件将自动复制到系统指定目录,之后即可直接使用。

1.2 使用 MinGW + MSys 在 Windows 下的编译

在Windows环境下,编译步骤稍多一些。首先需要编译Boost自带的构建工具bjam。进入Boost源码目录下的 tools/build/jam_src,执行 build.bat mingw,稍等片刻即可获得bjam.exe。将其放置于 %PATH% 可访问的目录中,例如 C:\Windows\System32,或自行创建一个工具目录并添加到环境变量。

然后返回Boost源码根目录,运行bjam进行编译。需要指定Python相关变量:PYTHON_ROOT 指向Python安装目录(例如 E:\Python),PYTHON_VERSION 填入版本号(例如 2.4)。具体命令如下:

bjam.exe "-sTOOLS=mingw" "-sPYTHON_ROOT=E:\Python" "-sPYTHON_VERSION=2.4"

编译完成后,头文件和库文件默认存放在 C:\Boost 目录下,您可以根据需要将其移动到其他位置备用。

2 使用 Boost.Python 将 Python 模块嵌入 C++

Boost.Python目前尚未提供完整的Python嵌入C++的封装库,因此许多底层操作仍需借助Python C API来完成。然而,借助Boost.Python提供的几个工具模块,至少可以在很大程度上简化繁琐的引用计数和类型转换工作。

2.1 修改模块加载路径,装入 Python 模块

与所有嵌入Python的C/C++程序相同,第一步是在第一个 #include 之前引入 Python.h,然后在程序开始时调用 Py_Initialize(),在程序结束时调用 Py_Finalize()

接下来需要准备加载Python模块。为了使Python解释器能够找到模块文件,需要将模块所在路径添加到搜索路径中。对应的Python语句大致如下:

import sys
if not '/module/path' in sys.path:
    sys.path.append('/module/path')

使用Python C API执行类似的代码即可。路径添加完成后,通过 PyImport_ImportModule 函数加载模块,该函数返回一个 PyObject * 指针。为避免手动处理引用计数的麻烦,可以使用Boost.Python提供的 handle 来包装该指针:

#include 

...

boost::python::handle<>* _module; // Module handle.
std::string path;   // Path of the Python module.
std::string module; // Module name.

...

try
{
    PyRun_SimpleString("import sys");
    PyRun_SimpleString((std::string("if not '") + path
        + "' in sys.path: sys.path.append('" + path + "')").c_str());
    _module = new boost::python::handle<>(
        PyImport_ImportModule((char *) module));
    ...
}
catch (...)
{
    PyErr_Print();
    PyErr_Clear();
    delete _module;
    _module = NULL;
    return false;
}

...

需要特别注意的是:通过Python C API初始化后的解释器不会自动将当前目录加入搜索路径。因此,即使Python模块位于当前目录,也必须使用上述代码手动添加当前路径,否则 PyImport_ImportModule 将无法找到模块。

当Python模块使用完毕或程序退出时,务必使用 delete 释放 _module 指针。handle 在释放时会自动处理底层的Python模块引用计数和资源回收。

2.2 调用 Python 函数

导入模块后,调用Python函数变得非常便捷。Boost.Python提供了一个实用的模板函数 boost::python::call_method,它封装了调用过程中的所有细节:无需手动将C++参数打包成 PyObject *、构造元组、传递参数以及解包返回值。只需如下编写:

boost::python::call_method<返回值类型>(模块指针, "Python 函数名",
        参数 1, 参数 2, ...);

模块指针可以通过之前提到的 _module 对象的 get() 方法获取,例如:

...
bool result;
std::string config_file;

...

try
{
    return boost::python::call_method(_module->get(), "initialize",
        config_file);
}
catch (...)
{
    PyErr_Print();
    PyErr_Clear();
    ...
}

...

2.3 使用 Python 类对象

通过Python C API调用Python类对象与调用普通函数的思路基本相同。只需调用类的构造方法获得一个对象,然后将该对象的指针视为模块指针,用相同的方式调用其成员方法。以下代码演示了如何从 _module 创建一个 YukiSession 对象、构造一个Python列表、调用其成员方法,以及从列表中提取元素:

...
boost::python::handle<> _yukisession;

...

// Retrieve the module handle and namespace handle.
boost::python::object main_module(*_module);
boost::python::object main_namespace = main_module.attr("__dict__");

// Call the method and get the object handle.
_yukisession = boost::python::handle<>((PyRun_String(
    "YukiSession()", Py_eval_input,
    main_namespace.ptr(), main_namespace.ptr())));

...

// Compose a list.
boost::python::list param;
param.append(boost::python::str(_addr.get_host_addr()));
param.append(boost::python::str());

// Call the method and retrieve the result.
// Method is equivalent to:
// "bool __thiscall YukiSession::on_welcome(list param);"
result = boost::python::call_method
    (_yukisession.get(), "on_welcome", param);

// Extract an item from a list.
str = boost::python::call_method
    (param.ptr(), "__getitem__", 1);

...

3 在嵌入的 Python 模块中调用 C++ 程序

通过动态链接库方式使用Boost.Python将C++模块导出到Python,与直接在C++可执行程序中导出模块供嵌入的Python解释器使用,编写方式几乎完全相同。此处仅简要介绍导出普通函数的方法。如需了解导出C++类、导出可被Python重载的类等高级功能,建议参考华宇煜的《Boost.Python简明教程》或官方文档。

3.1 导出 C++ 函数

使用 BOOST_PYTHON_MODULE 宏定义要导出的Python模块,然后在宏内部使用 boost::python::def 声明导出的函数、参数名和文档字符串。例如,下面的例子导出了C++函数 yukigettext,并在Python中将其重命名为 gettext

const char *yukigettext(const char *id);

BOOST_PYTHON_MODULE(yuki)
{
    boost::python::def("gettext", yukigettext,
        boost::python::args("id"), "Translate message.");
}

3.2 为 Python 初始化 C++ 模块

使用 BOOST_PYTHON_MODULE(name) 宏后,会自动生成一个名为 initname 的函数。我们需要在 Py_Initialize() 之后调用该函数来初始化导出的模块。例如,刚才导出的模块名为 yuki,则初始化时调用 inityuki()

...
Py_Initialize();
inityuki();
...

3.3 在 Python 模块中调用 C++ 模块

现在,在Python代码中就可以像导入普通模块一样导入该C++模块,并直接调用其中的函数:

import yuki

...

print yuki.gettext("This is a test!")


来源:https://blog.csdn.net/aihanjiao3525/article/details/101134537
上一篇Python获取下标的方法与技巧 下一篇Boost.Python嵌入教程:从C/C++调用Python代码
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
CentOS与Golang打包常见兼容性问题探讨
编程语言 · 2026-07-01

CentOS与Golang打包常见兼容性问题探讨

CentOS与Golang打包的兼容性问题集中在glibc版本不匹配、交叉编译环境变量错误、依赖库缺失及Go依赖管理不规范。可通过Docker容器编译、选择兼容Go版本、正确设置GOOS GOARCH环境变量、安装对应开发包及使用GoModules解决。

CentOS中Fortran与Python如何协同工作从入门到实战完整教程
编程语言 · 2026-07-01

CentOS中Fortran与Python如何协同工作从入门到实战完整教程

在CentOS中,Fortran与Python可通过f2py、SWIG、共享库调用或subprocess协同。f2py封装Fortran为Python模块,支持数组运算;共享库需手动对齐数据类型;系统调用适合独立计算。

CentOS中Golang打包优化方法
编程语言 · 2026-07-01

CentOS中Golang打包优化方法

在CentOS中优化Golang编译打包,可显著提升编译速度并减小二进制文件体积。关键技巧包括:设置环境变量、使用Go模块管理依赖、编译时添加-ldflags= "-s-w "去除调试信息、利用UPX工具压缩、运行strip清理符号表,以及优化cgo内C代码的编译选项。综合运用这些方法能有效优化最终程序。

在CentOS系统中cpustat与其他工具协同使用的完整方法
编程语言 · 2026-07-01

在CentOS系统中cpustat与其他工具协同使用的完整方法

cpustat作为sysstat包的CPU监控工具,可通过管道与grep等命令配合过滤数据,利用脚本自动记录带时间戳的日志,或结合图形工具查看,也可格式化输出后接入Zabbix、Grafana等Web监控系统,实现可视化与告警。

CentOS中readdir与其他Linux发行版的差异
编程语言 · 2026-07-01

CentOS中readdir与其他Linux发行版的差异

CentOS基于RHEL,与Ubuntu、Debian、Fedora在包管理器(yum dnfvsapt)、默认文件系统(XFSvsext4)等存在差异,但readdir等系统调用遵循POSIX标准,行为一致。