Tutorial

Материал из pyhrol.ru
Перейти к: навигация, поиск
На этой странице были произведены изменения, не отмеченные для перевода.


Другие языки:English 98% • ‎русский 100%

Все перечисленые ниже примеры "живые", их можно запустить локально

Номер Название Ссылки
Функции
0010 Простая функция .cpp, .py
0020 Функция с аргументами .cpp, .py
0030 Функция с возвращаемым значением .cpp, .py
0040 Аргументы по умолчанию .cpp, .py
0050 Именованные аргументы .cpp, .py
0060 Перегрузка аргументов .cpp, .py
0070 "Перегрузка" возвращаемых значений .cpp, .py
0080 Объектные типы .cpp, .py
0090 Объекты заданного типа .cpp, .py
0100 Произвольные типы .cpp, .py
0110 Обработка ошибок .cpp, .py
0120 Справка .cpp
Классы
0400 Определение .cpp, .py
0410 Создание/разрушение .cpp, .py
0420 Методы .cpp, .py
0430 Статические методы .cpp, .py
0440 Поля .cpp, .py
0450 Атрибуты .cpp, .py
0455 Атрибуты 2 .cpp, .py
0460 Указатели .cpp, .py
0470 Синглетон .cpp, .py
0480 "Умные" указатели .cpp, .py
0490 Арифметические операции (Number Protocol) .cpp, .py
0495 Арифметические операции над произвольными типами .cpp, .py
0500 Последовательности (Sequence Protocol) .cpp, .py
0505 Операции над последовательностями с произвольными типами .cpp, .py
0510 Ассоциативные массивы (Mapping Protocol) .cpp, .py
0520 Итераторы (Iterator Protocol) .cpp, .py
0540 Наследование .cpp, .py
0550 Аргументы объектного типа .cpp, .py
0560 "Внешний" конструктор .cpp, .py
Прочее
0800 Модуль pyhrol .py
0810 Трассировка .py, .txt
0820 Форматирование справки .py, .txt
0830 Версия с минимальным функционалом .py
0840 Перегрузка по имени аргумента .cpp, .py
0850 Сокрытие методов .cpp, .py
Типичные ошибки
0900 Неверное наследование .cpp, .py
0910 Конфликты в таблице символов .py
0920 Неверное использование макросов .cpp, .py
0925 Неверное использование макросов в классах .cpp, .py
0926 Неверное использование макросов в классах (обход) .py
0930 Неуникальность указателей .cpp, .py
0940 Недопустимые имена и символы .cpp, .py
0950 std::bad_cast .cpp, .py
0960 Закрытый конструктор копирования .cpp, .py

Функции

0010. Простая функция

Питонизирующий код простейшей функции выглядит так:

void simple_function(Tuples &_args)
{
PYHROL_AFTER_PARSE_TUPLE(_args)
PYHROL_AFTER_BUILD_VALUE(_args)
 
cout
<< __func__ << ": I am called" << endl
;
 
PYHROL_AFTER_EXECUTE_DEFAULT(_args)
}

Функция не имеет ни аргументов ни возвращаемого значения. Между макросами PYHROL_AFTER_BUILD_VALUE и PYHROL_AFTER_EXECUTE_DEFAULT может располагаться любой код, но согласно логике примера это должен быть вызов функции с сигнатурой:

void (*)();

Вместо этого вызова функция просто сообщает о себе в cout. Для регистрации функции используется следующий код:

static void __on_load() __attribute__ ((constructor));
void __on_load()
{
Container::container().add_function_no_args<simple_function>("simple_function", NULL);
}

Вызов функции в python-е тривиален. Попытка передать ей любой аргумент неизбежно закончится ошибкой TypeError.

см example_0010.cpp, example_0010.py

0020. Функция с аргументами

По сравнению с предыдущим примером добавился макрос PYHROL_PARSE_TUPLE_2, который требует, чтобы функции были переданы 2 аргумента:

int i;
char *pch;

которым соответсвуют python-типы int и str. Если хотя бы один из аргументов будет иметь неверный тип, либо аргументы будут указаны в другой последовательности, произойдет ошибка TypeError и код под макросом PYHROL_AFTER_BUILD_VALUE не будет выполнен.

см example_0020.cpp, example_0020.py

0030. Функция с возвращаемым значением

По сравнению с простой функцией добавился макрос PYHROL_BUILD_VALUE_2, который возвращает иницииализированные ранее 2 переменные:

int i;
const char *pch;

, которые представляются кортежем из 2 значений с типами int и str соответственно.

Возникает ощущение, что инициализация вышеуказанных переменных происходит после того, как они переданы в Tuples. Это обман зрения, неизбежный из-за зондирования. На самом деле макросы в теле этой функции образуют цикл, который выполняется не более 2-х раз, а область между макросами PYHROL_AFTER_BUILD_VALUE и PYHROL_AFTER_EXECUTE_DEFAULT -- не более одного, ессно. Не более одного означает, что в режиме зондирования этот код не выполняется вообще.

см example_0030.cpp, example_0030.py

0040. Аргументы по умолчанию

По сравнению с предыдущим примером аргументы получили некие начальные значения и добавлен вызов set_options:

int i = 0;
char *pch = const_cast<char *>("Zero");
 
_args.set_options(0);

Аргумент set_options означает, что все аргументы опциональны (0 обязательных). А значит под макросом PYHROL_AFTER_BUILD_VALUE переменные i и pch будут иметь либо значение по умолчанию, а оно в данном случае обязательно, либо будут инициализированы python-ом. python же может вызвать такую функцию с указанием обоих аргументов, с одним аргументом или вообще без них.

см example_0040.cpp, example_0040.py

0050. Именованные аргументы

Принципиальное отличие от всех предыдущих примеров -- функция зарегистрирована макросом PYHROL_REGISTER_FUNCTION_WITH_KEYWORDS. Это позволяет при вызове функции устанавливать значение ее аргументов по имени. Список имен аргументов при этом получается автоматически

см Именованные аргументы, example_0050.cpp, example_0050.py

0060. Перегрузка аргументов

В функцию введено сразу 5 макросов PYHROL_PARSE_TUPLE_* (в примере с неперегруженной функцией он был один). Это означает, что в python у функции function_with_overloaded_args 5 версий аргументов, причем одна из них не содержит аргументов вообще. Все макросы последовательно выполняются при каждом вызове function_with_overloaded_args до тех пор, пока сигнатура одного из них не совпадет с реальными аргументами, хранящимися в Tuples. Версию подошедшей сигнатуры возвращает метод Tuples::parsed_variant, нумерация с 0. Если ни одна из сигнатур не подходит, происходит ошибка TypeError, ее вызов заключен в макросе PYHROL_AFTER_PARSE_TUPLE.

