如何在Cython中封装带boost::function<>参数的C++类方法
Cython封装带boost::function参数的C++模板方法方案
问题背景
现有如下C++17类,其成员方法method接受boost::function<void(const T&)>类型的回调参数:
#include <boost/function.hpp> template<typename T> class MyClass { public: void method(const boost::function<void(const T&)>& callback) { // 业务逻辑示例:构造T类型对象并调用回调 T t{}; callback(t); } };
由于Cython无法直接处理泛型模板和boost的回调类型,需要通过显式实例化模板类型+回调适配器的方式完成封装,以下是具体实现步骤:
实现步骤
1. 编写Cython声明文件(myclass.pxd)
用于声明C++类和boost::function的核心接口:
cdef extern from "boost/function.hpp" namespace "boost": # 针对我们需要的回调签名声明boost::function cdef cppclass function[void(const T&)]: function(void (*)(const T&, PyObject*), PyObject*) except + cdef extern from "myclass.hpp" namespace "": cdef cppclass MyClass[T]: void method(const function[void(const T&)]& callback) except + # 声明std::string(如果需要支持字符串类型) cdef extern from <string> namespace std: cdef cppclass string: string(const char*) except + const char* c_str() const noexcept
2. 编写Cython封装实现(myclass_wrapper.pyx)
实现Python类与C类的绑定,以及Python回调到C的适配逻辑:
适配int类型的封装
import cython from cpython.object cimport PyObject, Py_XDECREF, Py_INCREF # C++侧适配器函数:将C++参数传递给Python回调 cdef void int_adapter(const int& val, PyObject* py_callback): cdef PyObject* args = PyTuple_New(1) PyTuple_SetItem(args, 0, cython.cast(PyObject*, cython.int(val))) # 调用Python回调,忽略返回值 PyObject* result = PyObject_CallObject(py_callback, args) Py_XDECREF(args) Py_XDECREF(result) cdef class PyMyClassInt: cdef MyClass[int]* cpp_obj def __cinit__(self): self.cpp_obj = new MyClass[int]() def __dealloc__(self): del self.cpp_obj def method(self, callback): # 持有Python回调的引用,避免被GC提前回收 cdef PyObject* py_cb = cython.cast(PyObject*, callback) Py_INCREF(py_cb) # 创建boost::function,绑定适配器和回调对象 cdef function[void(const int&)] cpp_cb = function[void(const int&)](int_adapter, py_cb) self.cpp_obj.method(cpp_cb) Py_DECREF(py_cb)
适配std::string类型的封装(可选)
# std::string的适配器函数 cdef void string_adapter(const string& val, PyObject* py_callback): cdef PyObject* args = PyTuple_New(1) # 将C++ string转换为Python字符串 PyTuple_SetItem(args, 0, cython.cast(PyObject*, val.c_str().decode('utf-8'))) PyObject* result = PyObject_CallObject(py_callback, args) Py_XDECREF(args) Py_XDECREF(result) cdef class PyMyClassString: cdef MyClass[string]* cpp_obj def __cinit__(self): self.cpp_obj = new MyClass[string]() def __dealloc__(self): del self.cpp_obj def method(self, callback): cdef PyObject* py_cb = cython.cast(PyObject*, callback) Py_INCREF(py_cb) cdef function[void(const string&)] cpp_cb = function[void(const string&)](string_adapter, py_cb) self.cpp_obj.method(cpp_cb) Py_DECREF(py_cb)
3. 编译脚本(setup.py)
用于将Cython代码编译为Python扩展模块:
from setuptools import setup, Extension from Cython.Build import cythonize # 替换为你的Boost库实际路径 BOOST_INCLUDE = "/usr/local/include" BOOST_LIB = "/usr/local/lib" ext_modules = [ Extension( "myclass_wrapper", ["myclass_wrapper.pyx"], language="c++", extra_compile_args=["-std=c++17", f"-I{BOOST_INCLUDE}"], extra_link_args=[f"-L{BOOST_LIB}", "-lboost_function"], ) ] setup( name="myclass_wrapper", ext_modules=cythonize(ext_modules), )
关键注意事项
- 显式实例化模板:Cython不支持泛型模板,每个需要支持的
T类型都要单独编写对应的Python封装类。 - 引用计数管理:必须正确维护Python回调对象的引用计数,防止回调在被C++调用前被垃圾回收。
- Boost依赖配置:编译时需确保Boost头文件和库路径正确,
boost_function库是可选的(部分环境下boost::function仅需头文件即可编译)。 - 异常处理:C方法声明中的
except +用于将C异常传播到Python层,若业务逻辑无异常可移除。
内容的提问来源于stack exchange,提问作者Altaf
相关产品推荐
相关产品推荐

