aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken6/doc/typesystem_converters.rst
blob: ab6fba93099394e78a5eda94af78c79c9148ca3e (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
.. _user-defined-type-conversion:

****************************
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 :ref:`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 (!PyNumber_Check(pyReal))
          return false;
      Shiboken::AutoDecRef pyImag(PySequence_GetItem(pyIn, 1));
      if (!PyNumber_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>

.. note:: The C++ containers ``std::list``\, ``std::vector``\,
          ``std::pair``\, ``std::map``\, ``std::span`` and ``std::unordered_map`` are
          built-in.
          To specify :ref:`opaque-containers`, use the :ref:`opaque-container` element.
          :ref:`container-type` can still be specified to modify the built-in behavior.
          For this case, a number of pre-defined conversion templates
          are provided (see :ref:`predefined_templates`).

.. _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``.