Attention.png Значения переменных гарантированно определены только в случае, если Tuples::parsed_variant совпадает с номером макроса PYHROL_PARSE_TUPLE_*, в котором они инициализируются. Например при Tuples::parsed_variant = 2 pch и i не определены

см Правила перегрузки, example_0060.cpp, example_0060.py

0070. "Перегрузка" возвращаемых значений

По аналогии с предыдущим примером в функцию введено 5 макросов PYHROL_BUILD_VALUE*. Это означает, что питонизированная функция function_with_overloaded_retvals может возвращать значения одного из 5 типов, причем в одном случае функция не возвращает ничего (None). Вместо макроса PYHROL_AFTER_EXECUTE_DEFAULT как во всех предыдущих примерах функция завершена макросом PYHROL_AFTER_EXECUTE, который в качестве второго аргумента принимает целое число build_variant в диапазоне [0, 5), что соответствует выбранному типу возвращаемого значения. Несуразность этой функции состоит в том, что сам build_variant также является аргументом (инициализируется макросом PYHROL_PARSE_TUPLE_1). Выход build_variant за пределы диапазона [0, 5) в python-е будет интерпретирован как ошибка ValueError. Код обработки этой ошибки заключен в теле макроса PYHROL_AFTER_EXECUTE.

Пользователь в ответе за инициализацию возвращаемых значений, соответствующих значению build_variant, наименьшие трудозатраты необходимы при build_variant = 3

см example_0070.cpp, example_0070.py

0080. Объектные типы

Аргументом макроса PYHROL_PARSE_TUPLE_{M} может быть PyObject, который представляет собой любой объект. В данном примере переменная parg типа PyObject * инициализируется любым python аргументом, а динамическая проверка и сопоставление его типа ложится на плечи пользователя. Функция function_manipulate_objects просто возвращает имя типа своего аргумента в виде строки

см example_0080.cpp, example_0080.py

0090. Объекты заданного типа

По сравнению с предыдущим примером функция function_manipulate_objects_strict принимает PyObject одного из двух возможных подтипов:

т. е. имеет 2 перегруженные версии, подобно примеру с перегрузкой.

Аргументы в макросе PYHROL_PARSE_TUPLE_* дожны быть парными и иметь следующие типы:

  1. PyTypeObject или его потомок
  2. PyObject *

Количество таких пар не может превышать половины максимального количества аргументов макроса PYHROL_PARSE_TUPLE_*. parg, инициализированный макросом, соответсвующим Tuples::parsed_variant можно смело преобразовывать к типу, указанному в этом макросе. Несоответствие аргумента ни одному из перегруженных типов снова приводит к ошибке TypeError.

В примере 0550 аналогичное преобразование выполняется без небезопасного оператора reinterpret_cast.

см example_0090.cpp, example_0090.py

0100. Произвольные типы

В данном примере аргументы и возвращаемые значения преобразовываются к произвольному типу с помощью конвертеров. Две пары переменных используются так:

PYHROL_PARSE_TUPLE_{M} PYHROL_BUILD_VALUE_{N}
типы предков TupleIn::in_conv_t TupleIn::anything TupleOut::out_conv_t TupleOut::anything
экземпляры потомков input::converter input i output::converter output o

Структура input ожидает, что ее аргумент будет иметь тип PyString_Type и преобразовывает его значение, либо вызывает исключение std::runtime_error которое трансформируется в ошибку StandardError. Результат правильного преобразованния -- член input::n.

Структура output преобразовывает свой член output::n к типу PyString_Type если последний имеет ожидаемое значение, либо вызывает исключение std::runtime_error которое также трансформируется в ошибку StandardError.

Собственно функция function_with_convertor приравнивает значения обоих конверторов на чистом C++:

o.n = i.n;

Конверторы input и output готовы к повторному использованию и могут применяться (или не применяться) в соответветсвующих макросах согласно таблице выше. Количество конверторов TupleIn::anything не может превышать половины максимального количества аргументов макроса PYHROL_PARSE_TUPLE_*, а TupleOut::anything -- половины максимального количества аргументов макроса PYHROL_BUILD_VALUE_*.

см конвертеры, обработка исключений, example_0100.cpp, example_0100.py

0110. Обработка ошибок

Выполнение функции function_with_errors всегда завершается ошибкой, тип которой зависит от значения ее аргумента behaviour_variant:

Использование исключений предпочтительнее, т. к. python-ошибки могут быть нечаянно затерты последующими вызовами PyErr_Clear или переопределены другими вызовами PyErr_Format. Это может произойти по недосмотру ниже в той же функции (или в результате ошибки в самой библиотеке pyhrol). На момент инициирования ошибки вся ненужная не стековая память должна быть освобождена.

см обработка исключений, example_0110.cpp, example_0110.py

0120. Справка

Функция с двумя версиями аргументов и одной версией возвращаемых значений по умолчанию имеет следующее описание:

help_generation_example(...)
    Automatic help generation on function arguments

, но при включенной __PY_HACK_SIGNATURES или после первого исполнения (благодаря зондированию) включает в себя детальное описание всех аргументов, их типов, названий, значений по умолчанию:

help_generation_example(...)
    Automatic help generation on function arguments
    ->    s#|id tuple with per argument help:
          char */*not null*/, uint32_t  sI  str                                sample string
          int32_t                       i   i                   12             integer value with default
          double                        d   f                   3.14           float value with default
    ->    ids# tuple with brief help
    ...and addition
          int32_t                       i   i                                  
          double                        d   f                                  
          char */*not null*/, uint32_t  sI  str                                
    
    <-    idIs 
          int32_t                       i                                      help on res1
          double                        d                                      help on res2
          uint32_t                      I                                      help on res3
          char */*nullable*/            s                                      help on res4

Формат справки определяется группой переменных окружения PYHROL_*_FORMAT и переформатируется "на лету" функцией set_help_format. Расширенная справка не генерируется в режиме PYHROL_SAFE_MODE.

см справка, example_0120.cpp

Классы

В большинстве примеров используется простейший класс MyClass, с функционалом не сложнее Hello world!

0400. Определение

В python-е появляется новый класс MyClass, реализованный на C++ и "пристегнутый" с помощью библиотеки pyhrol. Наследуя от TypeWrapper он приобретает базовый набор методов, однако является абсолютно бесполезным, т. к. унаследованный TypeWrapper<T>::constructor не создает экземпляр класса, а выдает ошибку:

NotImplementedError: Dummy constructor not overridden (example_0400.MyClass.<ctor>)

