有几种方法可以用 C 和 C++ 代码扩展 Python:

  • 使用 Python.h 的纯 C
  • 使用 Swig
  • 使用 Boost.Python,可选择使用 Py++ 预处理
  • 使用 pybind11
  • 使用 Cython

本页介绍 Boost.Python。在 Cython 出现之前,它是编写 C++ 扩展模块最舒适的方式。

Boost.Python 与 Boost C++ 库捆绑在一起。要在 Ubuntu 系统上安装它,你可能需要运行以下命令:

Bash
$ sudo apt-get install libboost-python-dev
$ sudo apt-get install python-dev

Hello World 示例

C++ 源代码 (hellomodule.cpp)

C++
#include <iostream>
using namespace std;

void say_hello(const char* name) {
    cout << "Hello " <<  name << "!\n";
}

#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
using namespace boost::python;

BOOST_PYTHON_MODULE(hello) {
    def("say_hello", say_hello);
}

setup.py

Python
#!/usr/bin/env python
from distutils.core import setup
from distutils.extension import Extension

setup(name="PackageName",
    ext_modules=[
        Extension("hello", ["hellomodule.cpp"],
        libraries = ["boost_python"])
    ])

现在我们可以使用以下命令构建我们的模块

Bash
python setup.py build

模块 hello.so 将最终位于例如 build/lib.linux-i686-2.4 中。

使用扩展模块

更改到 hello.so 文件所在的子目录。在交互式 Python 会话中,你可以按如下方式使用该模块

Python
>>> import hello
>>> hello.say_hello("World")
Hello World!

使用 CGAL 的示例

CGAL 库的一些(但不是全部)函数已经有 Python 绑定。这里提供了一个没有此绑定的情况的示例,以及如何实现它。该示例取自 CGAL 文档。

C++
// test.cpp
using namespace std;

/* PYTHON */
#include <boost/python.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
namespace python = boost::python;

/* CGAL */
#include <CGAL/Cartesian.h>
#include <CGAL/Range_segment_tree_traits.h>
#include <CGAL/Range_tree_k.h>

typedef CGAL::Cartesian<double> K;
typedef CGAL::Range_tree_map_traits_2<K, char> Traits;
typedef CGAL::Range_tree_2<Traits> Range_tree_2_type;
typedef Traits::Key Key;
typedef Traits::Interval Interval;

Range_tree_2_type *Range_tree_2 = new Range_tree_2_type;

void create_tree() {
    typedef Traits::Key Key;
    typedef Traits::Interval Interval;
    std::vector<Key> InputList, OutputList;
    InputList.push_back(Key(K::Point_2(8,5.1), 'a'));
    InputList.push_back(Key(K::Point_2(1.0,1.1), 'b'));
    InputList.push_back(Key(K::Point_2(3,2.1), 'c'));
    Range_tree_2->make_tree(InputList.begin(),InputList.end());
    Interval win(Interval(K::Point_2(1,2.1),K::Point_2(8.1,8.2)));
    std::cout << "\n Window Query:\n";
    Range_tree_2->window_query(win, std::back_inserter(OutputList));
    std::vector<Key>::iterator current=OutputList.begin();
    while(current!=OutputList.end()){
        std::cout << "  " << (*current).first.x() << "," << (*current).first.y()
                  << ":" << (*current).second << std::endl;
        current++;
    }
    std::cout << "\n Done\n";
}

void initcreate_tree() {;}

using namespace boost::python;

BOOST_PYTHON_MODULE(test) {
    def("create_tree", create_tree, "");
}
Python
# setup.py
#!/usr/bin/env python
from distutils.core import setup
from distutils.extension import Extension

setup(name="PackageName",
    ext_modules=[
        Extension("test", ["test.cpp"],
        libraries = ["boost_python"])
    ])

然后我们按如下方式编译并运行该模块

Bash
$ python setup.py build
$ cd build/lib*
$ python
>>> import test
>>> test.create_tree()
Window Query:
 3,2.1:c
 8,5.1:a
Done
>>>

处理 Python 对象和错误

我们还可以处理更复杂的数据,例如 Python 对象(如列表)。属性使用在对象的“attr”函数输出上执行的 extract 函数来访问。我们还可以通过告诉库发生了错误并返回来抛出错误。在下面的例子中,我们编写了一个名为“afunction”的 C++ 函数,我们想要调用它。该函数接受一个整数 N 和一个长度为 N 的向量作为输入,我们必须在调用该函数之前将 python 列表转换为字符串向量。

C++
#include <vector>
using namespace std;

void _afunction_wrapper(int N, boost::python::list mapping) {
    int mapping_length = boost::python::extract<int>(mapping.attr("__len__")());
    // 进行错误检查,映射的长度至少需要与 N 一样长
    if (mapping_length < N) {
        PyErr_SetString(PyExc_ValueError,
            "The string mapping must be at least of length N");
        boost::python::throw_error_already_set();
        return;
    }
    vector<string> mystrings(mapping_length);
    for (int i=0; i<mapping_length; i++) {
        mystrings[i] = boost::python::extract<char const *>(mapping[i]);
    }
    // 现在调用我们的 C++ 函数
    _afunction(N, mystrings);
}

using namespace boost::python;

BOOST_PYTHON_MODULE(c_afunction) {
    def("afunction", _afunction_wrapper);
}
最后修改: 2025年01月31日 星期五 01:56