aboutsummaryrefslogtreecommitdiffstats
path: root/doc
diff options
context:
space:
mode:
authorMarcelo Lira <marcelo.lira@openbossa.org>2009-11-13 12:35:44 -0300
committerMarcelo Lira <marcelo.lira@openbossa.org>2009-11-13 14:00:21 -0300
commit8f779f4dbdd3b62891ffd61e8a441f7108ea4cc7 (patch)
tree78c23fbdbdf68be7f10b388caf284a5a20de9566 /doc
parent4dd08dd079e7e56c7dcc0f8d3d89d1c8d78073d5 (diff)
added complete documentation on how the Shiboken generator interprets
and uses the type system's code-injection tag Reviewed by Lauro Neto <lauro.neto@openbossa.org>
Diffstat (limited to 'doc')
-rw-r--r--doc/_templates/index.html2
-rw-r--r--doc/codeinjectionsemantics.rst347
-rw-r--r--doc/contents.rst1
3 files changed, 350 insertions, 0 deletions
diff --git a/doc/_templates/index.html b/doc/_templates/index.html
index 8492c3ae5..ef2e9a610 100644
--- a/doc/_templates/index.html
+++ b/doc/_templates/index.html
@@ -11,6 +11,8 @@
<td width="50%">
<p class="biglink"><a class="biglink" href="{{ pathto("contents") }}">Contents</a><br/>
<span class="linkdescr">for a complete overview</span></p>
+ <p class="biglink"><a class="biglink" href="{{ pathto("codeinjectionsemantics") }}">Code Injection Semantics</a><br/>
+ <span class="linkdescr">explains how custom code injection is interpreted by {{ project }}</span></p>
<p class="biglink"><a class="biglink" href="{{ pathto("sequenceprotocol") }}">Sequence Protocol</a><br/>
<span class="linkdescr">support for python sequence protocol</span></p>
</td>
diff --git a/doc/codeinjectionsemantics.rst b/doc/codeinjectionsemantics.rst
new file mode 100644
index 000000000..15ba426ca
--- /dev/null
+++ b/doc/codeinjectionsemantics.rst
@@ -0,0 +1,347 @@
+************************
+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 beginning of a virtual method |
+ | | | |override on the C++ wrapper class (the one responsible for |
+ | | | |passing C++ calls to Python overrides, if there is any). |
+ | | +---------+--------------------------------------------------------------+
+ | | |end |This code injection goes to the end of a virtual method |
+ | | | |override on the C++ wrapper class, right before the return |
+ | | | |statement (if any). |
+ | +------+---------+--------------------------------------------------------------+
+ | |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. |
+ +---------------+------+---------+--------------------------------------------------------------+
+ |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 its
+contents with the injection spots marked.
+
+Noteworthy Situations
+---------------------
+
+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.
+
+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.
+
+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.
+
+
+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.
+
+ .. code-block:: c++
+
+ int InjectCodeWrapper::virtualMethod(int arg)
+ {
+ PyObject* method = BindingManager::instance().getOverride(this, "virtualMethod");
+ if (!method)
+ return this->InjectCode::virtualMethod(arg);
+
+ // INJECT-CODE: <modify-function><inject-code class="native" position="beginning">
+ // Uses: pre method call custom code.
+
+ (... 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.
+
+ 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``
+
diff --git a/doc/contents.rst b/doc/contents.rst
index 057191204..4e0a5e9e5 100644
--- a/doc/contents.rst
+++ b/doc/contents.rst
@@ -5,5 +5,6 @@ Table of contents
:maxdepth: 3
faq.rst
+ codeinjectionsemantics.rst
sequenceprotocol.rst
compiling.rst