Кроме того, единственный чисто виртуальный метод TypeWrapper<T>::destructor также является пустышкой, а значит и удалить объект MyClass тоже нельзя. Принципиальным является вызов статического метода PyType::init

см регистрация, example_0400.cpp, example_0400.py

0410. Создание/разрушение

По сравнению с предыдущим примером у класса MyClass появились конструктор и деструктор. Конструктор питонизирован стандартным способом.

Attention.png Обращение к переменной obj в конструкторе выше макроса PYHROL_AFTER_BUILD_VALUE недопустимо

В момент его вызова python уже выделил sizeof(MyClass) байт памяти, поэтому для вызова собственно конструктора MyClass используется "placement new" синтаксис оператора new:

new (&obj) MyClass(msg);

Соответственно для разрушения экземпляра MyClass используется явный вызов деструктора:

obj.~MyClass();

Привычный синтаксис new/delete можно видеть в примере с указателями, в котором PyType наследует от TypePointer

см example_0410.cpp, example_0410.py

0420. Методы

По сравнению с предыдущим примером добавлен единственный метод объекта MyClass. Его питонизатор аналогичен функции и отличается от нее только сигнатурой. Регистрация метода происходит в конструкторе PyType:

m_add_method<PyType, &PyType::say>("say", NULL);

см TypeWrapper::m_add_method и PYHROL_REGISTER_METHOD.

Таким образом это первый полностью функционирующий пример:

#!/usr/bin/python
 
import example_0420
 
c = example_0420.MyClass("Hello world!")
c.say()

, выводящий:

Hello world!
Attention.png Обращение к переменной obj в методах say и constructor выше макроса PYHROL_AFTER_BUILD_VALUE недопустимо

см example_0420.cpp, example_0420.py

0430. Статические методы

По сравнению с предыдущим примером:

  • к числу предков класса PyType добавлен TypeSpecial
  • MyClass2 наследуя от MyClass реализовал статическую версию метода say
  • в PyType реализован статический питонизатор метода say

Регистрируется метод в TypeSpecial:

m_add_class_method_with_keywords<&PyType::say>("say", NULL);

В итоге метод say вызывается и на классе и на объекте класса:

MyClass.say('Hello world!')
MyClass().say('Ciao mondo!')

Этот метод получает указатель на PyTypeObject, описывающий класс PyType. Тот же указатель возвращает статический метод TypeBase::m_get, доступный в любом другом контексте PyType.

TypeWrapper привлечен в качестве предка только потому, что он реализует чисто виртуальные методы TypeBase. Это пример метода класса, статический метод отличается от него только сигнатурой

см TypeSpecial, example_0430.cpp, example_0430.py

0440. Поля

Потомок MyClass имеет 3 поля. PyType, как обычно, наследует от TypeWrapper и реализует конструктор с деструктором. Особенность примера в том, что в конструкторе PyType питонизируются поля класса MyClass2, а не методы:

m_add_member<double, &MyClass2::weight>("weight", NULL);
m_add_member<const char *, &MyClass2::say_spanish>("say_spanish", NULL);
m_add_member<char [29], &MyClass2::say_russian>("say_russian", NULL);

Значения последних становятся доступны для чтения, а поле weight — для изменения из python-а. Размер массива say_russian (29) является неотъемлемой частью шаблонного типа. Строковые поля немодифицируемы в данном примере. Модификация полей любого типа и их отображение рассмотрены в следующем примере.

см типы полей, example_0440.cpp, example_0440.py

0450. Атрибуты

В предыдущем примере строковые поля C++-класса превратились в Python-атрибуты только для чтения. Для того чтобы сделать их доступными для изменения в PyType добавлены два метода get и set, отличающихся от стандартных только тем, что get не имеет (и не может иметь) аргументов, т. е. не содержит макросов PYHROL_PARSE_TUPLE_*, а set не имеет (и не может иметь) возвращаемых значений, т. е. не содержит макросов PYHROL_BUILD_VALUE_*. Зарегистрированы они следующим образом:

m_add_getter<PyType, &PyType::get>("message_ro", NULL);
m_add_getseter<PyType, &PyType::get, &PyType::set>("message_rw", NULL);

Очевидно, Python-класс MyClass получает два атрибута, один из которых модифицируемый. Связь этих атрибутов с C++-классом MyClass2 произвольна и определяется текущей реализацией методов get и set

см example_0450.cpp, example_0450.py

0455. Атрибуты 2

По сравнению с первой версией используется перегрузка и добавлен атрибут, допускающий только изменение. Класс называется point и содержит два дробных числа, символизирующих координаты. Для атрибута wo допустимы три версии значения:

p.wo = 1.134
p.wo = (1.134, -1245)
p.wo = complex(1.134, -1245)
Attention.png Во второй версии атрибут wo инициализируется кортежем из пары числовых значений, соответствующий ей макрос PYHROL_PARSE_TUPLE_2 обязательно предваряется установкой optTuple

Атрибут rw ровно то же, что ro и wo вместе взятые

В этом примере впервые использованы макросы PYHROL_REGISTER_(GET|SET|GETSE)TER

см example_0455.cpp, example_0455.py

0460. Указатели

Питонизируемый класс может быть представлен указателем. Для этого питонизатор PyType должен наследовать от TypePointer, а его конструктор выглядит так:

PyType()
: TypeBase<pointer<MyClass> >("MyClass", NULL)
{
m_add_method<PyType, &PyType::say>("say", NULL);
}

, т. е. шаблон T имеет тип pointer<MyClass> и Python на каждый экземпляр класса MyClass в отличие от примера 0410 выделяет sizeof(MyClass *) байт. Методы constructor и destructor в данном примере имеют другую сигнатуру. constructor:

virtual void constructor(pointer<MyClass> &obj, Tuples &_args) const
{
/*... macros*/
 
new (&obj) pointer<MyClass> (new MyClass(""));
 
/*... macro*/
}

destructor по-прежнему чисто виртуальный а потому обязательный:

virtual void destructor(pointer<MyClass> &obj) const
{
delete &*obj;
obj.~pointer();
}

Собственно pointer<MyClass> по-прежнему инициализируется "placement new" конструктором

см TypePointer, pointer, example_0460.cpp, example_0460.py

0470. Синглетон

В отличие от предыдущего примера метод constructor не создает новый экземпляр класса MyClass, а возвращает указатель на его глобальную сущность _G_obj. destructor не делает ничего. В результате все экземпляры MyClass в Python-е указывают на один и тот же класс

см example_0470.cpp, example_0470.py

0480. "Умные" указатели

PyType явно наследует от TypeSmart, а благодаря последнему — и от TypeWrapper неявно. Шаблонные типы этих предков таковы:

TypeSmart TypeWrapper
shared_ptr<MyClass2> MyClass2

