PySide Shiboken Type Converters
[toc align_right="yes" depth="2"]
PySide Shiboken Type Converters
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.
<br />// C++ class<br />struct Complex {<br /> Complex(double real, double imag);<br /> double real() const;<br /> double imag() const;<br />};
// Converting from C++ to Python using the CPython API:<br />PyObject* pyCpxObj = PyComplex_FromDoubles(complex.real(), complex.imag());
// Converting from Python to C+'':<br />double real = PyComplex_RealAsDouble(pyCpxObj);<br />double imag = PyComplex_ImagAsDouble(pyCpxObj);<br />Complex cpx(real, imag);<br />
For the user defined conversion code to be inserted in the proper places, the “<conversion-rule>” tag must be used.
<br /><primitive-type name="Complex&quot; target-lang-api-name="PyComplex&quot;><br /> <include file-name="complex.h&quot; location="global&quot;/&gt;
<br /> <conversion-rule&gt;
<br /> <native-to-target&gt;<br /> return PyComplex_FromDoubles(%in.real(), %in.imag());<br /> </native-to-target&gt;
<br /> <target-to-native&gt;<br /> &lt;!— The 'check' attribute can be derived from the 'type' attribute,<br /> it is defined here to test the CHECKTYPE type system variable. —&gt;<br /> <add-conversion type="PyComplex&quot; check="%CHECKTYPE[Complex](%in)"><br /> double real = PyComplex_RealAsDouble(%in);<br /> double imag = PyComplex_ImagAsDouble(%in);<br /> %out = %OUTTYPE (real, imag);<br /> </add-conversion&gt;<br /> </target-to-native&gt;
<br /> </conversion-rule&gt;
<br /></primitive-type&gt;<br />
The details will be given later, but the gist of it are the tags <native-to-target>, which has only one conversion from C+ to Python, and <target-to-native>, that may define the conversion of multiple Python types to C+’s “Complex” type.
https://raw.github.com/LnDn/PySide-Media/master/Media/BindingGenerator/converter.png
Shiboken expects the code for <native-to-target>, to directly return the Python result of the conversion, and the added conversions inside the <target-to-native> must attribute the Python to C+ conversion result to the %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 <add-conversion> tag and a custom check must be added. Here’s how to do it:
<br />&lt;!— Code injection at module level. —&gt;<br /><inject-code class="native&quot; position="beginning&quot;><br />static bool Check2TupleOfNumbers(PyObject* pyIn) {<br /> if (!PySequence_Check(pyIn) || !(PySequence_Size(pyIn) == 2))<br /> return false;<br /> Shiboken::AutoDecRef pyReal(PySequence_GetItem(pyIn, 0));<br /> if (!SbkNumber_Check(pyReal))<br /> return false;<br /> Shiboken::AutoDecRef pyImag(PySequence_GetItem(pyIn, 1));<br /> if (!SbkNumber_Check(pyImag))<br /> return false;<br /> return true;<br />}<br /></inject-code&gt;
<primitive-type name="Complex&quot; target-lang-api-name="PyComplex&quot;><br /> <include file-name="complex.h&quot; location="global&quot;/&gt;
<conversion-rule&gt;
<native-to-target&gt;<br /> return PyComplex_FromDoubles(%in.real(), %in.imag());<br /> </native-to-target&gt;
<target-to-native&gt;
<add-conversion type="PyComplex&quot;><br /> double real = PyComplex_RealAsDouble(%in);<br /> double imag = PyComplex_ImagAsDouble(%in);<br /> %out = %OUTTYPE (real, imag);<br /> </add-conversion&gt;
<add-conversion type="PySequence&quot; check="Check2TupleOfNumbers(%in)"><br /> Shiboken::AutoDecRef pyReal(PySequence_GetItem(%in, 0));<br /> Shiboken::AutoDecRef pyImag(PySequence_GetItem(%in, 1));<br /> double real = %CONVERTTOCPP[double](pyReal);<br /> double imag = %CONVERTTOCPP[double](pyImag);<br /> %out = %OUTTYPE (real, imag);<br /> </add-conversion&gt;
</target-to-native&gt;
</conversion-rule&gt;
</primitive-type&gt;<br />
Container Conversions
Converters for <container-type> are pretty much the same as for other type, except that they make use of the type system variables %INTYPE_# and %OUTTYPE_#. Shiboken combines the conversion code for containers with the conversion defined (or automatically generated) for the containees.
<br /><container-type name="std::map&quot; type="map&quot;><br /> <include file-name="map&quot; location="global&quot;/&gt;
<conversion-rule&gt;
<native-to-target&gt;<br /> PyObject* %out = PyDict_New();<br /> %INTYPE::const_iterator it = %in.begin();<br /> for (; it != %in.end(); +''it) {<br /> %INTYPE_0 key = it->first;<br /> %INTYPE_1 value = it->second;<br /> PyDict_SetItem(%out,<br /> %CONVERTTOPYTHON[%INTYPE_0](key),<br /> %CONVERTTOPYTHON[%INTYPE_1](value));<br /> }<br /> return %out;<br /> </native-to-target&gt;
<br /> <target-to-native&gt;
<br /> <add-conversion type="PyDict&quot;><br /> PyObject* key;<br /> PyObject* value;<br /> Py_ssize_t pos = 0;<br /> while (PyDict_Next(%in, &amp;pos, &amp;key, &amp;value)) {<br /> %OUTTYPE_0 cppKey = %CONVERTTOCPP[%OUTTYPE_0](key);<br /> %OUTTYPE_1 cppValue = %CONVERTTOCPP[%OUTTYPE_1](value);<br /> %out.insert(%OUTTYPE::value_type(cppKey, cppValue));<br /> }<br /> </add-conversion&gt;
<br /> </target-to-native&gt;<br /> </conversion-rule&gt;<br /></container-type&gt;<br />
h2. Variables & Functions
h3. %in
Variable replaced by the C+ input variable.
%out
Variable replaced by the C++ output variable. Needed to convey the result of a Python to C++ conversion.
%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_#
Replaced by the name of the #th type used in a container.
%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_#
Replaced by the name of the #th type used in a container.
%CHECKTYPE[CPPTYPE]
Replaced by a Shiboken type checking function for a Python variable. The C++ type is indicated by CPPTYPE.
Converting The Old Converters
If you use Shiboken 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:
<br /><primitive-type name="Complex&quot; target-lang-api-name="PyComplex&quot;><br /> <include file-name="complex.h&quot; location="global&quot;/&gt;<br /> <conversion-rule file="complex_conversions.h&quot;/&gt;<br /></primitive-type&gt;<br />
And implemented in a separate C++ file, like this:
<br />namespace Shiboken {<br />template&lt;> struct Converter&lt;Complex&gt;<br />{<br /> static inline bool checkType(PyObject* pyObj) {<br /> return PyComplex_Check(pyObj);<br /> }<br /> static inline bool isConvertible(PyObject* pyObj) {<br /> return PyComplex_Check(pyObj);<br /> }<br /> static inline PyObject* toPython(void* cppobj) {<br /> return toPython('''reinterpret_cast&lt;Complex'''>(cppobj));<br /> }<br /> static inline PyObject* toPython(const Complex&amp; cpx) {<br /> return PyComplex_FromDoubles(cpx.real(), cpx.imag());<br /> }<br /> static inline Complex toCpp(PyObject* pyobj) {<br /> double real = PyComplex_RealAsDouble(pyobj);<br /> double imag = PyComplex_ImagAsDouble(pyobj);<br /> return Complex(real, imag);<br /> }<br />};<br />}<br />