Boost::Python学习笔记
2017-01-05 15:17
423 查看
原文地址: http://edyfox.codecarver.org/html/boost_python.html
Boost.Python 是 Boost 中的一个组件,使用它能够大大简化用
C++ 为 Python 写扩展库的步骤,提高开发效率,虽然目前它对 Python 嵌入 C++ 的支持还不是很多,但也能提供很大方便。另外,华宇煜也编写了一份关于Boost.Python
简明教程。
在正式开始使用 Boost.Python 之前,我们必须先编译 Boost。首先到 Boost 的官方站点 下载
Boost 的源码包,把它们解压到你喜欢的目录,为编译做好准备。另外,在正式安装 Boost.Python 之前,我们必须先正确安装 Python。
首先切换到 Boost 源码所在的路径,执行
./configure --with-python=/usr/bin/python \
--with-python-version=2.4 \
--with-python-root=/usr
然后,和绝大部分 Linux 程序一行,执行
Boost 相应的头文件和库文件复制到相应的地方,就可以使用了。
首先需要编译的是 Boost 的编译工具 bjam,直接到 bjam 所在目录下,即 Boost 源码包所在目录下的
就编译好了。把编译好的 bjam.exe 复制到你的
接下来,切换到 Boost 源码所在路径,执行 bjam 进行编译。我们需要提供关于 Python 的一些参数,变量 PYTHON_ROOT 指向 Python 运行环境所在的目录,变量 PYTHON_VERSION 的值为 Python 的版本号,如果你的 Python 安装路径与滇狐不同,请将相应的变量修改为你机器上相应的路径,编译命令行如下:
bjam.exe "-sTOOLS=mingw" "-sPYTHON_ROOT=E:\Python" "-sPYTHON_VERSION=2.4"
编译完毕后,你将会在你的
Boost.Python 目前并没有提供完整的将 Python 模块嵌入到 C++ 的包装库,因此许多工作我们还必须通过 Python C API 来进行。但是,利用 Boost.Python 中提供的一些模块,能够给我们的工作带来极大便利。
与任何一个其它 Python 嵌入 C/C++ 的程序一样,我们需要在第一条
接下来,我们便可以开始准备装入 Python 模块了。为了让 Python 解释器能够正确地找到 Python 模块所在的位置,我们需要将 Python 模块所在的路径添加到模块搜索路径中,添加搜索路径的 Python 语句如下:
import sys
if not '/module/path' in sys.path:
sys.path.append('/module/path')
我们使用 Python C API 执行类似的语句,就能将模块的搜索路径添加到 Python 解释器中。添加了搜索路径后,就可以通过
Boost.Python 提供的
#include <boost/python.hpp>
...
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 解释器并没有把当前路径列入默认的搜索路径中。因此,即使你的 Python 模块就存放在当前路径,你也必须使用上面的代码将当前路径添加到搜索路径中之后,才能通过
当 Python 模块使用完毕或程序结束时,请使用
Python 模块并回收相应资源。
导入了 Python 模块之后,调用 Python 函数就非常容易了。Boost.Python 里封装了一个非常好用的模板函数
Python C API 中繁琐的“将参数打包为
boost::python::call_method<返回值类型>(模块指针, "Python 函数名",
参数 1, 参数 2, ...);
模块指针可以通过我们前面得到的
...
bool result;
std::string config_file;
...
try
{
return boost::python::call_method<bool>(_module->get(), "initialize",
config_file);
}
catch (...)
{
PyErr_Print();
PyErr_Clear();
...
}
...
使用 Python C API 调用 Python 函数和调用 Python 类对象是没有太大区别的,我们只需要调用类的构造方法,得到一个类对象,然后把该类的指针看做模块指针,按照前面调用普通函数的方法调用类成员方法就可以了。例如,下列代码从
Python list 对象、从 Python list 对象中获取元素的方式。
...
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<bool>
(_yukisession.get(), "on_welcome", param);
// Extract an item from a list.
str = boost::python::call_method<std::string>
(param.ptr(), "__getitem__", 1);
...
通过动态链接库的方式使用 Boost.Python 导出C++ 模块到 Python 程序与在 C++ 可执行程序中导出模块给嵌入的 Python 解释器,编写程序的方式几乎是完全相同的。因此这里只简单介绍导出普通函数的方法,想详细了解更多高级功能,如导出 C++ 类、导出可被 Python 重载的类等,可以参看华宇煜的Boost.Python
简明教程或官方Boost.Python 文档。
首先使用
Doc String,例如在下面的例子中,我们导出了一个 C++ 函数
const char *yukigettext(const char *id);
BOOST_PYTHON_MODULE(yuki)
{
boost::python::def("gettext", yukigettext,
boost::python::args("id"), "Translate message.");
}
使用
Python 的模块。例如我们刚才导出模块用的宏
...
Py_Initialize();
inityuki();
...
此时我们在 Python 模块中只需要像普通的 Python 模块那样,将导入的 C++ 模块用
import yuki
...
print yuki.gettext("This is a test!")
Boost.Python 是 Boost 中的一个组件,使用它能够大大简化用
C++ 为 Python 写扩展库的步骤,提高开发效率,虽然目前它对 Python 嵌入 C++ 的支持还不是很多,但也能提供很大方便。另外,华宇煜也编写了一份关于Boost.Python
简明教程。
1 Boost 安装简介
在正式开始使用 Boost.Python 之前,我们必须先编译 Boost。首先到 Boost 的官方站点 下载Boost 的源码包,把它们解压到你喜欢的目录,为编译做好准备。另外,在正式安装 Boost.Python 之前,我们必须先正确安装 Python。
1.1 Linux 下的编译
首先切换到 Boost 源码所在的路径,执行 ./configure脚本,为配置脚本提供 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 下的编译
首先需要编译的是 Boost 的编译工具 bjam,直接到 bjam 所在目录下,即 Boost 源码包所在目录下的\tools\build\jam_src,执行
build.bat mingw,稍等片刻,bjam.exe
就编译好了。把编译好的 bjam.exe 复制到你的
%PATH%路径能够直接找到的地方,为后续的编译工作做好准备。
接下来,切换到 Boost 源码所在路径,执行 bjam 进行编译。我们需要提供关于 Python 的一些参数,变量 PYTHON_ROOT 指向 Python 运行环境所在的目录,变量 PYTHON_VERSION 的值为 Python 的版本号,如果你的 Python 安装路径与滇狐不同,请将相应的变量修改为你机器上相应的路径,编译命令行如下:
bjam.exe "-sTOOLS=mingw" "-sPYTHON_ROOT=E:\Python" "-sPYTHON_VERSION=2.4"
编译完毕后,你将会在你的
C:\Boost下找到编译得到的 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 模块所在的位置,我们需要将 Python 模块所在的路径添加到模块搜索路径中,添加搜索路径的 Python 语句如下:
import sys
if not '/module/path' in sys.path:
sys.path.append('/module/path')
我们使用 Python C API 执行类似的语句,就能将模块的搜索路径添加到 Python 解释器中。添加了搜索路径后,就可以通过
PyImport_ImportModule函数加载 Python 模块了。
PyImport_ImportModule返回值是
PyObject *,为了避免手工处理繁琐的引用计数等问题,我们求助于
Boost.Python 提供的
handle模块,将
PyObject *封装起来,以方便使用,代码如下:
#include <boost/python.hpp>
...
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 解释器并没有把当前路径列入默认的搜索路径中。因此,即使你的 Python 模块就存放在当前路径,你也必须使用上面的代码将当前路径添加到搜索路径中之后,才能通过
PyImport_ImportModule加载到模块。
当 Python 模块使用完毕或程序结束时,请使用
delete将
_module指针释放,
handle被释放的时候会自动释放相应的
Python 模块并回收相应资源。
2.2 调用 Python 函数
导入了 Python 模块之后,调用 Python 函数就非常容易了。Boost.Python 里封装了一个非常好用的模板函数boost::python::call_method,它可以替你处理调用函数时需要处理的种种细节,将你从
Python C API 中繁琐的“将参数打包为
PyObject *”、“构造 Tuple”、“传递 Tuple”、“解包返回值”等工作中彻底解放出来,你只需要这样:
boost::python::call_method<返回值类型>(模块指针, "Python 函数名",
参数 1, 参数 2, ...);
模块指针可以通过我们前面得到的
_module的
get方法获得,例如:
...
bool result;
std::string config_file;
...
try
{
return boost::python::call_method<bool>(_module->get(), "initialize",
config_file);
}
catch (...)
{
PyErr_Print();
PyErr_Clear();
...
}
...
2.3 使用 Python 类对象
使用 Python C API 调用 Python 函数和调用 Python 类对象是没有太大区别的,我们只需要调用类的构造方法,得到一个类对象,然后把该类的指针看做模块指针,按照前面调用普通函数的方法调用类成员方法就可以了。例如,下列代码从 _module中创建了一个
YukiSession对象,然后调用了其中的
on_welcome方法。除了展示调用类成员方法外,这段代码还展示了构造
Python list 对象、从 Python list 对象中获取元素的方式。
...
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<bool>
(_yukisession.get(), "on_welcome", param);
// Extract an item from a list.
str = boost::python::call_method<std::string>
(param.ptr(), "__getitem__", 1);
...
3 在嵌入的 Python 模块中调用 C++ 程序
通过动态链接库的方式使用 Boost.Python 导出C++ 模块到 Python 程序与在 C++ 可执行程序中导出模块给嵌入的 Python 解释器,编写程序的方式几乎是完全相同的。因此这里只简单介绍导出普通函数的方法,想详细了解更多高级功能,如导出 C++ 类、导出可被 Python 重载的类等,可以参看华宇煜的Boost.Python简明教程或官方Boost.Python 文档。
3.1 导出 C++ 函数
首先使用 BOOST_PYTHON_MODULE宏定义需要导出给 Python 的模块,然后用
boost::python::def语句定义导出的函数、参数列表以及
Doc String,例如在下面的例子中,我们导出了一个 C++ 函数
yukigettext,并重命名为
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)定义了 Python 模块后,该宏会自动生成一个函数
initname,我们需要在
Py_Initialize()之后调用这个自动生成的函数,初始化导出到
Python 的模块。例如我们刚才导出模块用的宏
BOOST_PYTHON_MODULE(yuki),因此初始化的时候就应该调用
inityuki():
...
Py_Initialize();
inityuki();
...
3.3 在 Python 模块中调用 C++ 模块
此时我们在 Python 模块中只需要像普通的 Python 模块那样,将导入的 C++ 模块用 import语句加载进来,就可以调用了:
import yuki
...
print yuki.gettext("This is a test!")
相关文章推荐
- Celery 学习笔记(1)简介
- 模板方法模式(python)
- 策略模式(python)
- 代理模式(python)
- 深入理解python对json的操作总结
- python utf-8 配置
- 装饰模式(python)
- asyncio学习(1)
- 简单工厂模式(python)
- tensorflow笔记:使用tf来实现word2vec
- python之基础篇(八)——模块与包
- Caffe学习系列(13):数据可视化环境(python接口)配置
- Python开发环境之pyenv环境搭建
- python爬虫新闻的学习笔记
- python(28)获得网卡的IP地址,如何在其他文件夹中导入python模块
- Azure Python SDK安装及排错
- python 笔记2:python语法基础
- python练手项目0007
- 练习python写了一个四则运算程序,支持乘方和“.3"这种格式
- 编译依赖于LAPACK、ATLAS的NUMPY