Ссылки
PyBindGen
PyBindGen - это генератор биндингов к коду на C/C++ для Python. Он позволяет сгенерировать код на C или C++, компилирующийся в динамическую библиотеку. Эта библиотека будет модулем Python с интерфейсом для всех классов и функций C/C++.Есть два основных варианта его использования:
- Использовать модуль pybindgen и вручную описать на Python все классы, методы и функции, для которых нужно сгенерировать биндинги.
- Использовать модуль pybindgen.gccxmlparser, который позволяет распарсить заголовочные файлы C/C++ с помощью pygccxml и автоматически сгенерировать скрипт Python, аналогичный написанному вручную в пункте (1). Этот скрипт будет в свою очередь генерировать биндинги.
Атрибуты
При возникновении неоднозначных ситуаций, их нужно устранять, указывая для классов, функций и их параметров дополнительные атрибуты, в частности политику передачи владения объектами.- Если используется вариант (1), атрибуты указываются при добавлении методов в скрипте на Python.
- Если используется вариант (2), атрибуты указываются в коде на C/C++ в комментариях специального формата (аннотациях).
Аннотации
Аннотация - это комментарий вида:// -#- АННОТАЦИЯ -#-
Между // и -#- должен быть пробел. Комментарий должен быть строго на предыдущей строке от объявления.
Для функции, метода или поля:
// -#- АННОТАЦИЯ -#- void foo();
Для класса или структуры:
class bar // -#- АННОТАЦИЯ -#- { ... };
Игнорирование
Аннотация ignore отключает генерацию биндингов для объявления.// -#- ignore -#- void foo()
Наследование
Аннотация allow_subclassing включает возможность наследования классов Python от классов C++.class foo // -#- allow_subclassing=true -#- { ... };
Если наследник в питоне имеет конструктор, нужно не забыть вызвать в нем конструктор базового класса, иначе сгенерированный биндинг будет проинициализирован неправильно.
class bar(foo): def __init__(self): foo.__init__(self) # do something
Подсчет ссылок
Если указать методы объекта, реализующие подсчет ссылок, они будут автоматически использоваться в Python. Это может значительно упростить соглашения о передачи владения объектов, см. раздел "Владение".class foo // -#- incref_method=IncRef; decref_method=DecRef; peekref_method=PeekRef -#- { void IncRef(); void DecRef(); int PeekRef(); };
Владение
Эти атрибуты устраняют неоднозначности поведения сгенерированного кода при передаче и возвращении указателей. Набор атрибутов аналогичен политикам в Boost.Python.Использовать объекты с подсчетом ссылок проще:
- политики владения для объектов без подсчета ссылок зависят от поведения каждого конкретного метода и способов создания и удаления объектов;
- политики для объектов с подсчетом ссылок зависят только от соглашения, вызывает ли incref() и decref() вызывающая сторона или вызываемая, в остальном владение автоматически разруливается подсчетом ссылок.
Аннотации имеют вид:
// -#- @параметр(атрибут=значение, атрибут=значение); @параметр(...); ... -#-
Специальный параметр @return обозначает возвращаемое значение.
Атрибуты для объектов без подсчета ссылок
Параметры
Атрибут transfer_ownership определяет, передает ли Python владение переданным объектом коду C++:
- Если равен true:
- Python теряет владение объектом.
- Python больше не отвечает за удаление объекта.
- Python обнулит все свои ссылки на этот объект.
- Конструктор копирования не вызывается.
- Если равен false:
- Python сохраняет владение объектом.
- Для объекта будет вызван конструктор копирования и в C++ будет передана копия. Если у объекта запрещен конструктор копирования, биндинги не скомпилируются. Если объект полиморфный, могут возникнуть проблемы в рантайме.
// -#- @p1(transfer_ownership=false) -#- void myfunc(myclass *p1);
Возвращаемое значение
Атрибут caller_owns_return определяет, получает ли Python владение объектом, возвращенным из C++:
- Если равен true:
- Python получает владение объектом.
- Python отвечает за удаление объекта.
- C++-код больше не имеет права использовать этот объект.
- Конструктор копирования не вызывается.
- Если равен false:
- Python не владеет объектом.
- Python не отвечает за удаление объекта.
- C++-код по-прежнему может использовать объект и отвечает за его удаление.
- Для возвращенного объекта будет вызван конструктор копирования, и в в Python будет использоваться созданная копия. Если у объекта запрещен конструктор копирования, биндинги не скомпилируются. Если объект полиморфный, могут возникнуть проблемы в рантайме.
// -#- @return(caller_owns_return=true) -#- myclass * myfunc();
Атрибут reference_existing_object может модифицировать поведение caller_owns_return:
- Если равен true:
- Python получает владение объектом.
- Python отвечает за удаление объекта.
- C++-код по-прежнему может использовать объект.
- C++-код при этом больше не отвечает за удаление объекта. Программист ответственен за то, чтобы Python не удалил объект раньше, чем C++-код перестал его использовать.
- Конструктор копирования не вызывается.
// -#- @return(reference_existing_object=true) -#- myclass * myfunc();
struct foo { bar b; // -#- @return(return_internal_reference=true) -#- bar * get_bar() { return &b; } };
Атрибуты объектов с подсчетом ссылок
Параметры
Атрибут transfer_ownership определяет, кто отвечает за увеличения счетчика ссылок.
- Если равен true, Python должен увеличить счетчик ссылок перед вызовом, если хочет продолжать использовать объект.
- Если равен false, Python не должен ничего делать. Если C++-код хочет пользоваться объектом, он сам увеличит счетчик ссылок.
Возвращаемое значение
Атрибут caller_owns_return определяет, кто отвечает за увеличения счетчика ссылок.
- Если равен true, C++-код уже увеличил счетчик ссылок перед тем, как вернуть объект.
- Если равен false, Python должен сам увеличить счетчик ссылок, если хочет пользоваться объектом.
Выходные аргументы
Если атрибут direction равен out, C++-код будет модифицировать переданный объект.// -#- @p1(direction=out) -#- void foo(int *p1);
Массивы
Если параметр является массивом, его длину можно указать с помощью атрибута array_length.// -#- @a1(direction=out, array_length=100) -#- void bar(int *a1);