SWIG 将 C 库连接到 Python(SWIG 生成的类使用起来很麻烦)

发布于 2024-12-14 05:31:54 字数 1230 浏览 2 评论 0 原文

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

但是,如果我包装/子类化 SWIG 生成的类)类,那么我不确定是否可以将新类传递给需要指向 C 结构的指针的 C API 函数,我无法直接修改 SWIG 生成的类(或者至少我不能)。不应该)-出于明显的原因,

解决这个问题的最佳方法是什么?一种更Python式的创建/销毁对象的方法,同时能够直接将指针传递给公开的C函数?

I am using SWIG to generate Python language bindings to my C library. I have managed to build the bindings and exported data structures, but I'm having to jump through some hoops when using the library.

For example the C header has data types and function prototypes as follows:

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

In my SWIG interface file, I am exporting both the functions and the MyStruct data type. Assuming my python extension module is called foobar, I can then write Python script like this:

#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)

It is very cumbersome (and error prone) to declare an object and then assign a pointer to it before using it. Is there a better way of using the SWIG generated classes?. I was thinking of wrapping higher level classes (or subclassing the SWIG generated classes) to automagically take care of object creation and destruction (as well as providing some OBVIOUS member functions like MYSTRUCT_Func1().

HOWEVER, if I do wrap/subclass the SWIG generated classes, then I am not sure that I can pass the new classes to the C API functions that expect a pointer to a C struct. I can't modify the SWIG generated classes directly (or atleast I shouldn't) - for OBVIOUS reasons.

What is the best way to solve this problem? A more pythonic way of creating/destroying objects, whilst at the same time being able to pass pointers directly to the exposed C functions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

〆凄凉。 2024-12-21 05:31:54

您的代码:

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) 

Your code:

ms = fb.MyStruct

# This will fail (throws a Python exception) 
# ret =  fb.MYSTRUCT_Func1(ms, 123) 

Only assigns the class to ms, not an instance of a class, which is why the commented line fails. The following should work:

ms = fb.MyStruct()
ret =  fb.MYSTRUCT_Func1(ms, 123) 
羁绊已千年 2024-12-21 05:31:54

在 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)

我希望这个有帮助。

Writing a wrapper on the Python side seems like a good idea to me, not sure why you think that is not going to work.

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)

And if you need to access the members of the struct, then you can do so with self.ms.member or by writing getters and setters.

You can also fit your clone function into this design.

Edit: Regarding your comment, let's say you have a global function that takes a pointer to MyStruct:

int gFunc(MyStruct* ms);

In the Python side, you can write a wrapper as follows:

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

I hope this helps.

不醒的梦 2024-12-21 05:31:54

您可以将 SWIG 生成的模块命名为 _foobar 并编写一个纯 Python 模块 foobar 来定义必要的 pythonic 接口,例如 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 中使用的 Python C 扩展类型 MyStruct

ms = Mystruct()
print(ms.func1(123))

请参阅 包装 Person.h 以获得完整示例。

You could name your SWIG-generated module _foobar and write a pure Python module foobar that defines the necessary pythonic interface e.g., M2Crypto an openssl wrapper follows that approach.

Another option is to use Cython to create the interface straight in 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)

This creates Python C extension type MyStruct that can be used from Python as:

ms = Mystruct()
print(ms.func1(123))

See wrap Person.h for a complete example.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文