You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 21:33:29