Question

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?

Was it helpful?

Solution

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) 

OTHER TIPS

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top