aboutsummaryrefslogtreecommitdiffstats
path: root/doc/codeinjectionsemantics.rst
blob: d748a3aae527d02f1ba5a6dca64a7ba0ed4b3716 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
************************
Code Injection Semantics
************************

API Extractor provides the `inject-code
<http://www.pyside.org/docs/apiextractor/typesystem_manipulating_objects.html#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 |project|.

    .. code-block:: xml

         <inject-code class="native | target" position="beginning | end">
             // custom code
         </inject-code>

Conventions
===========

**C++ Wrapper**
  This term refers to a generated C++ class that extends a class from the
  wrapped library. It is used only when a wrapped C++ class is polymorphic,
  i.e. it has or inherits any virtual methods.

**Python Wrapper**
  The code that exports the C++ wrapped class to Python. **Python wrapper**
  refers to all the code needed to export a C++ class to Python, and
  **Python method/function wrapper** means the specific function that calls
  the C++ method/function on behalf of Python.

**Native**
  This is a possible value for the ``class`` attribute of the ``inject-code``
  tag, it means things more akin to the C++ side.

**Target**
 Another ``class`` attribute value, it indicates things more close to the
 Python side.

inject-code tag
===============

The following table describes the semantics of ``inject-code`` tag as used on
|project|.

    +---------------+------+---------+--------------------------------------------------------------+
    |Parent Tag     |Class |Position |Meaning                                                       |
    +===============+======+=========+==============================================================+
    |value-type,    |native|beginning|Write to the beginning of a class wrapper ``.cpp`` file, right|
    |object-type    |      |         |after the ``#include`` clauses. A common use would be to write|
    |               |      |         |prototypes for custom functions whose definitions are put on a|
    |               |      |         |``native/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 on ``native/beginning``.                  |
    |               +------+---------+--------------------------------------------------------------+
    |               |target|beginning|Put custom code on the beginning of the wrapper initializer   |
    |               |      |         |function (``init_CLASS(PyObject *module)``). This could be    |
    |               |      |         |used to manipulate the ``PyCLASS_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 |beginning|Used only for virtual functions. The code is injected when the|
    |               |      |         |function does not has a pyhton 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 the ``native/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 a ``native/beginning`` code        |
    |               |      |         |injection.                                                    |
    |               +------+---------+--------------------------------------------------------------+
    |               |target|beginning|Insert code at the start of the module initialization function|
    |               |      |         |(``initMODULENAME()``), before the calling ``Py_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.         |
    +---------------+------+---------+--------------------------------------------------------------+


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.

    .. code-block:: c++

        class InjectCode {
        public:
            InjectCode();
            double overloadedMethod(int arg);
            double overloadedMethod(double arg);
            virtual int virtualMethod(int arg);
        };

From the C++ class, |project| 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.

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 erroneus 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 |project| 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

    .. code-block:: xml

         <inject-code class="target" position="beginning | end">
             %CPPSELF.originalMethodName();
         </inject-code>


instead of


    .. code-block:: xml

         <inject-code class="target" position="beginning | end">
            %CPPSELF.%FUNCTION_NAME();
         </inject-code>


Code Injection for Functions/Methods
====================================


.. _codeinjecting_method_native:

On The Native Side
------------------

Notice that this is only used when there is a C++ wrapper, i.e. the wrapped
class is polymorphic.

    .. code-block:: c++

        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.

    .. code-block:: c++

            static PyObject*
            PyInjectCode_overloadedMethod(PyObject* self, PyObject* arg)
            {
                PyObject* py_result = 0;
                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 0;

                return py_result;

                PyInjectCode_overloadedMethod_TypeError:
                    PyErr_SetString(PyExc_TypeError, "'overloadedMethod()' called with wrong parameters.");
                    return 0;
            }


.. _codeinjecting_classes:

Code Injection for Wrapped Classes
==================================

.. _codeinjecting_classes_native:

On The Native Side
------------------

Those injections go in the body of the ``CLASSNAME_wrapper.cpp`` file for the
wrapped class.

    .. code-block:: c++

        // 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``


.. _codeinjecting_classes_target:

On The Target Side
------------------

Code injections to the class Python initialization function.

    .. code-block:: c++

        // 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 wapped as Python modules, a collection of classes,
functions, enums and namespaces. |project| 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 :ref:`codeinjecting_classes_native`.

On The Target Side
------------------

This is very similar to class wrapper code injections :ref:`codeinjecting_classes_target`.
Notice that the inject code at ``target/end`` is inserted before the check for errors
to prevent bad custom code to pass unnoticed.

    .. code-block:: c++

        // 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``