TL;DR 有人知道如何指示SWIG将C结构的这些成员当作函数指针来处理,并使其可以从Python调用吗?在
整个故事 我有C结构包含指向函数的指针。这些函数都是按类型定义的。我有一个C函数,它将为这个C结构分配内存,并将函数指针设置为指向有效的C函数。 我的简化头文件如下所示
// simplified api.h
typedef void *handle_t;
typedef void sample_t;
typedef error_t comp_close_t(handle_t *h);
typedef error_t comp_process_t(handle_t h,
sample_t *in_ptr,
sample_t *out_ptr,
size_t *nr_samples);
typedef struct
{
comp_close_t *close;
comp_process_t *process;
} audio_comp_t;
// prototype for init
error_t comp_init(handle_t *h, int size);
以及相应的简化源文件:
^{pr2}$以及我的接口文件的最新版本:
%module comp_wrapper
%{
#include "api.h"
%}
%include "api.h"
// Take care of the double pointer in comp_init
%ignore comp_init;
%rename(comp_init) comp_init_overload;
%newobject comp_init;
%inline %{
audio_comp_t* comp_init_overload(int size) {
audio_comp_t *result = NULL;
error_t err = comp_init(&result, size);
if (SSS_NO_ERROR == err) {
...
}
return result;
}
%}
// wrap the process call to verify the process_t * function pointer
%inline %{
sss_error_t call_process( audio_comp_t *h,
sample_t *in,
sample_t *out,
size_t nr_samples)
{
return h->process(h, in, out, &nr_samples);
}
%}
我想使用SWIG创建语言绑定,这样我就可以用Python中最小的锅炉板代码调用这些类似对象的结构。最后,我想用这样的方法:
h = comp_init(50)
h.process(h, input_data, output_data, block_size)
h.close(h)
然而,SWIG将这些结构中的这些函数指针视为对象,所以每当我想调用它们时
>>> h = comp_init(50)
>>> h.api.process()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'SwigPyObject' object is not callable
我可以通过一些类似“call_process”函数来解决它,您可以在接口文件中找到它:
call_process(h, in, out, 32)
但这需要我为所有的struct成员函数添加一个额外的包装器,而这不需要,因为[SWIG文档声明完全支持函数指针][1]
我假设应该在接口文件中编写一些代码,以便SWIG知道它处理的是函数而不是SwigPyObject
有一些关于如何处理(python)回调的信息,但在这种情况下,这些信息似乎都不起作用: SWIG call function pointers stored within struct
或者不将头文件中的所有信息或多或少地复制到接口文件中: Using SWIG with pointer to function in C struct
最后但并非最不重要的一点是,如果将函数指针包装在结构中,则解决方案5不起作用: How to wrap a c++ function which takes in a function pointer in python using SWIG
有人知道如何指导SWIG将C-struct的这些成员当作函数指针来处理,并使其可以从Python调用吗?
最简单的解决方案是,如果我们声称function pointers are simply member functions那么它将生成的包装器工作得非常好。在
为了证明在这个例子中,我们需要修复示例代码中的一些错误,所以我使用了api.h,如下所示:
api.c看起来像这样:
^{pr2}$有了这些,我们可以编写api.i,如下所示:
除了隐藏原始结构并声明它充满了成员函数而不是成员指针之外,我们还做了一些事情:
h.close()
,而不是h.close(h)
)comp_init
函数,而不是仅仅替换它。这纯粹是一个偏好的问题,我只是添加了它来展示如何使用它。在这使我可以运行以下Python:
如果您愿意对API的头进行一些修饰性的更改,以方便操作,我们可以做一些对Python和C都很好的工作。在
我在api.h中引入了一个宏,
MAKE_API_FUNC
,它包装了最初在其中的typedef语句。当用C编译器编译时,它仍然会产生完全相同的结果,但是它让我们可以更好地使用SWIG来操作。在所以api.h现在看起来是这样的:
所以在api.i中,我们现在用另一个宏来替换这个宏,它声称函数指针typedef实际上是一个结构,带有一个特殊提供的
__call__
函数。通过创建这个额外的函数,我们可以将所有Python参数自动代理到对实际函数指针的调用中。在这使用了与我在my answer on wrapping ^{} objects 中使用的相同的preprocessor mechanisms,但应用于该问题的函数指针。另外,我从Python的角度使用了^{} to make a constructor/destructor ,这使得API更易于使用。如果这是真正的代码,我可能也会使用
%rename
。在话虽如此,我们现在可以使用以下Python代码:
关于如何将错误代码映射到Python异常上的讨论,请参见SWIG docs。在
我们可以通过一个简单的技巧,消除对变量宏的参数进行迭代的需要,从而进一步简化这一过程。如果我们将api.h宏更改为接受3个参数,其中第3个参数是函数指针的所有参数,如下所示:
然后我们可以更改SWIG接口,使其不提供通过
%extend
添加的__call__
函数的定义,而是编写一个宏,直接调用我们想要的函数指针:这里的棘手之处在于
typedef struct {...} name;
习惯用法使得重命名或隐藏结构内部的函数指针更加困难。(但是,这只是为了保持handle_t
参数的自动添加而必需的)。在相关问题 更多 >
编程相关推荐