Соответственно:

  • TypeSmart питонизирует shared_ptr с конструктором и деструктором а также счетчиком ссылок use_count, причем одна из версий конструктора копирует аналогичный объект shared_ptr
  • TypeWrapper, как обычно питонизирует метод say

Функция from_shared является третьим шаблонным аргументом TypeSmart и "извлекает" из shared_ptr указатель на MyClass2. Код ниже создает один экземпляр класса MyClass2, затем дважды копирует его:

a = MyClass('1')
b = MyClass(a)
c = MyClass(b)
 
print a.cnt(), b.cnt(), c.cnt()
print sys.getrefcount(a), sys.getrefcount(b), sys.getrefcount(c)

в итоге:

3 3 3
2 2 2

Аналогично питонизируется weak_ptr и любой другой указатель. Назначение объекта argObject объясняется в примере 0550

см example_0480.cpp, example_0480.py

0490. Арифметические операции (Number Protocol)

PyType помимо TypeWrapper наследует от TypeNumber и из более чем 30 его виртуальных методов переопределяет всего 3:

В итоге несмотря на то, что MyClass имеет только строковое поле, его питонизированный аналог ведет себя как число — его можно складывать и "инвертировать":

m = MyClass('Hello')
m += MyClass(' world!')
m.say()
m = ~m
m.say()
Hello world!
!dlrow olleH

Все остальные методы имеют реализацию по умолчанию — NotImplementedError.

Attention.png В одних методах, наследуемых от TypeNumber и глубже от TypeNumberCommon первый аргумент представляет из себя ранее созданный объект MyClass (справедливо для всех inline_*-методов), в других — только что выделенную память для нового объекта, который необходимо сконструировать

см TypeNumber, TypeNumberCommon, example_0490.cpp, example_0490.py

0495. Арифметические операции над произвольными типами

В отличие от предыдущего примера PyType имплементирует Number Protocol наследуя от TypeNumberAny и самостоятельно проверяет и преобразовывает аргументы, входящие в арифметические выражения. Поэтому помимо себе подобных, теоретически может оперировать над любыми другими типами данных. Реализованы:

для типов int, str и собственно MyClass.

В арифметическом выражении по крайней мере один из операндов должен иметь тип MyClass, причем для inline методов он должен быть слева. Метод add несмотря на коммутативность операции сложения содержит 5 версий сигнатур:

  • MyClass + int
  • int + MyClass
  • MyClass + str
  • str + MyClass
  • MyClass + MyClass

а inplace_add — 3

Attention.png В одних методах, наследуемых от TypeNumber и глубже от TypeNumberCommon первый аргумент представляет из себя ранее созданный объект MyClass (справедливо для всех inline_*-методов), в других — только что выделенную память для нового объекта, который необходимо сконструировать

Назначение объекта argObject объясняется в примере 0550

см TypeNumberAny, TypeNumberCommon, example_0495.cpp, example_0495.py

0500. Последовательности (Sequence Protocol)

Питонизатор PyType наследует от TypeSequence и переопределяет те виртуальные методы предка, которые не используют аргумент Tuples. Последние будут рассмотрены в следующем примере. В качестве последовательности используются символы, населяющие строковое поле в MyClass. Операция:

s = MyClass("A") * 10

вызывает ошибку NotImplementedError потому что соответсвующий ей метод сознательно не переопределен и имеет имплементацию по умолчанию. Во всем остальном MyClass это 100%-я sequence, над которой можно подшутить:

s = MyClass(":-)")
s *= 1024
s.say()
Attention.png Первый аргумент в методах:
virtual void concat(const Ptr<T> &, const Ptr<const T> &, const Ptr<const T> &) const;
virtual void repeat(const Ptr<T> &, const Ptr<const T> &, Py_ssize_t) const;
virtual void get(const Ptr<T> &, const Ptr<const T> &, Py_ssize_t, Py_ssize_t) const;
virtual void concat(const Ptr<T> &, const Ptr<const T> &, Tuples &) const;

, наследуемых от TypeSequence требует вызова конструктора типа T

см TypeSequence, example_0500.cpp, example_0500.py

0505. Операции над последовательностями с произвольными типами

В отличие от примера 0500 переопределены методы, использующие аргумент Tuples:

virtual void concat(const Ptr<T> &, Tuples &) const;
virtual void concat(const Ptr<T> &, const Ptr<const T> &, Tuples &) const;
virtual void assign(const Ptr<T> &, Tuples &, Py_ssize_t, Py_ssize_t) const;
virtual void contains(bool &, const Ptr<const T> &, Tuples &) const;

Их имплементация в предке как раз вызывает методы из предыдущего примера при условии, что правый операнд имеет тот же тип. Имплементция этих методов в данном примере допускает помимо типа MyClass типы int и str.

Интерес представляют операции с диапазонами:

c = MyClass('С Новым Годом!')
c[13] = ' <?> '
c.say()
c[14:17] = 2014
c.say()
c[18:18] = '-м'
c.say()

Первый вызов -- замена 13-го элемента (нумерация с 0) на множество из 5 символов, второй — замена 3 символов с 14-го по 16-й включительно на число, которое при преобразовании в строку превращается 4 символа и последний — вставка в позицию 18 еще 2 символов без замены (если быть точным, то количество символов надо пересчитать в байты, это UTF-8). Результат таков:

С Новым <?> Годом!
С Новым 2014 Годом!
С Новым 2014-м Годом!
Attention.png Первый аргумент в методах:
virtual void concat(const Ptr<T> &, const Ptr<const T> &, const Ptr<const T> &) const;
virtual void repeat(const Ptr<T> &, const Ptr<const T> &, Py_ssize_t) const;
virtual void get(const Ptr<T> &, const Ptr<const T> &, Py_ssize_t, Py_ssize_t) const;
virtual void concat(const Ptr<T> &, const Ptr<const T> &, Tuples &) const;

, наследуемых от TypeSequence требует вызова конструктора типа T

Применение пространству имен disambiguate нашлось в примере 0910, назначение объекта argObject объясняется в примере 0550

см TypeSequence, example_0505.cpp, example_0505.py

0510. Ассоциативные массивы (Mapping Protocol)

Питонизируемым объектом является простейшая структурка с полем std::map:

struct myClass
{
typedef map<string, string> strings_t;
strings_t strings;
};

PyType наследует от TypeMap и публикует в Python-е данную структуру под именем MyClass. Интерфейс TypeMap состоит всего из 4-х методов с реализацией по умолчанию — NotImplementedError. Все они переопределены в PyType. Второй предок TypeWrapper используется для удобства — он реализует чисто виртуальные методы TypeBase в которых особой необходимости в данном примере нет.

