aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken2/doc/typeconverters.rst
blob: 872daa18712f700a25534c31ddd4c6829bffdf27 (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
****************************
User Defined Type Conversion
****************************

In the process of creating Python bindings of a C++ library, most of the C++
classes will have wrappers representing them in Python land.
But there may be other classes that are very simple and/or have a Python type
as a direct counter part. (Example: a "Complex" class, that represents complex
numbers, has a Python equivalent in the "complex" type.) Such classes, instead
of getting a Python wrapper, normally have conversions rules, from Python to
C++ and vice-versa.

      .. code-block:: c++

          // C++ class
          struct Complex {
              Complex(double real, double imag);
              double real() const;
              double imag() const;
          };

          // Converting from C++ to Python using the CPython API:
          PyObject* pyCpxObj = PyComplex_FromDoubles(complex.real(), complex.imag());

          // Converting from Python to C++:
          double real = PyComplex_RealAsDouble(pyCpxObj);
          double imag = PyComplex_ImagAsDouble(pyCpxObj);
          Complex cpx(real, imag);


For the user defined conversion code to be inserted in the proper places,
the "<conversion-rule>" tag must be used.

      .. code-block:: xml

        <primitive-type name="Complex" target-lang-api-name="PyComplex">
          <include file-name="complex.h" location="global"/>

          <conversion-rule>

            <native-to-target>
            return PyComplex_FromDoubles(%in.real(), %in.imag());
            </native-to-target>

            <target-to-native>
              <!-- The 'check' attribute can be derived from the 'type' attribute,
                   it is defined here to test the CHECKTYPE type system variable. -->
              <add-conversion type="PyComplex" check="%CHECKTYPE[Complex](%in)">
              double real = PyComplex_RealAsDouble(%in);
              double imag = PyComplex_ImagAsDouble(%in);
              %out = %OUTTYPE(real, imag);
              </add-conversion>
            </target-to-native>

          </conversion-rule>

        </primitive-type>


The details will be given later, but the gist of it are the tags
:ref:`native-to-target <native-to-target>`, which has only one conversion from C++ to Python, and
:ref:`native-to-native <target-to-native>`, that may define the conversion of multiple Python types
to C++'s "Complex" type.

.. image:: images/converter.png
    :height: 240px
    :align: center

|project| expects the code for :ref:`native-to-target <native-to-target>`, to directly return the
Python result of the conversion, and the added conversions inside the
:ref:`target-to-native <target-to-native>` must attribute the Python to C++ conversion result to
the :ref:`%out <out>` variable.

Expanding on the last example, if the binding developer want a Python 2-tuple
of numbers to be accepted by wrapped C++ functions with "Complex" arguments,
an :ref:`add-conversion <add-conversion>` tag and a custom check must be added.
Here's how to do it:

      .. code-block:: xml

        <!-- Code injection at module level. -->
        <inject-code class="native" position="beginning">
        static bool Check2TupleOfNumbers(PyObject* pyIn) {
            if (!PySequence_Check(pyIn) || !(PySequence_Size(pyIn) == 2))
                return false;
            Shiboken::AutoDecRef pyReal(PySequence_GetItem(pyIn, 0));
            if (!SbkNumber_Check(pyReal))
                return false;
            Shiboken::AutoDecRef pyImag(PySequence_GetItem(pyIn, 1));
            if (!SbkNumber_Check(pyImag))
                return false;
            return true;
        }
        </inject-code>

        <primitive-type name="Complex" target-lang-api-name="PyComplex">
          <include file-name="complex.h" location="global"/>

          <conversion-rule>

            <native-to-target>
            return PyComplex_FromDoubles(%in.real(), %in.imag());
            </native-to-target>

            <target-to-native>

              <add-conversion type="PyComplex">
              double real = PyComplex_RealAsDouble(%in);
              double imag = PyComplex_ImagAsDouble(%in);
              %out = %OUTTYPE(real, imag);
              </add-conversion>

              <add-conversion type="PySequence" check="Check2TupleOfNumbers(%in)">
              Shiboken::AutoDecRef pyReal(PySequence_GetItem(%in, 0));
              Shiboken::AutoDecRef pyImag(PySequence_GetItem(%in, 1));
              double real = %CONVERTTOCPP[double](pyReal);
              double imag  = %CONVERTTOCPP[double](pyImag);
              %out = %OUTTYPE(real, imag);
              </add-conversion>

            </target-to-native>

          </conversion-rule>

        </primitive-type>


.. _container_conversions:

Container Conversions
=====================

Converters for :ref:`container-type <container-type>` are pretty much the same as for other type,
except that they make use of the type system variables
:ref:`%INTYPE_# <intype_n>` and :ref:`%OUTTYPE_# <outtype_n>`.
|project| combines the conversion code for containers with the conversion
defined (or automatically generated) for the containers.

      .. code-block:: xml

            <container-type name="std::map" type="map">
              <include file-name="map" location="global"/>

              <conversion-rule>

                <native-to-target>
                PyObject* %out = PyDict_New();
                %INTYPE::const_iterator it = %in.begin();
                for (; it != %in.end(); ++it) {
                  %INTYPE_0 key = it->first;
                  %INTYPE_1 value = it->second;
                          PyDict_SetItem(%out,
                                 %CONVERTTOPYTHON[%INTYPE_0](key),
                         %CONVERTTOPYTHON[%INTYPE_1](value));
                }
                return %out;
                </native-to-target>

                <target-to-native>

                  <add-conversion type="PyDict">
                  PyObject* key;
                  PyObject* value;
                  Py_ssize_t pos = 0;
                  while (PyDict_Next(%in, &amp;pos, &amp;key, &amp;value)) {
                      %OUTTYPE_0 cppKey = %CONVERTTOCPP[%OUTTYPE_0](key);
                      %OUTTYPE_1 cppValue = %CONVERTTOCPP[%OUTTYPE_1](value);
                      %out.insert(%OUTTYPE::value_type(cppKey, cppValue));
                  }
                  </add-conversion>

                </target-to-native>
              </conversion-rule>
            </container-type>


.. _variables_and_functions:

Variables & Functions
=====================


.. _in:

**%in**

  Variable replaced by the C++ input variable.


.. _out:

**%out**

  Variable replaced by the C++ output variable. Needed to convey the
  result of a Python to C++ conversion.


.. _intype:

**%INTYPE**

  Used in Python to C++ conversions. It is replaced by the name of type for
  which the conversion is being defined. Don't use the type's name directly.


.. _intype_n:

**%INTYPE_#**

  Replaced by the name of the #th type used in a container.


.. _outtype:

**%OUTTYPE**

  Used in Python to C++ conversions. It is replaced by the name of type for
  which the conversion is being defined. Don't use the type's name directly.


.. _outtype_n:

**%OUTTYPE_#**

  Replaced by the name of the #th type used in a container.


.. _checktype:

**%CHECKTYPE[CPPTYPE]**

  Replaced by a |project| type checking function for a Python variable.
  The C++ type is indicated by ``CPPTYPE``.


.. _oldconverters:

Converting The Old Converters
=============================

If you use |project| for your bindings, and has defined some type conversions
using the ``Shiboken::Converter`` template, then you must update your converters
to the new scheme.

Previously your conversion rules were declared in one line, like this:


    .. code-block:: xml

        <primitive-type name="Complex" target-lang-api-name="PyComplex">
          <include file-name="complex.h" location="global"/>
          <conversion-rule file="complex_conversions.h"/>
        </primitive-type>


And implemented in a separate C++ file, like this:


    .. code-block:: c++

        namespace Shiboken {
        template<> struct Converter<Complex>
        {
            static inline bool checkType(PyObject* pyObj) {
                return PyComplex_Check(pyObj);
            }
            static inline bool isConvertible(PyObject* pyObj) {
                return PyComplex_Check(pyObj);
            }
            static inline PyObject* toPython(void* cppobj) {
                return toPython(*reinterpret_cast<Complex*>(cppobj));
            }
            static inline PyObject* toPython(const Complex& cpx) {
                return PyComplex_FromDoubles(cpx.real(), cpx.imag());
            }
            static inline Complex toCpp(PyObject* pyobj) {
                double real =  PyComplex_RealAsDouble(pyobj);
                double imag =  PyComplex_ImagAsDouble(pyobj);
                return Complex(real, imag);
            }
        };
        }


In this case, the parts of the implementation that will be used in the new
conversion-rule are the ones in the two last method
``static inline PyObject* toPython(const Complex& cpx)`` and
``static inline Complex toCpp(PyObject* pyobj)``. The ``isConvertible`` method
is gone, and the ``checkType`` is now an attribute of the :ref:`add-conversion <add-conversion>`
tag. Refer back to the first example in this page and you will be able to
correlate the above template with the new scheme of conversion rule definition.