SWIG将C库与Python接口(SWIG生成的类使用起来很麻烦)
我正在使用SWIG生成到我的C库的Python语言绑定。我已经成功地构建了绑定和导出的数据结构,但在使用该库时,我不得不克服一些困难 例如,C标头具有如下数据类型和函数原型:SWIG将C库与Python接口(SWIG生成的类使用起来很麻烦),python,c,swig,python-extensions,Python,C,Swig,Python Extensions,我正在使用SWIG生成到我的C库的Python语言绑定。我已经成功地构建了绑定和导出的数据结构,但在使用该库时,我不得不克服一些困难 例如,C标头具有如下数据类型和函数原型: struct MyStruct { /* fields */ } struct MyStruct * MYSTRUCT_Alloc(void); void MYSTRUCT_Free(struct MyStruct *); struct MyStruct * MYSTRUCT_Clone(const stru
struct MyStruct
{
/* fields */
}
struct MyStruct * MYSTRUCT_Alloc(void);
void MYSTRUCT_Free(struct MyStruct *);
struct MyStruct * MYSTRUCT_Clone(const struct MyStruct *);
int MYSTRUCT_Func1(const struct MyStruct *, const int);
/* and so on */
def gFuncWrapper(mystruct):
return fb.gFunc(mystruct.ms)
在我的SWIG接口文件中,我导出函数和MyStruct数据类型。假设我的python扩展模块名为foobar,那么我可以编写如下python脚本:
#import foobar as fb
# The line below creates a Python class which is a wrapper to MyStruct. HOWEVER I cannot pass this class to a function like MYSTRUCT_Func1 until I have initialized it by calling MYSTRUCT_Alloc ...
ms = fb.MyStruct
# This will fail (throws a Python exception)
# ret = fb.MYSTRUCT_Func1(ms, 123)
# However this works
ms = fb.MYSTRUCT_Alloc()
ret = fb.MYSTRUCT_Func1(ms, 123)
声明一个对象,然后在使用它之前分配一个指向它的指针是非常麻烦的(而且容易出错)。是否有更好的方法使用SWIG生成的类?。我正在考虑包装更高级别的类(或对SWIG生成的类进行子类化),以自动处理对象的创建和销毁(以及提供一些明显的成员函数,如MYSTRUCT_Func1()
但是,如果我对SWIG生成的类进行了包装/子类化,那么我不确定是否可以将新类传递给需要指向C结构的指针的C API函数。我不能直接修改SWIG生成的类(或者至少不应该修改),原因很明显
解决这个问题的最佳方法是什么?一种更具Python风格的创建/销毁对象的方法,同时能够将指针直接传递到公开的C函数?在Python端编写包装器对我来说似乎是个好主意,但不确定为什么您认为这样做行不通
class MyStructWrapper:
def __init__(self):
self.ms = fb.MYSTRUCT_Alloc()
def __del__(self):
fb.MYSTRUCT_Free(self.ms)
def func1(self, arg):
return fb.MYSTRUCT_Func1(self.ms, arg)
如果需要访问结构的成员,那么可以使用self.ms.member
或编写getter和setter
您还可以将克隆
功能安装到此设计中
编辑:关于您的评论,假设您有一个全局函数,它的指针指向MyStruct
:
int gFunc(MyStruct* ms);
在Python端,您可以编写如下包装器:
struct MyStruct
{
/* fields */
}
struct MyStruct * MYSTRUCT_Alloc(void);
void MYSTRUCT_Free(struct MyStruct *);
struct MyStruct * MYSTRUCT_Clone(const struct MyStruct *);
int MYSTRUCT_Func1(const struct MyStruct *, const int);
/* and so on */
def gFuncWrapper(mystruct):
return fb.gFunc(mystruct.ms)
我希望这会有所帮助。您可以将SWIG生成的模块命名为
\u foobar
,并编写一个纯Python模块foobar
,该模块定义了必要的pythonic接口,例如,openssl
包装器遵循该方法
另一个选项是使用Cython直接在C中创建接口:
cdef class MyStruct:
cdef MyStruct_ptr this
def __cinit__(self):
self.this = MYSTRUCT_Alloc();
if self.this is NULL:
raise MemoryError
def __dealloc__(self):
if self.this is not NULL:
MYSTRUCT_Free(self.this)
def func1(self, n):
return MYSTRUCT_Func1(self.this, n)
这将创建Python C扩展类型MyStruct
,可从Python中用作:
ms = Mystruct()
print(ms.func1(123))
有关完整示例,请参见。您的代码:
ms = fb.MyStruct
# This will fail (throws a Python exception)
# ret = fb.MYSTRUCT_Func1(ms, 123)
仅将类分配给ms
,而不是类的实例,这就是注释行失败的原因。以下操作应有效:
ms = fb.MyStruct()
ret = fb.MYSTRUCT_Func1(ms, 123)
我也有自由(非成员)函数,它们期望C struct ptr作为参数。目前,我可以将SWIG生成的类传递给那些公开的C函数。我可以类似地将MyStructRapper传递给这些函数吗?如果没有,是否有“特殊方法”例如,我可以在包装类上实现的值,以便在传递给需要C结构指针的函数时返回原始指针?当然,您需要为全局函数创建Python包装函数。这些函数将使用Python包装类,但将ms成员发送到C函数。我将更新我的回答是用一个例子。考虑使用cType来访问你的库而不是SWIG。啊,这样我就错了(我是一个Python Nebe)。它现在工作正常。最后一件事。我想给MyStultSub()类添加新的行为和方法。但关键的是,我仍然希望能够通过“增强类”。到需要原始指针的函数(目前,我可以将自动生成的MyStruct类传递给需要原始指针的导出C函数)。显然,我不能直接修改生成的代码,所以我尝试将MyStruct子类化,但当我将新类传递给我的C函数时,我得到了类型为“struct MyStruct*”的TypeError异常参数1。我想在MyStruct周围提供一个包装器的另一个原因是为了能够正确地销毁它。它是一个嵌套结构和一些函数实际上,从堆中分配内存-因此它被正确地销毁,以避免MEM泄漏。您是否考虑编写C++类并使用SWIG来暴露它?然后,您可以定义适当的构造函数和析构函数并添加其他方法。SWIG将将该类平坦化为C方法并提供Python包。每个类都是在Python中再次扮演一个类。马克说的是个好主意。我对这个问题的回答是在Python方面做这样的事情。如果你觉得在C++上比Python更舒服,在C++上做的更好。我觉得你都对。我已经写了C++的包装。C库以便于从Python中使用。我将接受马克的回答,因为他指出了如何从Python中使用库,并建议使用C++包装器简化事情。