15 мая 2013 г.

Аннотации в PyBindGen

 Ссылки


PyBindGen

PyBindGen - это генератор биндингов к коду на C/C++ для Python. Он позволяет сгенерировать код на C или C++, компилирующийся в динамическую библиотеку. Эта библиотека будет модулем Python с интерфейсом для всех классов и функций C/C++.

Есть два основных варианта его использования:
  1. Использовать модуль pybindgen и вручную описать на Python все классы, методы и функции, для которых нужно сгенерировать биндинги.
  2. Использовать модуль pybindgen.gccxmlparser, который позволяет распарсить заголовочные файлы C/C++ с помощью pygccxml и автоматически сгенерировать скрипт Python, аналогичный написанному вручную в пункте (1). Этот скрипт будет в свою очередь генерировать биндинги.

Атрибуты

При возникновении неоднозначных ситуаций, их нужно устранять, указывая для классов, функций и их параметров дополнительные атрибуты, в частности политику передачи владения объектами.
  1. Если используется вариант (1), атрибуты указываются при добавлении методов в скрипте на Python.
  2. Если используется вариант (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();

 

Атрибут return_internal_reference указывает, что метод вернул указатель на объект, являющийся составной частью вызываемого объекта.

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);

Комментариев нет:

Отправить комментарий