python - 将 python 函数传递给 SWIG 包装的 C++ 代码

标签 python c++ swig

我正在尝试使用 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);

帕斯卡.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: in method 'handleri_eater', argument 1 of type 'handleri *'”

有什么方法可以将 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 文件中找到这个完整示例 here .

编辑:合并了@Flexo 建议,将 Python 部分移动到 SWIG 文件的 %pythoncode block 中。

编辑:合并了@user87746 对 Python 3.6+ 兼容性的建议。

关于python - 将 python 函数传递给 SWIG 包装的 C++ 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34445045/

相关文章:

python - 如何在 huey 任务中进行模拟/猴子修补?

python - 如何显示另一个.py文件调用的窗口?

c++ - 什么是 C++ 中的标准延迟/终结器实现?

c++ - 自定义 Windows GUI 库

python - 通过 SWIG 从 Python 中的 C 函数返回结构数据类型

python - 如何使用 SWIG 处理 unique_ptr

javascript - IPython notebook ~ 使用 javascript 运行 python 代码?

python - 比较两个变量是否在python中引用同一个对象

c++ - 测量数组中的唯一数字

python - SWIG 类型映射返回输出参数 char*/size_t