我一直在使用Cython尝试与用c ++编写的库进行交互.到目前为止,事情进展顺利,我可以有效地使用库中的MOST函数.我唯一的问题在于实现回调.该库有4个函数定义,看起来像这样:
typedef void (*Function1)(const uint16_t *data,
unsigned width, unsigned height);
void SetCallBack(Function1);
Run Code Online (Sandbox Code Playgroud)
所以为了实现它们,我想我会用cython做这样的事情:
ctypedef void (*Function1)(unsigned short *data,
unsigned width, unsigned height);
cdef extern from "lib.hpp":
void SetCallBack(Function1)
Run Code Online (Sandbox Code Playgroud)
然而,实际上编译正确,我不能为我的生活思考如何以回调可行的方式实际实现它.我首先尝试创建一个只调用它的函数,类似于你为任何其他函数执行它的方式,得出这个:
def PySetCallBack(Func):
SetCallBack(Func)
Run Code Online (Sandbox Code Playgroud)
但这给了我(可预测的)错误:
"无法将Python对象转换为'Function1'"
所以,是的,那就是我在的地方.如果有人有任何在Cython中设置回调的经验,我将非常感谢任何帮助.谢谢.
编辑:根据您的建议,我创建了一个带有cdef的中间函数,如下所示:
cdef void cSetCallBack(Function1 function):
SetCallBack(function)
Run Code Online (Sandbox Code Playgroud)
这似乎让我...更近了?至少现在得到一个不同的错误:
error: invalid conversion from ‘void (*)(short unsigned int*, unsigned int, unsigned int)’ to ‘void (*)(const uint16_t*, unsigned int, unsigned int)’
Run Code Online (Sandbox Code Playgroud)
现在,我可以说这些类型是相同的,所以我无法弄清楚发生了什么.
Edit2:通过声明一个新的typedef修复了这个问题:
ctypedef unsigned short uint16_t
Run Code Online (Sandbox Code Playgroud)
并使用它作为调用的参数,但显然实际上并没有更接近,但只是带我绕过一个侧轨,因为当试图调用该函数时,我得到相同的"无法将Python对象转换为'Function1' "再次出错.
所以,我几乎回到了我开始的地方.我现在唯一可以做的就是明确地将python对象作为交流函数进行投射,但是,说实话,我不知道如何去做.
编辑第三个:好吧,在解析你的答案之后我终于得到了它,它有效,所以万岁等等.我最终做的是创建这样的函数:
cdef void cSetCallback(Function1 function):
SetCallback(function)
cdef void callcallback(const_ushort *data, unsigned width, unsigned height):
global callbackfunc
callbackfunc(data,width,height)
cSetCallback(callcallback)
def PySetCallback(callbackFunc):
global callbackfunc
callbackfunc = callbackFunc
Run Code Online (Sandbox Code Playgroud)
所以现在唯一的问题是它无法将const_ushort*数据转换为python对象,但这完全是另一个问题,所以我想这个问题已经解决了,非常感谢.
我最近遇到过这种情况,我还必须使用Cython将现有的C++库与Python连接起来,大量使用事件/回调.找到这方面的资料并不容易,我想把所有这些放在一起:
首先,包装C++回调类(基于'double(METHOD)(void)'原型,但它可能已经模板化,因为Cython可以处理模板):
ALabCallBack.h:
#ifndef ALABCALLBACK_H_
#define ALABCALLBACK_H_
#include <iostream>
using namespace std;
namespace elps {
//template < typename ReturnType, typename Parameter >
class ALabCallBack {
public:
typedef double (*Method)(void *param, void *user_data);
ALabCallBack();
ALabCallBack(Method method, void *user_data);
virtual ~ALabCallBack();
double cy_execute(void *parameter);
bool IsCythonCall()
{
return is_cy_call;
}
protected:
bool is_cy_call;
private:
//void *_param;
Method _method;
void *_user_data;
};
} /* namespace elps */
#endif /* ALABCALLBACK_H_ */
Run Code Online (Sandbox Code Playgroud)
ALabCallBack.cpp:
#include "ALabCallBack.h"
namespace elps {
ALabCallBack::ALabCallBack() {
is_cy_call = true;
};
ALabCallBack::~ALabCallBack() {
};
ALabCallBack::ALabCallBack(Method method, void *user_data) {
is_cy_call = true;
_method = method;
_user_data = user_data;
};
double ALabCallBack::cy_execute(void *parameter)
{
return _method(parameter, _user_data);
};
} /* namespace elps */
Run Code Online (Sandbox Code Playgroud)
地点:
'callback'::从C类信息中激活Python(= Method)对象方法的模式/转换器方法
'method':: Python用户传递的有效方法(= user_data)
'parameter'::要传递给'method'的参数
现在,我们需要实现.pyx文件......
我们的基础原型:
ctypedef double (*Method)(void *param, void *user_data)
Run Code Online (Sandbox Code Playgroud)
然后,我们为C++类提供了一个Cython包装器:
cdef extern from "../inc/ALabCallBack.h" namespace "elps" :
cdef cppclass ALabCallBack:
ALabCallBack(Method method, void *user_data)
double cy_execute(void *parameter)
Run Code Online (Sandbox Code Playgroud)
用于将C类型原型转换为Python对象调用的模式/转换器方法:
cdef double callback(void *parameter, void *method):
return (<object>method)(<object>parameter)
Run Code Online (Sandbox Code Playgroud)
现在让我们将这些功能嵌入到Cython类中:
cdef class PyLabCallBack:
cdef ALabCallBack* thisptr
def __cinit__(self, method):
# 'callback' :: The pattern/converter method to fire a Python
# object method from C typed infos
# 'method' :: The effective method passed by the Python user
self.thisptr = new ALabCallBack(callback, <void*>method)
def __dealloc__(self):
if self.thisptr:
del self.thisptr
cpdef double execute(self, parameter):
# 'parameter' :: The parameter to be passed to the 'method'
return self.thisptr.cy_execute(<void*>parameter)
Run Code Online (Sandbox Code Playgroud)
编辑:更好地输入执行函数:def execute => cpdef double
而已.称之为做类似的事情:
def func(obj):
print obj
obj.Test() # Call to a specific method from class 'PyLabNode'
return obj.d_prop
n = PyLabNode() # Custom class of my own
cb = PyLabCallBack(func)
print cb.execute(n)
Run Code Online (Sandbox Code Playgroud)
由于python是隐式类型的,因此当触发回调时,我们可以访问与作为参数传递的对象的类相关的'obj'对象的属性.
它可以很容易地适应纯C实现.请告诉我你是否可以看到任何可能的增强功能(在我的情况下,由于事件是集中激发的,因此必须提高性能).
如果您可以修改库来定义:
typedef void (*Function1)(const uint16_t *data,
unsigned width, unsigned height,
void *user_data);
void SetCallBack(Function1, void*);
Run Code Online (Sandbox Code Playgroud)
相反,我担心你运气不好.如果你有void*,那么你定义一个函数来调用具有正确参数的python可调用对象,并SetCallBack使用此函数和python可调用.
如果不能,但回调是全局的(似乎是),你可以创建一个全局变量来存储python对象.比你再次创建一个函数来调用python对象并将它传递给SetCallBack你PySetCallback只会设置全局并确保注册正确的功能.
如果回调是特定于上下文的,但你无法将它传递给"用户数据"指针,我担心你在这里运气不好.
我知道python和C++,但不是cython,所以我不知道你是否可以在cython中创建函数,或者你是否必须用C++编写.