Как ключи, так и зачения MyClass могут быть двух типов: int и str. Для того, чтобы выполнялся данный Python-код:

c = MyClass()
c[1] = "One"
c["Two"] = 2
c[3] = 3
c["Four"] = "Four"

, метод assign имеет 4 версии сигнатур (4 макроса PYHROL_PARSE_TUPLE_2). Исключение std::out_of_range, вызываемое в случае если ключ не найден при поиске или уже существует при вставке, преобразуется в IndexError

см TypeMap, example_0510.cpp, example_0510.py

0520. Итераторы (Iterator Protocol)

В примере питонизированы 3 концепции:

Имя в Python-е C++ тип Метод Описание
Контейнер MyClass std::string iter, создает новый итератор iter Питонизирован классом Container, наследующим от TypeIterable (см шаблоны TypeIterable ). Предметом хранения в нем являются символы (точнее байты), входящие в строку
Итератор iter Пара итераторов объединенная в структуру, [begin, end) next, инкрементирует существующий итератор, создает новый объект item Питонизирован классом Iterator, также наследующим от TypeIterable (см шаблоны TypeIterable )
Значение item const char Элемент контейнера

Мутное описание выше, воплощенное в полутора сотнях строк кода используется в Python-е очень и очень просто:

for d in MyClass("Yes!"):
print d.data
Y
e
s
!

В этом примере прояляется в действии механизм управления временем жизни объектов на основе счетчиков ссылок. iter не дает разрушить объект MyClass пока сам жив, а item не дает это сделать с объектом iter. Действительно, итераторы в iter будут недействительны, если подразумеваемый объект std::string будет уничтожен. Хотя item не зависит от прочих объектов (это всего навсего копия const char) она также управляет счетчиками. iter достает из Ptr и явным образом хранит указатель на свой контейнер в поле m_container типа PyObject, а item (для наглядности) использует для этого тривиальный класс AutoHolder

Применение метода allocate_static объясняется в примере 0560

см TypeIterable, счетчики ссылок, Ptr, AutoHolder, example_0520.cpp, example_0520.py

0540. Наследование

Рассматривается вопрос наследования питонизаторов в C++ с целью уменьшить объем кода когда питонизируемые классы наследуют друг от друга.

Вопрос наследования объектов в Python-е не рассматривается.

Питонизируемый MyClass2 наследует от MyClass добавляя метод reset. Казалось бы, достаточно сделать так:

class PyType: public TypeWrapper<MyClass>
{
/*pythonize and register method MyClass::say*/
}
 
class PyType2: public PyType
{
/*pythonize and register method MyClass2::reset*/
}

, но в этом случае у обоих питонизаторов PyType и PyType2 будет общий предок TypeBase с тем же шаблоном — MyClass, а это значит, что TypeBase::m_get вернет ошибку TypeInitException, см пример 0900

Правильное решение — создать общего предка PyTypeCommon:

template <class T> class PyTypeCommon: public TypeWrapper<T>
{
/*pythonize method T::say*/
};
 
class PyType: public PyTypeCommon<MyClass>
{
/*register method MyClass::say*/
};
 
class PyType2: public PyTypeCommon<MyClass2>
{
/*register method MyClass2::say
pythonize and register method MyClass2::reset
*/

};

В этом случае будут созданы по одному статическому экземпляру TypeBase<MyClass> и TypeBase<MyClass2>, экспортирующиму в Python объекты PyTypeObject для MyClass и MyClass2 соответственно.

Attention.png Несмотря на то, что PyType наследует от PyTypeCommon реализацию метода say, синтаксис:
m_add_method<PyType, &PyType::say>("say", NULL);

приводит к ошибке преобразования типов при инстанцировании шаблонов. Правильный синтаксис:

m_add_method<PyTypeCommon<MyClass>, &PyType::say>("say", NULL);

То же касается PyType2

см TypeBase, TypeInitException, example_0540.cpp, example_0540.py

0550. Аргументы объектного типа

По аналогии с примером 0090 используется формат O! для того чтобы гарантировать тип аргумента. Теперь в качестве аргумента может выступать другой экземпляр типа MyClass, тип MyString или стандартная C-строка. Как и в примере 0060 используется перегрузка.

Соль примера в методе PyType::set. Важнейший его фрагмент:

char *arg;
argObject as_me;
PyTypeString::argObject as_std_string;
 
PYHROL_PARSE_TUPLE_1(NULL, _args, arg)
PYHROL_PARSE_TUPLE_2(NULL, _args, as_me.cls, as_me.pobj)
PYHROL_PARSE_TUPLE_2(NULL, _args, as_std_string.cls, as_std_string.pobj)

Полученный в наследство от TypeBase объект argObject упрощает синтаксис и защищает от ошибок преобразования типов:

  • из as_me простым присвоением получается MyClass2:
*obj = as_me;
obj->set_message(as_std_string->c_str());

С учетом C-строки это все возможные типы аргументов метода set. Все прочие типы ожидаемо вызывают ошибку TypeError.

Класс argObject ранее использовался в примерах 0480, 0495, 0505

см example_0550.cpp, example_0550.py

0560. "Внешний" конструктор

Пример демонстрирует, как новый экземпляр класса MyClass может быть получен не вызовом конструктора, как обычно, а возвращаемым значением произвольной функции:

c = new_myclass('Hello world!')

MyClass умышленно лишен конструктора и создать его можно только вызовом new_myclass (или, что то же, new_myclass2).

Функция new_myclass:

  1. выделяет память для будущего объекта MyClass2; это структура T_struct<T>, член endosome которой как раз имеет тип MyClass2 и размер sizeof<MyClass2>; Python увидит ее как PyObject
  2. вызывает конструктор класса MyClass2, используя синтаксис "placement new"
  3. преобразовывает T_struct<T> к типу PyObject
  4. в случае исключения в операции 2) освобождает память и передает исключение выше; это видно при включенной трассировке.

Функция new_myclass2 аналогична new_myclass, только вместо статических методов TypeBase<T>::allocate_static и TypeBase<T>::free_static использует ссылку на PyType и их динамические аналоги.

Пример назван "внешним" конструктором, так как new_myclass не имеет отношения к классу MyClass (особенно с точки зрения Python-а). Используемые здесь методы ранее появлялись в примере 0520

см example_0560.cpp, example_0560.py

Прочее

0800. Модуль pyhrol

pyhrol.Container суть база метаданных, а также интерфейс для динамического управления настройками библиотеки pyhrol. Одно из полезнейших его свойств — обеспечивает доступ к справке по всем методам, включая те, которые не отображаются во встроенной справочной системе (например __add__, __iadd__):

