PySide Shiboken Type Converters: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
(Rename category "LanguageBindings::PySide" -> "PySide")
 
(7 intermediate revisions by 3 users not shown)
Line 1: Line 1:
[toc align_right=&quot;yes&amp;quot; depth=&quot;2&amp;quot;]<br />[[Category:LanguageBindings::PySide]]


= 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.
[[Category:PySide]]


<code><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 /></code>
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 />For the user defined conversion code to be inserted in the proper places, the “&lt;conversion-rule&amp;gt;” tag must be used.
<br /><code><br />&lt;primitive-type name=&quot;Complex&amp;quot; target-lang-api-name=&quot;PyComplex&amp;quot;&gt;<br /> &lt;include file-name=&quot;complex.h&amp;quot; location=&quot;global&amp;quot;/&amp;gt;
<br /> &lt;conversion-rule&amp;gt;
<br /> &lt;native-to-target&amp;gt;<br /> return PyComplex_FromDoubles(%in.real(), %in.imag());<br /> &lt;/native-to-target&amp;gt;
<br /> &lt;target-to-native&amp;gt;<br /> &amp;lt;!— The 'check' attribute can be derived from the 'type' attribute,<br /> it is defined here to test the CHECKTYPE type system variable. —&amp;gt;<br /> &lt;add-conversion type=&quot;PyComplex&amp;quot; check=&quot;%CHECKTYPE[Complex](%in)&quot;&gt;<br /> double real = PyComplex_RealAsDouble(%in);<br /> double imag = PyComplex_ImagAsDouble(%in);<br /> %out = %OUTTYPE (real, imag);<br /> &lt;/add-conversion&amp;gt;<br /> &lt;/target-to-native&amp;gt;
<br /> &lt;/conversion-rule&amp;gt;
<br />&lt;/primitive-type&amp;gt;<br /></code>
<br />The details will be given later, but the gist of it are the tags &lt;native-to-target&amp;gt;, which has only one conversion from C''+ to Python, and &lt;target-to-native&amp;gt;, that may define the conversion of multiple Python types to C+''’s “Complex” type.
<br />[[Image:https://raw.github.com/LnDn/PySide-Media/master/Media/BindingGenerator/converter.png|https://raw.github.com/LnDn/PySide-Media/master/Media/BindingGenerator/converter.png]]
<br />Shiboken expects the code for &lt;native-to-target&amp;gt;, to directly return the Python result of the conversion, and the added conversions inside the &lt;target-to-native&amp;gt; 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 &lt;add-conversion&amp;gt; tag and a custom check must be added. Here’s how to do it:
<code>
// C++ class
struct Complex {
Complex(double real, double imag);
double real() const;
double imag() const;
};


<code><br />&amp;lt;!— Code injection at module level. —&amp;gt;<br />&lt;inject-code class=&quot;native&amp;quot; position=&quot;beginning&amp;quot;&gt;<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 />&lt;/inject-code&amp;gt;
// Converting from C++ to Python using the CPython API:
PyObject* pyCpxObj = PyComplex_FromDoubles(complex.real(), complex.imag());


&lt;primitive-type name=&quot;Complex&amp;quot; target-lang-api-name=&quot;PyComplex&amp;quot;&gt;<br /> &lt;include file-name=&quot;complex.h&amp;quot; location=&quot;global&amp;quot;/&amp;gt;
// Converting from Python to C+'':
double real = PyComplex_RealAsDouble(pyCpxObj);
double imag = PyComplex_ImagAsDouble(pyCpxObj);
Complex cpx(real, imag);
</code>


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


&lt;native-to-target&amp;gt;<br /> return PyComplex_FromDoubles(%in.real(), %in.imag());<br /> &lt;/native-to-target&amp;gt;
<code>
<primitive-type name="Complex" target-lang-api-name="PyComplex">
<include file-name="complex.h" location="global"/>


&lt;target-to-native&amp;gt;
<conversion-rule>


&lt;add-conversion type=&quot;PyComplex&amp;quot;&gt;<br /> double real = PyComplex_RealAsDouble(%in);<br /> double imag = PyComplex_ImagAsDouble(%in);<br /> %out = %OUTTYPE (real, imag);<br /> &lt;/add-conversion&amp;gt;
<native-to-target>
return PyComplex_FromDoubles(%in.real(), %in.imag());
</native-to-target>


&lt;add-conversion type=&quot;PySequence&amp;quot; check=&quot;Check2TupleOfNumbers(%in)&quot;&gt;<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 /> &lt;/add-conversion&amp;gt;
<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>


&lt;/target-to-native&amp;gt;
</conversion-rule>


&lt;/conversion-rule&amp;gt;
</primitive-type>
</code>


&lt;/primitive-type&amp;gt;<br /></code>
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.
 
[[Image:Converter.png|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:
 
<code>
<!— 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>
</code>


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


Converters for &lt;container-type&amp;gt; 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.
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.
 
<code>
<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>


<code><br />&lt;container-type name=&quot;std::map&amp;quot; type=&quot;map&amp;quot;&gt;<br /> &lt;include file-name=&quot;map&amp;quot; location=&quot;global&amp;quot;/&amp;gt;
<add-conversion type="PyDict">
PyObject* key;
PyObject* value;
Py_ssize_t pos = 0;
while (PyDict_Next(%in, &pos, &key, &value)) {
%OUTTYPE_0 cppKey = %CONVERTTOCPP[%OUTTYPE_0](key);
%OUTTYPE_1 cppValue = %CONVERTTOCPP[%OUTTYPE_1](value);
%out.insert(%OUTTYPE::value_type(cppKey, cppValue));
}
</add-conversion>


&lt;conversion-rule&amp;gt;
</target-to-native>
</conversion-rule>
</container-type>
</code>


&lt;native-to-target&amp;gt;<br /> PyObject* %out = PyDict_New();<br /> %INTYPE::const_iterator it = %in.begin();<br /> for (; it != %in.end(); +''it) {<br /> %INTYPE_0 key = it-&gt;first;<br /> %INTYPE_1 value = it-&gt;second;<br /> PyDict_SetItem(%out,<br /> %CONVERTTOPYTHON[%INTYPE_0](key),<br /> %CONVERTTOPYTHON[%INTYPE_1](value));<br /> }<br /> return %out;<br /> &lt;/native-to-target&amp;gt;
== Variables & Functions ==
<br /> &lt;target-to-native&amp;gt;
=== %in ===
<br /> &lt;add-conversion type=&quot;PyDict&amp;quot;&gt;<br /> PyObject* key;<br /> PyObject* value;<br /> Py_ssize_t pos = 0;<br /> while (PyDict_Next(%in, &amp;amp;pos, &amp;amp;key, &amp;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 /> &lt;/add-conversion&amp;gt;
Variable replaced by the C''+ input variable.
<br /> &lt;/target-to-native&amp;gt;<br /> &lt;/conversion-rule&amp;gt;<br />&lt;/container-type&amp;gt;<br /></code>
<br />h2. Variables &amp; Functions
<br />h3. %in
<br />Variable replaced by the C''+ input variable.


=== %out ===
=== %out ===
Line 65: Line 158:
=== %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.
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_# ===
=== %INTYPE_# ===
Line 73: Line 166:
=== %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.
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_# ===
=== %OUTTYPE_# ===
Line 89: Line 182:
Previously your conversion rules were declared in one line, like this:
Previously your conversion rules were declared in one line, like this:


<code><br />&lt;primitive-type name=&quot;Complex&amp;quot; target-lang-api-name=&quot;PyComplex&amp;quot;&gt;<br /> &lt;include file-name=&quot;complex.h&amp;quot; location=&quot;global&amp;quot;/&amp;gt;<br /> &lt;conversion-rule file=&quot;complex_conversions.h&amp;quot;/&amp;gt;<br />&lt;/primitive-type&amp;gt;<br /></code>
<code>
<primitive-type name="Complex" target-lang-api-name="PyComplex">
<include file-name="complex.h" location="global"/>
<conversion-rule file="complex_conversions.h"/>
</primitive-type>
</code>


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


<code><br />namespace Shiboken {<br />template&amp;lt;&gt; struct Converter&amp;lt;Complex&amp;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&amp;lt;Complex'''&gt;(cppobj));<br /> }<br /> static inline PyObject* toPython(const Complex&amp;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 /></code>
<code>
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);
}
};
}
</code>

Latest revision as of 03:31, 5 June 2016



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.

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

<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 <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:

<! 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

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.

<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, &pos, &key, &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 & Functions

%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:

<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:

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);
 }
};
}