SWIG-интерфейс библиотеки C на Python (сгенерированные SWIG классы громоздки в использовании) - PullRequest
2 голосов
/ 08 ноября 2011

Я использую SWIG для генерации привязок языка Python к моей библиотеке C.Мне удалось построить привязки и экспортированные структуры данных, но мне приходится перепрыгивать через некоторые обручи при использовании библиотеки.

Например, заголовок 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 ().

HOWEVER, если я это сделаюОберните / создайте подклассы сгенерированных SWIG классов, тогда я не уверен, что смогу передать новые классы функциям API C, которые ожидают указатель на структуру C. Я не могу изменить сгенерированные SWIG классы напрямую (или, по крайней мере, я не долженt) - по очевидным причинам.

Как лучше всего решить эту проблему? Более питонический способ создания / уничтожения объектов, в то же время имея возможность передавать указатели непосредственно на открытые функции C?

Ответы [ 3 ]

2 голосов
/ 08 ноября 2011

Ваш код:

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) 
2 голосов
/ 08 ноября 2011

Написание оболочки на стороне 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 или написав методы получения и установки.

Вы также можете приспособить свою функцию clone к этому дизайну.

Редактировать : Что касается вашего комментария, допустим, у вас есть глобальная функция, которая принимает указатель на MyStruct:

int gFunc(MyStruct* ms);

На стороне Python вы можете написать оболочку следующим образом:

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

Надеюсь, это поможет.

0 голосов
/ 08 ноября 2011

Вы можете назвать свой сгенерированный SWIG-модуль _foobar и написать чистый Python-модуль foobar, который определяет необходимый питонический интерфейс, например, 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 для полного примера.

...