for m in pyhrol.Methods():
if m.kind == 'specialCall':
print m.type.name, m.name
print m.doc

Данный пример показывает список перегруженных сигнатур в имплементированных методах TypeNumber из примера 0495.

Вероятно для целей отладки может понадобиться получить список всех классов, питонизированных с помощью pyhrol:

for t in Types():
if t.module.name != "pyhrol":
print t.module.name + '.' + t.name

см модуль pyhrol, example_0800.py

0810. Трассировка

Трассировка включает в себя сигнатуры промежуточных C++-функций, адреса объектов и произвольные сообщения. Должна помочь в случае нештатной ситуации. Управляется переменной окружения PYHROL_TRACE и/или методом Container.set_trace_options

В данном примере видно, в какой последовательности вызываются конструкторы, алокаторы и деструкторы в случае, когда успешно выполняется вся цепочка команд и когда на определенном этапе возникает ошибка. В совокупности количество вызовов:

Функция Количество
mediator_constructor 3
allocate_static 5
mediator_destructor 8

Можно опустить скучные подробности реализации, ибо интуитивно понятно, что mediator_constructor и allocate_static выделяют память при создании объекта, а mediator_destructor -- освобождает. Все по-честному, количество выделенной памяти совпадает с количеством освобожденной.

см PYHROL_TRACE, example_0810.txt, example_0810.py

0820. Форматирование справки

Используется функция из примера со справкой 0120 и класс MyClass версии 0495. Функция Container.set_help_options превращает справку в псевдографическую таблицу. Формат справки применяется динамически для всех загруженных модулей, написанных на pyhrol-е. В псевдографику превращается также справка по атрибутам __add__, __iadd__.

см Container.set_help_options, example_0820.py

0830. Версия с минимальным функционалом

Пример собирается с ключом PYHROL_SAFE_MODE:

cmake -DPYHROL_SAFE_MODE=True .
ctest -R 0830

Используется функция из примера со справкой 0120. Пример показывает, что в этом режиме справка не генерируется вообще. Не используется и зондирование — нехорошие примеры из раздела ниже уже не вызывают ошибок. Более того, такие методы как Container.set_help_format, Container.enable_signature_hack недоступны вообще.

Attention.png Крайне опасно использовать режим PYHROL_SAFE_MODE для модулей, вызывающих ошибку загрузки с включенным режимом зондирования. Те же самые ошибки, будучи вовремя не обезвреженными, могут проявить себя непредсказуемо

см Установка, PYHROL_SAFE_MODE, example_0830.py

0840. Перегрузка по имени аргумента

Возможность вызова разных сигнатур питонизируемых функций, используя разные имена параметров того же типа всплыла как неожиданный побочный эффект алгоритма перегрузки. Действительно, пример содержит четыре макроса PYHROL_PARSE_TUPLE_* три из которых отличаются только именем переменной, а стало быть и именем параметра в Python-е. Вызов с разными именами параметров приводит к разным значениям Tuples::parsed_variant.

Трудно понять, какую практическую пользу можно извлечь из данной особенности, скорее всего она больше вредна, чем полезна. Ее просто нужно иметь в виду.

см правила перегрузки, example_0840.cpp, example_0840.py

0850. Сокрытие методов

Экземпляр PyTypeObject, так или иначе наследуемый вместе с TypeBase находится во власти своего потомка, в данном примере DummyType. Если во всех предыдущих примерах конструкторы питонизирующих классов занимались регистрацией новых методов, то здесь указатели на все уже существующие методы сброшены. DummyType наследует сразу ото всех возможных потомков TypeBase, но MyClass, представляющий его в Python-е абсолютно голый — ни методов, ни атрибутов.

Отсутствие указателя на конструктор (tp_new = NULL) проявляется следующим образом при попытке создания объекта:

TypeError: cannot create 'example_0850.MyClass' instances

что отличается от примера 0400 меньшей информативностью, хотя это дело вкуса.

Таким образом можно удалить из интерфейса класса ненужные, нежелательные, нереализованные методы.

см example_0850.cpp, example_0850.py

Типичные ошибки

Так делать нельзя:

0900. Неверное наследование

Аналогичная примеру 0540 попытка питонизировать MyClass и его потомка MyClass2 избегая копирования кода для общих методов. Кажущаяся очевидной мысль унаследовать PyType2 от PyType приводит к печальным последствиям:

  • не удается зарегистрировать метод PyType2::reset, поскольку тип достается в наследство: T = MyClass; по ругательствам компилятора не сразу можно понять, в чем причина
  • у метода TypeBase::constructor в обоих классах один и тот же общий статический адаптер; то же справедливо и для остальных виртуальных методов но до проверки этого факта загрузка примера не доходит, потому что:
  • оба класса PyType и PyType2 имеют общего предка TypeBase<T>, где T = MyClass а следовательно будут на двоих использовать один и тот же объект PyTypeObject чего никак нельзя допустить, а посему:
terminate called after throwing an instance of 'pyhrol::TypeInitException'
  what():  Reinitialization of type "example_0900.MyClass"

— единственное адекватное решение.

Правильный способ наследования в такой ситуации, основанный на применении шаблонов, представлен в примере 0540.

см пример 0540, example_0900.cpp, example_0900.py

0910. Конфликты в таблице символов

Те же последствия и по той же причине, что и в примере 0900, только дублируются экземпляры TypeBase<MyClass2> из разных модулей:

import example_0490
import example_0495
terminate called after throwing an instance of 'pyhrol::TypeInitException'
  what():  Reinitialization of type "example_0490.MyClass"

Дело в том, что идентификатор статической переменной, представляющей объект PyTypeObject для типа T = MyClass2 в таблицах символов обоих модулей одинаков:

_ZZN6pyhrol8TypeBaseI8MyClass2E5m_getEPS2_E1t

несмотря на то, что классы MyClass2 абсолютно разные. Эта проблема известна как "конфликт имен". Судя по низкой освещенности в Сети, достаточно экзотична, лучшая ее демонстрация видимо здесь.

Решение достаточно простое — в примере 0505 MyClass2 заключен в простанство имен disambiguate, конфликт исчерпан и пара примеров 0500 и 0505 правильно загружаются и работают совместно.

см примеры 0490, 0495, 0500, 0505; example_0910.py

0920. Неверное использование макросов

В данноме примере намеренно допущены следующие ошибки:

