Python Cython:std::具有自定义参数类型的函数回调

Python Cython:std::具有自定义参数类型的函数回调,python,c++,cython,Python,C++,Cython,在回答以下问题之前,请先阅读此帖子: 在公认的答案中,我们清楚地展示了如何使用boostpython将python函数转换为std::function 在这个示例之后,我能够以std::function作为参数包装函数,并使用python函数作为输入调用它们。但是,这仅在std::function参数是诸如int、double、string等基本体时有效 我们将非常感谢您提供有关如何使自定义类型也适用于此的任何指导。这不是一个完整的答案-它假设您可以填补问题所基于的空白。不幸的是,这比那个案子要

在回答以下问题之前,请先阅读此帖子:

在公认的答案中,我们清楚地展示了如何使用boostpython将python函数转换为
std::function

在这个示例之后,我能够以
std::function
作为参数包装函数,并使用python函数作为输入调用它们。但是,这仅在
std::function
参数是诸如
int
double
string
等基本体时有效


我们将非常感谢您提供有关如何使自定义类型也适用于此的任何指导。

这不是一个完整的答案-它假设您可以填补问题所基于的空白。不幸的是,这比那个案子要复杂一点


只是定义问题——假设你有一个自定义C++类的参数,比如:

class cpp_class {
  // some non-trivial contents
};
因此,你的C++界面看起来像这样:

void call_some_std_func(std::function<void(cpp_class&)> callback) {
    callback(5,std::string("hello"));
}
我创建了一个包装类,其中包含一个处理内存的析构函数和一个可从外部代码调用的可公开访问的构造函数。此版本是安全的,因为包装器拥有它所持有的对象,因此不能写入无效内存。但是,因为它复制了,所以不能对原来的C++对象进行修改。 第二种选择是保留指向您不拥有的对象的指针。代码基本相同,只是您删除了
\uuu dealoc\uuu
,并避免在
make\u CyWrapper
中复制:

obj.ptr = &x // instead of new cpp_class(x)

这是不安全的-您需要确保C++对象比Cython包装器寿命长,但允许您修改对象。 您还可以想象一些其他选项:您可以使用Cython包装器获得现有对象的所有权(这样的方案必须通过指针而不是引用传递,或者可以使用移动构造函数);您可以将C++类解构为基本类型表示的表示,并将其传递给Python;您可以使用共享指针来分割所有权;或者,当您的C++实例被破坏时,您有一种更精细的方法将您的Cython包装标记为“无效”。


接下来要做什么取决于您是在使用boostpython(因为它方便、可调用地包装Python对象),还是在制作自己的版本。(我在前面的回答中展示了这两种可能性)

假设使用boostpython,您需要做两件事——告诉它转换的情况,并确保它导入您的包装器在其中定义的模块(如果不这样做,则会出现令人兴奋的分段错误)

您还需要确保在使用之前导入包装器Cython模块(否则会出现分段错误)。我在“py_object_wrapper.hpp”中这样做了,但只要在某个地方做过一次,就可以把它放在你喜欢的地方

void operator()(cpp_class& a) {
    PyInit_your_module_name();
    if (held) {
        call_obj(held,a);
    }
}

您能给出一个完整但最少的示例来演示您正在尝试做什么吗?(这适用于我的说明性实现,而不是Boost Python实现。我会在可能的情况下看看如何使用Boost Python)感谢David的详细回答,但正如所指出的,我主要是在使用Boost Python方法。我想我需要这样做:在我为另一种方式写了答案之后,我开始怀疑你想要Boost Python方式。。。我认为您的链接是正确的想法:我认为您在Cython中制作了一个转换器(可能使用上面所示的包装类),制作一个<代码> CDEF Puxue/Cuffo.Caster函数来获取C++类并返回包装器,然后使用<代码> Boo::Python::ToyPython转换器。这可能是一天或太之前,我有时间看它,所以如果你或其他人发现它更快,请张贴它。。。
struct convert_to_PyWrapper {
 static PyObject* convert(const cpp_class& rhs) {
    // the const_cast here is a bit dodgy, but was needed to make it work
    return make_CyWrapper(const_cast<cpp_class&>(rhs));
}
};

inline void setup_boost_python() {
    PyInit_your_module_name(); // named inityour_module_name in Python 2
    boost::python::to_python_converter<
        cpp_class,
        convert_to_PyWrapper>();
}
cdef public void call_obj(obj, cpp_class& c):
    obj(make_CyWrapper(c))
void operator()(cpp_class& a) {
    PyInit_your_module_name();
    if (held) {
        call_obj(held,a);
    }
}