Интерфейс библиотеки C SWIG для Python (классы, созданные SWIG, громоздки в использовании)

Я использую 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 классов), чтобы автоматически заботиться о создании и уничтожении объектов (а также о предоставлении некоторых ОЧЕВИДНЫХ функций-членов, таких как MYSTRUCT_Func1().

ОДНАКО, если я оберну/подклассифицирую сгенерированные SWIG классы, то я не уверен, что смогу передать новые классы функциям C API, которые ожидают указатель на структуру C. Я не могу напрямую изменять сгенерированные классы SWIG (или, по крайней мере, не должен) - по ОЧЕВИДНЫМ причинам.

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


person Homunculus Reticulli    schedule 08.11.2011    source источник
comment
Рассмотрите возможность использования ctypes для доступа к вашей библиотеке вместо SWIG.   -  person ThiefMaster    schedule 08.11.2011


Ответы (3)


Ваш код:

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) 
person Mark Tolonen    schedule 08.11.2011
comment
Ах, так вот, где я ошибаюсь (я новичок в питоне). Теперь он работает правильно. И последнее. Я хотел бы добавить новые поведения и методы в класс MyStruct(), НО, что особенно важно, я все еще хочу иметь возможность передавать расширенный класс функциям, которые ожидают необработанный указатель (в настоящее время я МОГУ передать автоматически сгенерированный класс MyStruct моему классу). экспортированные функции C, которые ожидают необработанный указатель). Очевидно, что я не могу напрямую изменить сгенерированный код, поэтому я попытался создать подкласс MyStruct, но когда я передаю новый класс в свою функцию C, я получаю аргумент исключения TypeError 1 типа 'struct MyStruct *' - person Homunculus Reticulli; 08.11.2011
comment
Еще одна причина, по которой я хотел бы создать оболочку вокруг MyStruct, заключается в том, что я могу правильно ее уничтожить. Это вложенная структура, и некоторые функции фактически выделяют память из кучи, поэтому ее необходимо правильно уничтожить, чтобы избежать утечки памяти. - person Homunculus Reticulli; 08.11.2011
comment
Рассматривали ли вы возможность написания класса C++ и использования SWIG для его раскрытия? Затем вы можете определить правильные конструкторы и деструктор и добавить дополнительные методы. SWIG выполнит работу по объединению класса в методы C и предоставит класс-оболочку Python, чтобы он снова действовал как класс в Python. - person Mark Tolonen; 08.11.2011
comment
То, что сказал @Mark, - хорошая идея. Мой ответ на этот вопрос предлагает сделать что-то подобное, но на стороне Python. Выполнение этого на стороне C++ было бы лучше, если вы чувствуете себя более комфортно, работая на C++, чем на Python. - person Miguel; 08.11.2011
comment
Марк и Мигель. Я думаю, вы оба правы. Я написал обертки C++ для библиотеки C, чтобы облегчить использование Python. Я приму ответ Марка, потому что он указал, как использовать библиотеку из Python, а также предложил использовать оболочку C++ для упрощения. - person Homunculus Reticulli; 12.11.2011
comment
@Homunculus Reticulli: Или вы можете написать тип расширения в Cython как предложено в моем ответе вместо C++. Это позволяет вам вызывать Python прозрачно, а также эффективно вызывать C. В качестве бонуса вы используете Python-подобный синтаксис вместо C++ в этом случае, в конце концов, вы решили написать Python-оболочку для библиотеки C, поэтому вы могли увидеть некоторые преимущества в использовании языка Python. - person jfs; 13.11.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)

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

person Miguel    schedule 08.11.2011
comment
У меня также есть бесплатные (не являющиеся членами) функции, которые ожидают C struct ptrs в качестве аргументов. В настоящее время я могу передать созданный SWIG класс этим открытым функциям C. Могу ли я аналогичным образом передать MyStructWrapper этим функциям? Если нет, есть ли специальный метод, который я могу, например. значение, которое я могу реализовать в классе-оболочке, чтобы он возвращал необработанный указатель при передаче функции, которая ожидает указатели на структуру C? - person Homunculus Reticulli; 08.11.2011
comment
Конечно, вам нужно создать функции-оболочки Python для глобальных функций. Они возьмут класс-оболочку Python, но отправят член ms функциям C. Я обновлю свой ответ примером. - person Miguel; 08.11.2011