Code Injection Semantics¶
API Extractor provides the inject-code tag allowing the user to put custom written code to on specific locations of the generated code. Yet this is only part of what is needed to generate proper binding code, where the custom code should be written to depends upon the technology used on the generated binding code.
This is the inject-code
tag options that matters to Shiboken.
<inject-code class="native | target" position="beginning | end"> // custom code </inject-code>
inject-code tag¶
The following table describes the semantics of inject-code tag as used on
Shiboken. The class
attribute specifies whether to code is injected
into the C++ Wrapper or the Python Wrapper (see
Code Generation Terminology).
The position
attribute specifies the location of the custom code in the
function.
Parent Tag
Class
Position
Meaning
value-type, object-type
native
beginning
Write to the beginning of a class wrapper
.cpp
file, right after the#include
clauses. A common use would be to write prototypes for custom functions whose definitions are put on anative/end
code injection.end
Write to the end of a class wrapper
.cpp
file. Could be used to write custom/helper functions definitions for prototypes declared onnative/beginning
.target
beginning
Put custom code on the beginning of the wrapper initializer function (
init_CLASS(PyObject *module)
). This could be used to manipulate thePyCLASS_Type
structure before registering it on Python.end
Write the given custom code at the end of the class wrapper initializer function (
init_CLASS(PyObject *module)
). The code here will be executed after all the wrapped class components have been initialized.modify-function
native
beginning
Code here is put on the virtual method override of a C++ wrapper class (the one responsible for passing C++ calls to a Python override, if there is any), right after the C++ arguments have been converted but before the Python call.
end
This code injection is put in a virtual method override on the C++ wrapper class, after the call to Python and before dereferencing the Python method and tuple of arguments.
target
beginning
This code is injected on the Python method wrapper (
PyCLASS_METHOD(...)
), right after the decisor have found which signature to call and also after the conversion of the arguments to be used, but before the actual call.end
This code is injected on the Python method wrapper (
PyCLASS_METHOD(...)
), right after the C++ method call, but still inside the scope created by the overload for each signature.shell
declaration
Used only for virtual functions. This code is injected at the top.
beginning
Used only for virtual functions. The code is injected when the function does not has a Python implementation, then the code is inserted before c++ call
end
Same as above, but the code is inserted after c++ call
typesystem
native
beginning
Write code to the beginning of the module
.cpp
file, right after the#include
clauses. This position has a similar purpose as thenative/beginning
position on a wrapper class.cpp
file, namely write function prototypes, but not restricted to this use.end
Write code to the end of the module
.cpp
file. Usually implementations for function prototypes inserted at the beginning of the file with anative/beginning
code injection.target
beginning
Insert code at the start of the module initialization function (
initMODULENAME()
), before the callingPy_InitModule
.end
Insert code at the end of the module initialization function (
initMODULENAME()
), but before the checking that emits a fatal error in case of problems importing the module.declaration
Insert code into module header.
Anatomy of Code Injection¶
To make things clear let’s use a simplified example of generated wrapper code and the places where each kind of code injection goes.
Below is the example C++ class for whom wrapper code will be generated.
class InjectCode { public: InjectCode(); double overloadedMethod(int arg); double overloadedMethod(double arg); virtual int virtualMethod(int arg); };
From the C++ class, Shiboken will generate a injectcode_wrapper.cpp
file
with the binding code. The next section will use a simplified version of the
generated wrapper code with the injection spots marked with comments.
There are a number of placeholders indicated by a percent sign %
, which
will be expanded when inserting the code. For a list, see
Type System Variables.
Noteworthy Cases¶
The type system description system gives the binding developer a lot of flexibility, which is power, which comes with responsibility. Some modifications to the wrapped API will not be complete without some code injection.
Removing arguments and setting a default values for them¶
A simple case is when a function have one argument removed, as when the C++
method METHOD(ARG)
is modified to be used from Python as METHOD()
;
of course the binding developer must provide some guidelines to the generator
on what to do to call it. The most common solution is to remove the argument and
set a default value for it at the same time, so the original C++ method could be
called without problems.
Removing arguments and calling the method with your own hands¶
If the argument is removed and no default value is provided, the generator will
not write any call to the method and expect the modify-function - target/beginning
code injection to call the original C++ method on its own terms. If even this
custom code is not provided the generator will put an #error
clause to
prevent compilation of erroneous binding code.
Calling the method with your own hands always!¶
If your custom code to be injected contains a call to the wrapped C++ method,
it surely means that you don’t want the generator to write another call to the
same method. As expected Shiboken will detect the user written call on the code
injection and will not write its own call, but for this to work properly the
binding developer must use the template variable %FUNCTION_NAME
instead
of writing the actual name of the wrapped method/function.
In other words, use
<inject-code class="target" position="beginning | end"> %CPPSELF.originalMethodName(); </inject-code>
instead of
<inject-code class="target" position="beginning | end"> %CPPSELF.%FUNCTION_NAME(); </inject-code>
Code Injection for Functions/Methods¶
On The Native Side¶
Notice that this is only used when there is a C++ wrapper, i.e. the wrapped class is polymorphic.
int InjectCodeWrapper::virtualMethod(int arg) { PyObject *method = BindingManager::instance().getOverride(this, "virtualMethod"); if (!py_override) return this->InjectCode::virtualMethod(arg); (... here C++ arguments are converted to Python ...) // INJECT-CODE: <modify-function><inject-code class="native" position="beginning"> // Uses: pre method call custom code, modify the argument before the // Python call. (... Python method call goes in here ...) // INJECT-CODE: <modify-function><inject-code class="native" position="end"> // Uses: post method call custom code, modify the result before delivering // it to C++ caller. (... Python method and argument tuple are dereferenced here ...) return Shiboken::Converter<int>::toCpp(method_result); }
On The Target Side¶
All the overloads of a method from C++ are gathered together on a single Python
method that uses an overload decisor to call the correct C++ method based on the
arguments passed by the Python call. Each overloaded method signature has its
own beginning
and end
code injections.
static PyObject *PyInjectCode_overloadedMethod(PyObject *self, PyObject *arg) { PyObject* py_result{}; if (PyFloat_Check(arg)) { double cpp_arg0 = Shiboken::Converter<double >::toCpp(arg); // INJECT-CODE: <modify-function><inject-code class="target" position="beginning"> // Uses: pre method call custom code. py_result = Shiboken::Converter<double >::toPython( PyInjectCode_cptr(self)->InjectCode::overloadedMethod(cpp_arg0) ); // INJECT-CODE: <modify-function><inject-code class="target" position="end"> // Uses: post method call custom code. } else if (PyNumber_Check(arg)) { (... other overload calling code ...) } else goto PyInjectCode_overloadedMethod_TypeError; if (PyErr_Occurred() || !py_result) return {}; return py_result; PyInjectCode_overloadedMethod_TypeError: PyErr_SetString(PyExc_TypeError, "'overloadedMethod()' called with wrong parameters."); return {}; }
Code Injection for Wrapped Classes¶
On The Native Side¶
Those injections go in the body of the CLASSNAME_wrapper.cpp
file for the
wrapped class.
// Start of ``CLASSNAME_wrapper.cpp`` #define protected public // default includes #include <shiboken.h> (...) #include "injectcode_wrapper.h" using namespace Shiboken; // INJECT-CODE: <value/object-type><inject-code class="native" position="beginning"> // Uses: prototype declarations (... C++ wrapper virtual methods, if any ...) (... Python wrapper code ...) PyAPI_FUNC(void) init_injectcode(PyObject *module) { (...) } (...) // INJECT-CODE: <value/object-type><inject-code class="native" position="end"> // Uses: definition of functions prototyped at ``native/beginning``. // End of ``CLASSNAME_wrapper.cpp``
On The Target Side¶
Code injections to the class Python initialization function.
// Start of ``CLASSNAME_wrapper.cpp`` (...) PyAPI_FUNC(void) init_injectcode(PyObject *module) { // INJECT-CODE: <value/object-type><inject-code class="target" position="beginning"> // Uses: Alter something in the PyInjectCode_Type (tp_flags value for example) // before registering it. if (PyType_Ready(&PyInjectCode_Type) < 0) return; Py_INCREF(&PyInjectCode_Type); PyModule_AddObject(module, "InjectCode", ((PyObject*)&PyInjectCode_Type)); // INJECT-CODE: <value/object-type><inject-code class="target" position="end"> // Uses: do something right after the class is registered, like set some static // variable injected on this same file elsewhere. } (...) // End of ``CLASSNAME_wrapper.cpp``
Code Injection for Modules¶
The C++ libraries are wrapped as Python modules, a collection of classes,
functions, enums and namespaces. Shiboken creates wrapper files for all of
them and also one extra MODULENAME_module_wrapper.cpp
to register the whole
module. Code injection xml tags who have the typesystem
tag as parent will
be put on this file.
On The Native Side¶
This works exactly as the class wrapper code injections On The Native Side.
On The Target Side¶
This is very similar to class wrapper code injections On The Target Side.
Notice that the inject code at target/end
is inserted before the check for errors
to prevent bad custom code to pass unnoticed.
// Start of ``MODULENAME_module_wrapper.cpp`` (...) initMODULENAME() { // INJECT-CODE: <typesystem><inject-code class="target" position="beginning"> // Uses: do something before the module is created. PyObject *module = Py_InitModule("MODULENAME", MODULENAME_methods); (... initialization of wrapped classes, namespaces, functions and enums ...) // INJECT-CODE: <typesystem><inject-code class="target" position="end"> // Uses: do something after the module is registered and initialized. if (PyErr_Occurred()) Py_FatalError("can't initialize module sample"); } (...) // Start of ``MODULENAME_module_wrapper.cpp``
In addition, code can be injected into the module header by specifying target
and declaration
. This is useful for type definitions.
© 2022 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.