将python函数传递给SWIG封装的C++代码
Passing python functions to SWIG wrapped C++ code
我正在尝试使用SWIG为python包装一个C++库。库经常使用回调函数,通过将特定类型的回调函数传递给类方法。
现在,在包装完代码之后,我想从python中创建回调逻辑。这可能吗?这是我正在做的一个实验。。目前不起作用。
头文件和swig文件如下:
paska.h:
typedef void (handleri)(int code, char* codename);
// handleri is now an alias to a function that eats int, string and returns void
void wannabe_handleri(int i, char* blah);
void handleri_eater(handleri* h);
paska.i:
%module paska
%{ // this section is copied in the front of the wrapper file
#define SWIG_FILE_WITH_INIT
#include "paska.h"
%}
// from now on, what are we going to wrap ..
%inline %{
// helper functions here
void wannabe_handleri(int i, char* blah) {
};
void handleri_eater(handleri* h) {
};
%}
%include "paska.h"
// in this case, we just put the actual .cpp code into the inline block ..
最后,我在python中进行了测试。。
import paska
def testfunc(i, st):
print i
print st
paska.handleri_eater(paska.wannabe_handleri(1,"eee")) # THIS WORKS!
paska.handleri_eater(testfunc) # THIS DOES NOT WORK!
最后一行抛出"TypeError:在方法"handleri_eater"中,类型为"handleri*"的参数1"
有没有办法将python函数"强制转换"为SWIG包装器所接受的类型?
在我看来,ctypes
和SWIG typemap
的组合将是解决问题的最简单方法。ctypes
使生成一个调用Python可调用的C函数变得容易。Python代码应该遵循以下行:
import example
# python callback
def py_callback(i, s):
print( 'py_callback(%d, %s)'%(i, s) )
example.use_callback(py_callback)
在SWIG方面,我们有:(1)一个Python函数use_callback
,它用ctypes
包装器包装Python回调,并将地址作为整数传递给_example.use_callback()
;(2)一个SWIG typemap
,它提取地址并将其强制转换为适当的函数指针。
%module example
// a typemap for the callback, it expects the argument to be an integer
// whose value is the address of an appropriate callback function
%typemap(in) void (*f)(int, const char*) {
$1 = (void (*)(int i, const char*))PyLong_AsVoidPtr($input);;
}
%{
void use_callback(void (*f)(int i, const char* str));
%}
%inline
%{
// a C function that accepts a callback
void use_callback(void (*f)(int i, const char* str))
{
f(100, "callback arg");
}
%}
%pythoncode
%{
import ctypes
# a ctypes callback prototype
py_callback_type = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
def use_callback(py_callback):
# wrap the python callback with a ctypes function pointer
f = py_callback_type(py_callback)
# get the function pointer of the ctypes wrapper by casting it to void* and taking its value
f_ptr = ctypes.cast(f, ctypes.c_void_p).value
_example.use_callback(f_ptr)
%}
您可以在这里找到这个完整的CMakeLists.txt文件示例。
edit:包含@Flexo建议,将Python部分移动到SWIG文件的%pythoncode块中。
edit:结合了@user87746关于Python 3.6+兼容性的建议。
您可以使用"directors"在Python中实现回调逻辑。
基本上,不是传递回调函数,而是传递回调对象。基本对象可以在C++中定义,并提供virtual
回调成员函数。然后可以从继承此对象,并在Python中覆盖回调函数。然后可以将继承的对象传递给C++函数,而不是回调函数。为了实现这一点,您需要为这样一个回调类启用director功能。
不过,这确实需要更改底层C++库。
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 将可变参数函数的参数封装在类实例中
- 如何封装一个函数,以便它只能由同一类中的一个其他函数调用?
- 封装C++模板
- 将 RTOS 队列对象封装在仅具有静态分配的 IQueue 自定义接口中
- 从封装在对象中的函数 C++ 返回时为空的列表
- 当要访问的对象被多次封装时,如何正确使用setter
- 在为嵌套类定义行外友元时,我真的必须打破封装吗?
- 如何在类中封装C/C++套接字发送和接收函数?
- 如何使用吸气剂方法实现C++封装
- 封装 std::map 以允许迭代,但没有直接密钥访问?
- 类C++友元函数无法访问封装的类
- 当从成员类调用封装的std::begin时,程序崩溃
- 从私有成员类中断封装派生的模板类
- C++实用程序,用于将长开关语句转换为封装开关案例阶梯的简洁函数调用
- 如何修复列表视图中的错误?,封装控件时无法选择任何项
- 我们可以使用命名空间实现封装吗?
- 封装 std::vector 以允许迭代,但不允许其他内容
- lambda[=] 上的复制值被另一个封装的 lambda[&] 阻止
- 如何使用提升范围将自定义迭代器封装在函数中