Например для автоматической генерации биндингов дико хорош swig. Но и у него периодически рвет крышу при парзинге развесистых заголовков. В качестве prooflink см. например как сделан биндинг писона к ida pro в ida sdk - для этого большинство заголовочных файлов были жестоко отпатчены на предмет вставки где попало макроса SWIG
Но есть например и другой способ, про который я не помню где вычитал и потому буду нагло считать, что придумал его сам.
Допустим что у нас есть некий C++ объект и мы хотим прикрутить его к perl:
class SomeUsefullClass
{
public:
SomeUsefullClass();
~SomeUsefullClass();
// this is our methods for binding
int MakeMeIncredibleRich(int howMuch);
int StoreResults(const char *fname);
// тут еще гора методов, которые мы не хотим биндить
protected:
SomeShittyClass m_Worker;
SomeOtherShittyClass m_Serializer;
// и тут еще гора методов, не имеющих к биндингу никакого отношения
};
C весьма высокой вероятностью если такое описание реального класса подсунуть swig, то его стошнит (заметим в скобках что нам еще придется указать всю кучу использованных header files). А если немного помедитировать над приведенным выше описанием, то можно заметить, что нам не нужны все кишки и потроха реального класса - например swigу можно было бы подсунуть ровно то что нам нужно
class SomeUsefullClass
{
public:
SomeUsefullClass();
~SomeUsefullClass();
int MakeMeIncredibleRich(int howMuch);
int StoreResults(const char *fname);
}
Такой заголовок компилится без проблем. И даже иногда линкуется с библиотекой, содержащей реализацию SomeUsefullClass. Но чаще не линкуется и не работает. Потому что мы хотим вызвать конструктор/деструктор реального объекта.И тут нам может помочь директива %exception (или даже пара %newobject & %delobject):
%module SomeUsefullClass
%{
extern SomeUsefullClass *get_new_SomeUsefullClass();
extern void delete_SomeUsefullClass(SomeUsefullClass *);
%}
%exception SomeUsefullClass::SomeUsefullClass() %{
result = get_new_SomeUsefullClass();
%}
%exception SomeUsefullClass::~SomeUsefullClass() %{
delete_SomeUsefullClass(arg1);
%}
// тут фальшивое описание класса SomeUsefullClass
Остается написать пару методов get_new_SomeUsefullClass & delete_SomeUsefullClass и все прекрасно собирается и работает.
Однако поскольку я чудовищно ленив, то и этот метод на мой вкус требует слишком много ненужной писанины (и хуже того - дублирования кода в разных файлах !). Можно поступить еще проще - в заголовках реального класса SomeUsefullClass проставить комменты с нужными атрибутами для нужных методов, и потом например банальным perl скриптом автоматически сгенерировать файл интерфейса для swig и по паре методов для заменяемых конструктора/деструктора. Итого наш сорец может выглядеть примерно так:
//@bind_class
class SomeUsefullClass
{
public:
//@fake_constructor
SomeUsefullClass();
//@fake_destructor
~SomeUsefullClass();
//@bind_method
int MakeMeIncredibleRich(int howMuch);
//@bind_method
int StoreResults(const char *fname);
protected:
SomeShittyClass m_worker;
SomeOtherShittyClass m_Serializer;
...
};
//@end_bind_class
Написание простого парзера, распознающего нужные атрибуты, оставляется в качестве тривиального упражнения для читателя (потому что я ленив например)
SWIG c STL-контейнерами работает через какую-то анальную жопу: я так и не смог добиться что бы, к примеру, возвращаемый моим методом std::map прозрачно и без лишних костылей транслировался в пайтоновский dictionary.
ОтветитьУдалитьХотя, возможно, просто неасилел.
да, с stl есть такая проблема у swig
ОтветитьУдалитьно скажи честно - часто ли тебе нужно прямо целиком stl объекты прокидывать в binding, если и в perl и в писоне есть свои собственные типы данных для словарей и можно написать тривиальный метод для конвертации ?
> можно написать тривиальный метод для конвертации
ОтветитьУдалитьТак и делаю обычно, но вообще хотелось бы обходиться без лишних промежуточных сущностей, тем более, что они достаточно шаблонны и однотипны.
Конкретно под пайтон в бусте какие-то биндинги есть, всё хочу глянуть, как руки дойдут - может там дела получше обстоят.
а в perlе есть например typemap файлы - тоже достаточно один раз написать
ОтветитьУдалитьНо по моему это bad design - возвращать в script engine списки объектов. В крайнем случае можно сделать для своего объекта tied hash например для эмуляции коллекций