SWIG 如何在可从 Python 调用的结构中创建 typedef 函数指针
SWIG how to make a typedef function pointer in a struct callable from Python
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);
以及相应的简化源文件:
// simplified api.c
static comp_close_t my_close;
static comp_process_t my_process;
audio_comp_t comp = {
my_close,
my_process
};
error_t comp_init(audio_comp_t **handle) {
*handle = ∁
return 0;
}
error_t my_close(handle_t *h) {
// stuff
*h = NULL;
return 0;
}
error_t my_process(handle_t h,
sample_t *in_ptr,
sample_t *out_ptr,
size_t *nr_samples) {
audio_comp_t *c = (audio_comp_t*) h;
// stuff
printf("doing something useful\n");
}
以及我的接口文件的最新版本:
%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)
但这需要我为所有结构成员函数添加一个额外的包装器,虽然这不是必需的,因为 [SWIG 文档指出完全支持函数指针][1]
我假设我应该在接口文件中编写一些代码,以便 SWIG 知道它正在处理函数而不是 SwigPyObject
有一些关于如何处理 (python) 回调的信息,但其中 none 似乎特别适用于这种情况:
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 结构的这些成员视为函数指针并使其可从 Python 调用吗?
最简单的解决方案是,如果我们向 SWIG 声明 function pointers are simply member functions 那么它将生成的包装器工作得很好。
为了证明在这种情况下我们需要修复您的示例代码中的一些错误,所以我最终 api.h 看起来像这样:
// simplified api.h
#include <stdint.h>
#include <stdlib.h>
typedef uint32_t api_error_t;
typedef void *handle_t;
typedef void sample_t;
typedef api_error_t comp_close_t(handle_t h);
typedef api_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
api_error_t comp_init(handle_t *new_h);
和api.c看起来像这样:
#include "api.h"
#include <stdio.h>
// simplified api.c
static comp_close_t my_close;
static comp_process_t my_process;
audio_comp_t comp = {
my_close,
my_process
};
api_error_t comp_init(handle_t *handle) {
*handle = ∁
return 0;
}
api_error_t my_close(handle_t h) {
(void)h; // stuff
return 0;
}
api_error_t my_process(handle_t h,
sample_t *in_ptr,
sample_t *out_ptr,
size_t nr_samples) {
audio_comp_t *c = (audio_comp_t*) h;
(void)c;(void)in_ptr;(void)out_ptr;// stuff
printf("doing something useful\n");
return 0;
}
有了这个,我们可以写 api.i 如下:
%module api
%{
#include "api.h"
%}
%include <stdint.i>
%typemap(in,numinputs=0) handle_t *new_h (handle_t tmp) %{
= &tmp;
%}
%typemap(argout) handle_t *new_h %{
if (!result) {
$result = SWIG_NewPointerObj(tmp$argnum, $descriptor(audio_comp_t *), 0 /*| SWIG_POINTER_OWN */);
}
else {
// Do something to make the error a Python exception...
}
%}
// From my earlier answer: https://whosebug.com/a/11029809/168175
%typemap(in,numinputs=0) handle_t self "=NULL;"
%typemap(check) handle_t self {
= arg1;
}
typedef struct {
api_error_t close(handle_t self);
api_error_t process(handle_t self,
sample_t *in_ptr,
sample_t *out_ptr,
size_t nr_samples);
} audio_comp_t;
%ignore audio_comp_t;
%include "api.h"
除了隐藏原始结构并声称它充满了成员函数而不是成员指针之外,我们还做了一些事情:
- 使 SWIG automatically pass the handle in as the 1st argument 而不是要求 Python 用户过于冗长。 (在 python 中变为
h.close()
而不是 h.close(h)
)
- 使用 argout typemap 包装真正的
comp_init
函数,而不是仅仅替换它。这纯粹是一个偏好问题,我只是添加它来展示它的使用方式。
这让我的 运行 以下 Python:
import api
h=api.comp_init()
print(h)
h.process(None, None, 0)
h.close()
如果您愿意对 API 的 header 进行一些外观更改以促进东西。
我在 api.h、MAKE_API_FUNC
中引入了一个宏,它包装了您原来在其中的 typedef 语句。当使用 C 编译器编译时,它仍然会产生完全相同的结果,但是它可以让我们使用 SWIG 更好地操作。
所以 api.h 现在看起来像这样:
// simplified api.h
#include <stdint.h>
#include <stdlib.h>
typedef uint32_t api_error_t;
typedef void *handle_t;
typedef void sample_t;
#ifndef MAKE_API_FUNC
#define MAKE_API_FUNC(name, type, ...) typedef api_error_t comp_ ## name ## _t(__VA_ARGS__)
#endif
MAKE_API_FUNC(close, audio_comp_t, handle_t);
MAKE_API_FUNC(process, audio_comp_t, handle_t, sample_t *, sample_t *, size_t);
typedef struct
{
comp_close_t *close;
comp_process_t *process;
} audio_comp_t;
// prototype for init
api_error_t comp_init(handle_t *new_h);
所以在 api.i 中,我们现在用另一个宏替换那个宏,它向 SWIG 声称函数指针 typedef 实际上是一个结构,具有专门提供的 __call__
函数。通过创建这个额外的函数,我们可以将所有 Python 参数自动代理到对实际函数指针的调用中。
%module api
%{
#include "api.h"
%}
%include <stdint.i>
// From: https://whosebug.com/a/2653351
#define xstr(a) str(a)
#define str(a) #a
#define name_arg(num, type) arg_ ## num
#define param_arg(num, type) type name_arg(num, type)
#define FE_0(...)
#define FE_1(action,a1) action(0,a1)
#define FE_2(action,a1,a2) action(0,a1), action(1,a2)
#define FE_3(action,a1,a2,a3) action(0,a1), action(1,a2), action(2,a3)
#define FE_4(action,a1,a2,a3,a4) action(0,a1), action(1,a2), action(2,a3), action(3,a4)
#define FE_5(action,a1,a2,a3,a4,a5) action(0,a1), action(1,a2), action(2,a3), action(3,a4), action(4,a5)
#define GET_MACRO(_1,_2,_3,_4,_5,NAME,...) NAME
%define FOR_EACH(action,...)
GET_MACRO(__VA_ARGS__, FE_5, FE_4, FE_3, FE_2, FE_1, FE_0)(action,__VA_ARGS__)
%enddef
%define MAKE_API_FUNC(name, api_type, ...)
%nodefaultctor comp_ ## name ## _t;
%nodefaultdtor comp_ ## name ## _t;
typedef struct {
%extend {
api_error_t __call__(FOR_EACH(param_arg, __VA_ARGS__)) {
return $self(FOR_EACH(name_arg, __VA_ARGS__));
}
}
} comp_ ## name ## _t;
// Workaround from: https://github.com/swig/swig/issues/609
%rename("%s_fptr", "%$isvariable", "match$ismember"="1", "match$type"=xstr(comp_ ## name ## _t)) name;
%extend api_type {
%pythoncode %{
name = lambda self, *args: self.name ## _fptr(self, *args)
%}
}
%enddef
%ignore comp_init;
%include "api.h"
%extend audio_comp_t {
audio_comp_t() {
handle_t new_h = NULL;
api_error_t err = comp_init(&new_h);
if (err) {
// throw or set Python error directly
}
return new_h;
}
~audio_comp_t() {
(void)$self;
// Do whatever we need to cleanup properly here, could actually call close
}
}
这是从 Python 的角度使用相同的 preprocessor mechanisms I used in my answer on wrapping std::function
objects, but applied to the function pointers of this problem. In addition I used %extend
to make a constructor/destructor,这使得 API 更好用。如果这是真正的代码,我可能也会使用 %rename
。
话虽如此,我们现在可以使用以下 Python 代码:
import api
h=api.audio_comp_t()
print(h)
print(h.process)
h.process(None, None, 0)
参见 SWIG docs 讨论如何将错误代码很好地映射到 Python 的异常。
我们可以通过一个简单的技巧消除迭代可变参数宏参数的需要,从而进一步简化它。如果我们将 api.h 宏更改为采用 3 个参数,其中第 3 个参数是函数指针的所有参数,如下所示:
// simplified api.h
#include <stdint.h>
#include <stdlib.h>
typedef uint32_t api_error_t;
typedef void *handle_t;
typedef void sample_t;
#ifndef MAKE_API_FUNC
#define MAKE_API_FUNC(name, type, args) typedef api_error_t comp_ ## name ## _t args
#endif
MAKE_API_FUNC(close, audio_comp_t, (handle_t self));
MAKE_API_FUNC(process, audio_comp_t, (handle_t self, 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
api_error_t comp_init(handle_t *new_h);
然后我们现在可以更改我们的 SWIG 接口,不提供我们通过 %extend
添加的 __call__
函数的定义,而是编写一个直接调用我们想要的函数指针的宏:
%module api
%{
#include "api.h"
%}
%include <stdint.i>
// From: https://whosebug.com/a/2653351
#define xstr(a) str(a)
#define str(a) #a
%define MAKE_API_FUNC(name, api_type, arg_types)
%nodefaultctor comp_ ## name ## _t;
%nodefaultdtor comp_ ## name ## _t;
%{
#define comp_ ## name ## _t___call__(fptr, ...) fptr(__VA_ARGS__)
%}
typedef struct {
%extend {
api_error_t __call__ arg_types;
}
} comp_ ## name ## _t;
// Workaround from: https://github.com/swig/swig/issues/609
%rename("%s_fptr", "%$isvariable", "match$ismember"="1", "match$type"=xstr(comp_ ## name ## _t)) name;
%extend api_type {
%pythoncode %{
name = lambda self, *args: self.name ## _fptr(self, *args)
%}
}
%enddef
%ignore comp_init;
%include "api.h"
%extend audio_comp_t {
audio_comp_t() {
handle_t new_h = NULL;
api_error_t err = comp_init(&new_h);
if (err) {
// throw or set Python error directly
}
return new_h;
}
~audio_comp_t() {
(void)$self;
// Do whatever we need to cleanup properly here, could actually call close
}
}
这里棘手的是 typedef struct {...} name;
惯用语的使用使得重命名或隐藏结构内的函数指针变得更加困难。 (然而,这只是保持自动添加 handle_t
参数所必需的)。
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);
以及相应的简化源文件:
// simplified api.c
static comp_close_t my_close;
static comp_process_t my_process;
audio_comp_t comp = {
my_close,
my_process
};
error_t comp_init(audio_comp_t **handle) {
*handle = ∁
return 0;
}
error_t my_close(handle_t *h) {
// stuff
*h = NULL;
return 0;
}
error_t my_process(handle_t h,
sample_t *in_ptr,
sample_t *out_ptr,
size_t *nr_samples) {
audio_comp_t *c = (audio_comp_t*) h;
// stuff
printf("doing something useful\n");
}
以及我的接口文件的最新版本:
%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)
但这需要我为所有结构成员函数添加一个额外的包装器,虽然这不是必需的,因为 [SWIG 文档指出完全支持函数指针][1]
我假设我应该在接口文件中编写一些代码,以便 SWIG 知道它正在处理函数而不是 SwigPyObject
有一些关于如何处理 (python) 回调的信息,但其中 none 似乎特别适用于这种情况: 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 结构的这些成员视为函数指针并使其可从 Python 调用吗?
最简单的解决方案是,如果我们向 SWIG 声明 function pointers are simply member functions 那么它将生成的包装器工作得很好。
为了证明在这种情况下我们需要修复您的示例代码中的一些错误,所以我最终 api.h 看起来像这样:
// simplified api.h
#include <stdint.h>
#include <stdlib.h>
typedef uint32_t api_error_t;
typedef void *handle_t;
typedef void sample_t;
typedef api_error_t comp_close_t(handle_t h);
typedef api_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
api_error_t comp_init(handle_t *new_h);
和api.c看起来像这样:
#include "api.h"
#include <stdio.h>
// simplified api.c
static comp_close_t my_close;
static comp_process_t my_process;
audio_comp_t comp = {
my_close,
my_process
};
api_error_t comp_init(handle_t *handle) {
*handle = ∁
return 0;
}
api_error_t my_close(handle_t h) {
(void)h; // stuff
return 0;
}
api_error_t my_process(handle_t h,
sample_t *in_ptr,
sample_t *out_ptr,
size_t nr_samples) {
audio_comp_t *c = (audio_comp_t*) h;
(void)c;(void)in_ptr;(void)out_ptr;// stuff
printf("doing something useful\n");
return 0;
}
有了这个,我们可以写 api.i 如下:
%module api
%{
#include "api.h"
%}
%include <stdint.i>
%typemap(in,numinputs=0) handle_t *new_h (handle_t tmp) %{
= &tmp;
%}
%typemap(argout) handle_t *new_h %{
if (!result) {
$result = SWIG_NewPointerObj(tmp$argnum, $descriptor(audio_comp_t *), 0 /*| SWIG_POINTER_OWN */);
}
else {
// Do something to make the error a Python exception...
}
%}
// From my earlier answer: https://whosebug.com/a/11029809/168175
%typemap(in,numinputs=0) handle_t self "=NULL;"
%typemap(check) handle_t self {
= arg1;
}
typedef struct {
api_error_t close(handle_t self);
api_error_t process(handle_t self,
sample_t *in_ptr,
sample_t *out_ptr,
size_t nr_samples);
} audio_comp_t;
%ignore audio_comp_t;
%include "api.h"
除了隐藏原始结构并声称它充满了成员函数而不是成员指针之外,我们还做了一些事情:
- 使 SWIG automatically pass the handle in as the 1st argument 而不是要求 Python 用户过于冗长。 (在 python 中变为
h.close()
而不是h.close(h)
) - 使用 argout typemap 包装真正的
comp_init
函数,而不是仅仅替换它。这纯粹是一个偏好问题,我只是添加它来展示它的使用方式。
这让我的 运行 以下 Python:
import api
h=api.comp_init()
print(h)
h.process(None, None, 0)
h.close()
如果您愿意对 API 的 header 进行一些外观更改以促进东西。
我在 api.h、MAKE_API_FUNC
中引入了一个宏,它包装了您原来在其中的 typedef 语句。当使用 C 编译器编译时,它仍然会产生完全相同的结果,但是它可以让我们使用 SWIG 更好地操作。
所以 api.h 现在看起来像这样:
// simplified api.h
#include <stdint.h>
#include <stdlib.h>
typedef uint32_t api_error_t;
typedef void *handle_t;
typedef void sample_t;
#ifndef MAKE_API_FUNC
#define MAKE_API_FUNC(name, type, ...) typedef api_error_t comp_ ## name ## _t(__VA_ARGS__)
#endif
MAKE_API_FUNC(close, audio_comp_t, handle_t);
MAKE_API_FUNC(process, audio_comp_t, handle_t, sample_t *, sample_t *, size_t);
typedef struct
{
comp_close_t *close;
comp_process_t *process;
} audio_comp_t;
// prototype for init
api_error_t comp_init(handle_t *new_h);
所以在 api.i 中,我们现在用另一个宏替换那个宏,它向 SWIG 声称函数指针 typedef 实际上是一个结构,具有专门提供的 __call__
函数。通过创建这个额外的函数,我们可以将所有 Python 参数自动代理到对实际函数指针的调用中。
%module api
%{
#include "api.h"
%}
%include <stdint.i>
// From: https://whosebug.com/a/2653351
#define xstr(a) str(a)
#define str(a) #a
#define name_arg(num, type) arg_ ## num
#define param_arg(num, type) type name_arg(num, type)
#define FE_0(...)
#define FE_1(action,a1) action(0,a1)
#define FE_2(action,a1,a2) action(0,a1), action(1,a2)
#define FE_3(action,a1,a2,a3) action(0,a1), action(1,a2), action(2,a3)
#define FE_4(action,a1,a2,a3,a4) action(0,a1), action(1,a2), action(2,a3), action(3,a4)
#define FE_5(action,a1,a2,a3,a4,a5) action(0,a1), action(1,a2), action(2,a3), action(3,a4), action(4,a5)
#define GET_MACRO(_1,_2,_3,_4,_5,NAME,...) NAME
%define FOR_EACH(action,...)
GET_MACRO(__VA_ARGS__, FE_5, FE_4, FE_3, FE_2, FE_1, FE_0)(action,__VA_ARGS__)
%enddef
%define MAKE_API_FUNC(name, api_type, ...)
%nodefaultctor comp_ ## name ## _t;
%nodefaultdtor comp_ ## name ## _t;
typedef struct {
%extend {
api_error_t __call__(FOR_EACH(param_arg, __VA_ARGS__)) {
return $self(FOR_EACH(name_arg, __VA_ARGS__));
}
}
} comp_ ## name ## _t;
// Workaround from: https://github.com/swig/swig/issues/609
%rename("%s_fptr", "%$isvariable", "match$ismember"="1", "match$type"=xstr(comp_ ## name ## _t)) name;
%extend api_type {
%pythoncode %{
name = lambda self, *args: self.name ## _fptr(self, *args)
%}
}
%enddef
%ignore comp_init;
%include "api.h"
%extend audio_comp_t {
audio_comp_t() {
handle_t new_h = NULL;
api_error_t err = comp_init(&new_h);
if (err) {
// throw or set Python error directly
}
return new_h;
}
~audio_comp_t() {
(void)$self;
// Do whatever we need to cleanup properly here, could actually call close
}
}
这是从 Python 的角度使用相同的 preprocessor mechanisms I used in my answer on wrapping std::function
objects, but applied to the function pointers of this problem. In addition I used %extend
to make a constructor/destructor,这使得 API 更好用。如果这是真正的代码,我可能也会使用 %rename
。
话虽如此,我们现在可以使用以下 Python 代码:
import api
h=api.audio_comp_t()
print(h)
print(h.process)
h.process(None, None, 0)
参见 SWIG docs 讨论如何将错误代码很好地映射到 Python 的异常。
我们可以通过一个简单的技巧消除迭代可变参数宏参数的需要,从而进一步简化它。如果我们将 api.h 宏更改为采用 3 个参数,其中第 3 个参数是函数指针的所有参数,如下所示:
// simplified api.h
#include <stdint.h>
#include <stdlib.h>
typedef uint32_t api_error_t;
typedef void *handle_t;
typedef void sample_t;
#ifndef MAKE_API_FUNC
#define MAKE_API_FUNC(name, type, args) typedef api_error_t comp_ ## name ## _t args
#endif
MAKE_API_FUNC(close, audio_comp_t, (handle_t self));
MAKE_API_FUNC(process, audio_comp_t, (handle_t self, 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
api_error_t comp_init(handle_t *new_h);
然后我们现在可以更改我们的 SWIG 接口,不提供我们通过 %extend
添加的 __call__
函数的定义,而是编写一个直接调用我们想要的函数指针的宏:
%module api
%{
#include "api.h"
%}
%include <stdint.i>
// From: https://whosebug.com/a/2653351
#define xstr(a) str(a)
#define str(a) #a
%define MAKE_API_FUNC(name, api_type, arg_types)
%nodefaultctor comp_ ## name ## _t;
%nodefaultdtor comp_ ## name ## _t;
%{
#define comp_ ## name ## _t___call__(fptr, ...) fptr(__VA_ARGS__)
%}
typedef struct {
%extend {
api_error_t __call__ arg_types;
}
} comp_ ## name ## _t;
// Workaround from: https://github.com/swig/swig/issues/609
%rename("%s_fptr", "%$isvariable", "match$ismember"="1", "match$type"=xstr(comp_ ## name ## _t)) name;
%extend api_type {
%pythoncode %{
name = lambda self, *args: self.name ## _fptr(self, *args)
%}
}
%enddef
%ignore comp_init;
%include "api.h"
%extend audio_comp_t {
audio_comp_t() {
handle_t new_h = NULL;
api_error_t err = comp_init(&new_h);
if (err) {
// throw or set Python error directly
}
return new_h;
}
~audio_comp_t() {
(void)$self;
// Do whatever we need to cleanup properly here, could actually call close
}
}
这里棘手的是 typedef struct {...} name;
惯用语的使用使得重命名或隐藏结构内的函数指针变得更加困难。 (然而,这只是保持自动添加 handle_t
参数所必需的)。