Функция Описание Обнаружение
function_incorrect_order Неверный порядок следования макросов. PYHROL_PARSE_TUPLE_1 должен быть над PYHROL_AFTER_PARSE_TUPLE При загрузке модуля, если Container.is_signature_hack_enabled() = 1 либо при первом вызове
function_with_args Функция зарегистрирована как функция без аргументов, однако использует макрос PYHROL_PARSE_TUPLE_1 для их анализа
function_incorrect_input Функция вызывает Tuples::parsed_variant() хотя не имеет аргументов При первом вызове
function_incorrect_return Функция не возвращает значений, а указывает в PYHROL_AFTER_EXECUTE индекс 1, что соответсвует несуществующему второму варианту сигнатуры, см пример 0070
function_invalid_tuple Аргументы макроса PYHROL_PARSE_TUPLE_* формируют не полный формат O! — не хватает аргумента PyObject, см пример 0090 При первом вызове, однако ошибку можно увидеть в справке, если Container.is_signature_hack_enabled() = 1
function_non_unique_keywords Все три аргумента имеют одно и то же имя, что недопустимо, очевидно

Эти ошибки приводят в негодность функцию, в которой они допущены, поэтому ни одна из них не является фатальной для модуля в отличии от примера 0930.

Те же ошибки будут иметь те же прояления и в методах классов.

см example_0920.cpp, example_0920.py

0925. Неверное использование макросов в классах

За основу взят пример 0420 с той лишь разницей, что в методе PyType::say указатель Ptr намеренно разыменовывается над макросом PYHROL_AFTER_BUILD_VALUE, чего делать нельзя при включенном зондировании (Container.is_signature_hack_enabled() = 1). В результате ValueError:

ValueError: Null pointer (MyClass.say)

, и, как следствие, метод вызвать не удастся:

AttributeError: Tuple invalid due to one or more previous errors. Call impossible in any way (example_0925.MyClass.say)

см example_0925.cpp, example_0925.py

0926. Неверное использование макросов в классах (обход)

Пример 0925 прекрасно работает при выключенном зондировании (Container.is_signature_hack_enabled() = 0) или в режиме PYHROL_SAFE_MODE. Но это не повод нарушать правила.

см example_0926.py

0930. Неуникальность указателей

В примере всего две функции. Функции правильно реализованы, однако в обход макросов PYHROL_REGISTER_* зарегистрированы под другими именами:

  • function_A регистрируется дважды под именами A и B
  • под именем B регистрируются обе функции function_A и function_B

Из двух ошибок AttributeError:

AttributeError: Following calls share the same static wrapper {A, B}
AttributeError: Call name "B" non unique and used 2 times

первая является фатальной, т. к. угрожает всему модулю. В отличии от примера 0920 модуль не работоспособен, ни одна из его функций не может быть вызвана.

см example_0930.cpp, example_0930.py

0940. Недопустимые имена и символы

В Python-е определены ключевые слова и маски, которые либо нельзя, либо не рекомендуется использовать в качестве имен функций и методов. Поскольку язык C++ ничего об этом не знает, эти ограничения легко преодолеваются и обнаруживаются только в виде предупреждений при загрузке скомпилированного модуля:

AttributeError: Method name "_method" starts from underscore, therefore will be ignored by interpreter (MyClass._method)
AttributeError: Function name "_function" starts from underscore, therefore will be ignored by interpreter
AttributeError: Function name "lambda" invalid because it is language keyword

Ни одна из ошибок выше не является фатальной для модуля, а имеет следующие последствия:

  • метод MyClass._method можно вызвать, но он не показывается в справке
  • функцию _function можно вызвать только если импортировать модуль командой
import example_0940
example_0940._function()

она также не показывается в справке

  • функцию lambda не удается вызвать никак — всегда проиходит ошибка SyntaxError

см example_0940.cpp, example_0940.py

0950. std::bad_cast

За основу взят пример 0420 с той лишь разницей, что PyType использует закрытое наследование от TypeWrapper. В результате при попытке выполнить метод MyClass.say():

StandardError: std::bad_cast (example_0940.MyClass.method2)

Причина произошедшего кроется в следующем:

  • при вызове методов статические адаптеры, инстанцированные PyType-ом выполняют обратное преобразование: TypeBase<MyClass> -> PyType, а это возможно только в случае, если наследование открытое

В данном примере источником ошибки является следующая строка:

(dynamic_cast<O &>(TypeBase<T>::m_get()).*F)(Ptr<T>(pobj, self), args);

см example_0950.cpp, example_0950.py

0960. Закрытый конструктор копирования

Если копирующий конструктор питонизируемого класса закрытый, гарантирована следующая ошибка компиляции:


examples/example_0960.cpp:38:3: error: ‘MyClass2::MyClass2(const MyClass2&)’ is private
   MyClass2(const MyClass2 &cp);
   ^
In file included from include/pyhrol.h:43:0,
                 from examples/example_0960.cpp:30:
include/pyhrol_type.hpp:238:5: error: within this context
     new (&pstruct->endosome) T(obj);
     ^

Дело в том, что наследуемый метод convert выполняет копирование объекта MyClass2, а следовательно подразумевает у него наличие конструктора копирования. Ошибка происходит даже если этот метод не вызывается, и даже если он переопределяется потомком. Решением проблемы может быть переход к TypePointer как в примере 0460 или радикальный отказ от метода convert как такового.

В данном примере проблема решена явным инстанцированием метода convert без использования конструктора копирования. Имплементация этого метода могла быть и пустой, если бы не использовалась для демонстрации.

Работающий пример не может позволить себе роскошь вызвать ошибку компиляции, поэтому по умолчанию в нем работает упомянутый выше механизм обхода. Провоцируется ошибка макросом DISABLE_EXPLICIT_TEMPLATE_INSTANTIATION:

cmake -D DISABLE_EXPLICIT_TEMPLATE_INSTANTIATION:BOOL=ON .
make example_0960

см example_0960.cpp, example_0960.py

Файлы

example_0810.txt

к примеру 0810, к списку примеров

