2

我正在使用 SWIG 为我的 C 库生成 Python 语言绑定。我已经设法构建绑定和导出的数据结构,但是在使用该库时我不得不跳过一些障碍。

例如,C 标头具有如下数据类型和函数原型:

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 */

在我的 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 生成的类)以自动处理对象的创建和销毁(以及提供一些 OBVIOUS 成员函数,如 MYSTRUCT_Func1()。

但是,如果我对 SWIG 生成的类进行包装/子类化,那么我不确定是否可以将新类传递给期望指向 C 结构的指针的 C API 函数。我不能直接修改 SWIG 生成的类(或者至少我不应该)——出于显而易见的原因。

解决此问题的最佳方法是什么?创建/销毁对象的更 Pythonic 方式,同时能够将指针直接传递给暴露的 C 函数?

4

3 回答 3

2

在 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 来实现。

您也可以将您的clone功能融入此设计。

编辑:关于您的评论,假设您有一个全局函数,它需要一个指向MyStruct

int gFunc(MyStruct* ms);

在 Python 端,您可以编写一个包装器,如下所示:

def gFuncWrapper(mystruct):
    return fb.gFunc(mystruct.ms)

我希望这有帮助。

于 2011-11-08T02:20:59.587 回答
2

你的代码:

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) 
于 2011-11-08T09:20:45.843 回答
0

您可以命名您的 SWIG 生成的模块_foobar并编写一个纯 Python 模块foobar来定义必要的 Python 接口,例如,M2Crypto包装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))

有关完整示例,请参见wrap Person.h

于 2011-11-08T02:29:28.413 回答