I    0 pyhrol::TupleDescriberFlexible::TupleDescriberFlexible()
=== Creating
M    1 static PyObject* pyhrol::TypeBase<T>::mediator_constructor(PyTypeObject*, PyObject*, PyObject*) [with T = pyhrol::types]
I    2 pyhrol::TupleDescriberFlexible::TupleDescriberFlexible()
M    1 static PyObject* pyhrol::TypeMap<T>::mediator_mp_subscript(PyObject*, PyObject*) [with T = pyhrol::types]
I    3 pyhrol::TupleBase::TupleBase(const char*, pyhrol::options, int8_t)
I    4 pyhrol::TupleBase::TupleBase(const char*, pyhrol::options, int8_t)
I    5 static pyhrol::TypeBase<T>::T_struct* pyhrol::TypeBase<T>::allocate_static() [with T = pyhrol::type]
I    6 pyhrol::TupleDescriberFlexible::TupleDescriberFlexible()
M    5 static PyObject* pyhrol::TypeWrapper<T, I>::m_getter(PyObject*, void*) [with O = pyhrol::Type, void (O::* G)(const pyhrol::Ptr<const T>&, pyhrol::Tuples&)const = &pyhrol::Type::get_methods, T = pyhrol::type, I = pyhrol::type]
I    7 pyhrol::TupleBase::TupleBase(const char*, pyhrol::options, int8_t)
I    8 static pyhrol::TypeBase<T>::T_struct* pyhrol::TypeBase<T>::allocate_static() [with T = pyhrol::methods]
I    9 pyhrol::TupleDescriberFlexible::TupleDescriberFlexible()
M    8 static PyObject* pyhrol::TypeMap<T>::mediator_mp_subscript(PyObject*, PyObject*) [with T = pyhrol::methods]
I   10 pyhrol::TupleBase::TupleBase(const char*, pyhrol::options, int8_t)
I   11 pyhrol::TupleBase::TupleBase(const char*, pyhrol::options, int8_t)
I   12 static pyhrol::TypeBase<T>::T_struct* pyhrol::TypeBase<T>::allocate_static() [with T = pyhrol::method]
I   13 pyhrol::TupleDescriberFlexible::TupleDescriberFlexible()
=== Destroying
M   12 static void pyhrol::TypeBase<T>::mediator_destructor(PyObject*) [with T = pyhrol::method]
M    8 static void pyhrol::TypeBase<T>::mediator_destructor(PyObject*) [with T = pyhrol::methods]
M    5 static void pyhrol::TypeBase<T>::mediator_destructor(PyObject*) [with T = pyhrol::type]
M    1 static void pyhrol::TypeBase<T>::mediator_destructor(PyObject*) [with T = pyhrol::types]
=== Creating (2)
M    1 static PyObject* pyhrol::TypeBase<T>::mediator_constructor(PyTypeObject*, PyObject*, PyObject*) [with T = pyhrol::types]
M    1 static PyObject* pyhrol::TypeMap<T>::mediator_mp_subscript(PyObject*, PyObject*) [with T = pyhrol::types]
I   12 static pyhrol::TypeBase<T>::T_struct* pyhrol::TypeBase<T>::allocate_static() [with T = pyhrol::type]
M   12 static PyObject* pyhrol::TypeWrapper<T, I>::m_getter(PyObject*, void*) [with O = pyhrol::Type, void (O::* G)(const pyhrol::Ptr<const T>&, pyhrol::Tuples&)const = &pyhrol::Type::get_methods, T = pyhrol::type, I = pyhrol::type]
I    8 static pyhrol::TypeBase<T>::T_struct* pyhrol::TypeBase<T>::allocate_static() [with T = pyhrol::methods]
M    8 static PyObject* pyhrol::TypeMap<T>::mediator_mp_subscript(PyObject*, PyObject*) [with T = pyhrol::methods]
M    8 static void pyhrol::TypeBase<T>::mediator_destructor(PyObject*) [with T = pyhrol::methods]
M   12 static void pyhrol::TypeBase<T>::mediator_destructor(PyObject*) [with T = pyhrol::type]
M    1 static void pyhrol::TypeBase<T>::mediator_destructor(PyObject*) [with T = pyhrol::types]
None
=== Creating (3)
M    1 static PyObject* pyhrol::TypeBase<T>::mediator_constructor(PyTypeObject*, PyObject*, PyObject*) [with T = pyhrol::types]
M    1 static PyObject* pyhrol::TypeMap<T>::mediator_mp_subscript(PyObject*, PyObject*) [with T = pyhrol::types]
M    1 static void pyhrol::TypeBase<T>::mediator_destructor(PyObject*) [with T = pyhrol::types]
None
M      static PyObject* pyhrol::TypeSpecial<T>::m_static_method3(PyObject*, PyObject*, PyObject*) [with void (* F)(pyhrol::Tuples&) = pyhrol::ContainerObject::set_trace_options, T = pyhrol::pointer<pyhrol::ContainerObject>]

к примеру 0810, к списку примеров

example_0820.txt

к примеру 0820, к списку примеров

Фрагмент:

...

help_generation_example(...)
    Automatic help generation on function arguments
    
      -> <s#|id>, "tuple with per argument help:"
           +------------------------------+----+--------------------+---------------+------------------------------+
           |C++ signature                 |fmt |Name                |Value          |Description                   |
           +------------------------------+----+--------------------+---------------+------------------------------+
           |char */*not null*/, int32_t   |si  |str                 |               |sample string                 |
           |int32_t                       |i   |i                   |12             |integer value with default    |
           |double                        |d   |f                   |3.14           |float value with default      |
    
      -> <ids#>, "tuple with brief help
    ...and addition"
           +------------------------------+----+--------------------+---------------+------------------------------+
           |C++ signature                 |fmt |Name                |Value          |Description                   |
           +------------------------------+----+--------------------+---------------+------------------------------+
           |int32_t                       |i   |i                   |               |                              |
           |double                        |d   |f                   |               |                              |
           |char */*not null*/, int32_t   |si  |str                 |               |                              |
    
    
      <- <idis>, ""
           +------------------------------+----+--------------------+---------------+------------------------------+
           |C++ signature                 |fmt |Name                |Value          |Description                   |
           +------------------------------+----+--------------------+---------------+------------------------------+
           |int32_t                       |i   |                    |               |help on res1                  |
           |double                        |d   |                    |               |help on res2                  |
           |int32_t                       |i   |                    |               |help on res3                  |
           |char */*nullable*/            |s   |                    |               |help on res4                  |

...

<nb_inplace_add>

-> <i>, "MyClass += int"
       +------------------------------+----+--------------------+---------------+------------------------------+
       |C++ signature                 |fmt |Name                |Value          |Description                   |
       +------------------------------+----+--------------------+---------------+------------------------------+
       |int32_t                       |i   |                    |               |int                           |

-> <s>, "MyClass += str"
       +------------------------------+----+--------------------+---------------+------------------------------+
       |C++ signature                 |fmt |Name                |Value          |Description                   |
       +------------------------------+----+--------------------+---------------+------------------------------+
       |char */*not null*/            |s   |                    |               |str                           |

-> <O!>, "MyClass += MyClass"
       +------------------------------+----+--------------------+---------------+------------------------------+
       |C++ signature                 |fmt |Name                |Value          |Description                   |
       +------------------------------+----+--------------------+---------------+------------------------------+
       |PyTypeObject, $MyClass        |!O  |                    |               |MyClass                       |

...

к примеру 0820, к списку примеров

Пространства имён

Варианты
Просмотры
Действия
Навигация