diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2021-10-21 16:12:43 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2021-11-08 08:14:26 +0100 |
commit | 5bfcf4b6613a364ee13d0cb5e865bb6582ab7dbb (patch) | |
tree | ff1d2f21854d8aaa036aa900730610c6061687a9 | |
parent | a7a8138bae00ef1aecafed04a8ac265a9c6f07ff (diff) |
shiboken6: Add predefined templates for standard container type conversion
Add some predefined XML templates with common conversions for
various container types (STL and Qt) and PyLong.
Remove the primitive type QModelIndexList since QModelIndexList is
just a typedef.
As a drive-by:
- Fix a bug in cppmultihash_to_pymap_conversion
which would not handle multiple keys correctly (insert
several times due to iterator kit not moved past the range).
- Simplify the pySequenceToCppContainer conversion by
using PyIter_Next().
[ChangeLog][shiboken6] Pre-defined XML templates for
standard container type conversion have been added.
Task-number: PYSIDE-1666
Change-Id: Ic2e36a75f26853651718e27e0788a37519393322
Reviewed-by: Christian Tismer <tismer@stackless.com>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
-rw-r--r-- | sources/pyside6/PySide6/QtCore/typesystem_core_common.xml | 79 | ||||
-rw-r--r-- | sources/pyside6/PySide6/glue/qtcore.cpp | 16 | ||||
-rw-r--r-- | sources/pyside6/PySide6/templates/core_common.xml | 110 | ||||
-rw-r--r-- | sources/shiboken6/ApiExtractor/CMakeLists.txt | 1 | ||||
-rw-r--r-- | sources/shiboken6/ApiExtractor/predefined_templates.cpp | 247 | ||||
-rw-r--r-- | sources/shiboken6/ApiExtractor/predefined_templates.h | 45 | ||||
-rw-r--r-- | sources/shiboken6/ApiExtractor/typedatabase.cpp | 11 | ||||
-rw-r--r-- | sources/shiboken6/ApiExtractor/typedatabase.h | 1 | ||||
-rw-r--r-- | sources/shiboken6/doc/typesystem_specifying_types.rst | 6 | ||||
-rw-r--r-- | sources/shiboken6/doc/typesystem_templates.rst | 71 | ||||
-rw-r--r-- | sources/shiboken6/tests/minimalbinding/typesystem_minimal.xml | 16 | ||||
-rw-r--r-- | sources/shiboken6/tests/samplebinding/typesystem_sample.xml | 46 |
12 files changed, 430 insertions, 219 deletions
diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml index 473d3065a..2ee01c5a9 100644 --- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml +++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml @@ -187,7 +187,9 @@ <conversion-rule> <native-to-target file="../glue/qtcore.cpp" snippet="return-pylong"/> <target-to-native> - <add-conversion type="PyLong" file="../glue/qtcore.cpp" snippet="conversion-pylong"/> + <add-conversion type="PyLong"> + <insert-template name="shiboken_conversion_pylong_to_cpp"/> + </add-conversion> </target-to-native> </conversion-rule> </primitive-type> @@ -203,7 +205,9 @@ <conversion-rule> <native-to-target file="../glue/qtcore.cpp" snippet="return-pylong"/> <target-to-native> - <add-conversion type="PyLong" file="../glue/qtcore.cpp" snippet="conversion-pylong"/> + <add-conversion type="PyLong"> + <insert-template name="shiboken_conversion_pylong_to_cpp"/> + </add-conversion> </target-to-native> </conversion-rule> </primitive-type> @@ -267,13 +271,13 @@ <include file-name="QStringList" location="global"/> <conversion-rule> <native-to-target> - <insert-template name="cpplist_to_pylist_conversion"> + <insert-template name="shiboken_conversion_cppsequence_to_pylist"> <replace from="%INTYPE_0" to="QString"/> </insert-template> </native-to-target> <target-to-native> <add-conversion type="PySequence"> - <insert-template name="pyseq_to_cpplist_conversion"> + <insert-template name="shiboken_conversion_pyiterable_to_cppsequentialcontainer_reserve"> <replace from="%OUTTYPE_0" to="QString"/> </insert-template> </add-conversion> @@ -336,33 +340,16 @@ </conversion-rule> </value-type> - <primitive-type name="QModelIndexList"> - <include file-name="qabstractitemmodel.h" location="global"/> - <conversion-rule> - <native-to-target> - <insert-template name="cpplist_to_pylist_conversion"> - <replace from="%INTYPE_0" to="QModelIndex"/> - </insert-template> - </native-to-target> - <target-to-native> - <add-conversion type="PySequence"> - <insert-template name="pyseq_to_cpplist_conversion"> - <replace from="%OUTTYPE_0" to="QModelIndex"/> - </insert-template> - </add-conversion> - </target-to-native> - </conversion-rule> - </primitive-type> - <container-type name="QSet" type="set"> <include file-name="QSet" location="global"/> <conversion-rule> <native-to-target> - <insert-template name="cpplist_to_pylist_conversion"/> + <!-- FIXME PYSIDE 7: shiboken_conversion_cppsequence_to_pyset --> + <insert-template name="shiboken_conversion_cppsequence_to_pylist"/> </native-to-target> <target-to-native> <add-conversion type="PySequence"> - <insert-template name="pyseq_to_cpplist_conversion"/> + <insert-template name="shiboken_conversion_pyiterable_to_cppsetcontainer"/> </add-conversion> </target-to-native> </conversion-rule> @@ -382,11 +369,11 @@ <!-- operator << needs to be declared in inheriting class --> <conversion-rule> <native-to-target> - <insert-template name="cpplist_to_pylist_conversion"/> + <insert-template name="shiboken_conversion_cppsequence_to_pylist"/> </native-to-target> <target-to-native> <add-conversion type="PySequence"> - <insert-template name="pyseq_to_cpplist_conversion"/> + <insert-template name="shiboken_conversion_pyiterable_to_cppsequentialcontainer_reserve"/> </add-conversion> </target-to-native> </conversion-rule> @@ -396,11 +383,11 @@ <include file-name="QStack" location="global"/> <conversion-rule> <native-to-target> - <insert-template name="cpplist_to_pylist_conversion"/> + <insert-template name="shiboken_conversion_cppsequence_to_pylist"/> </native-to-target> <target-to-native> <add-conversion type="PySequence"> - <insert-template name="pyseq_to_cpplist_conversion"/> + <insert-template name="shiboken_conversion_pyiterable_to_cppsequentialcontainer_reserve"/> </add-conversion> </target-to-native> </conversion-rule> @@ -410,11 +397,11 @@ <include file-name="QQueue" location="global"/> <conversion-rule> <native-to-target> - <insert-template name="cpplist_to_pylist_conversion"/> + <insert-template name="shiboken_conversion_cppsequence_to_pylist"/> </native-to-target> <target-to-native> <add-conversion type="PySequence"> - <insert-template name="pyseq_to_cpplist_conversion"/> + <insert-template name="shiboken_conversion_pyiterable_to_cppsequentialcontainer_reserve"/> </add-conversion> </target-to-native> </conversion-rule> @@ -426,11 +413,11 @@ <include file-name="pysideqflags.h" location="global"/> <conversion-rule> <native-to-target> - <insert-template name="cppmap_to_pymap_conversion"/> + <insert-template name="shiboken_conversion_qmap_to_pydict"/> </native-to-target> <target-to-native> <add-conversion type="PyDict"> - <insert-template name="pydict_to_cppmap_conversion"/> + <insert-template name="shiboken_conversion_pydict_to_qmap"/> </add-conversion> </target-to-native> </conversion-rule> @@ -442,11 +429,11 @@ <include file-name="pysideqflags.h" location="global"/> <conversion-rule> <native-to-target> - <insert-template name="cppmultihash_to_pymap_conversion"/> + <insert-template name="shiboken_conversion_qmultihash_to_pydict"/> </native-to-target> <target-to-native> <add-conversion type="PyDict"> - <insert-template name="pydict_to_cppmultimap_conversion"/> + <insert-template name="shiboken_conversion_pydict_to_qmultihash"/> </add-conversion> </target-to-native> </conversion-rule> @@ -456,11 +443,11 @@ <include file-name="QMap" location="global"/> <conversion-rule> <native-to-target> - <insert-template name="cppmap_to_pymap_conversion"/> + <insert-template name="shiboken_conversion_qmap_to_pydict"/> </native-to-target> <target-to-native> <add-conversion type="PyDict"> - <insert-template name="pydict_to_cppmap_conversion"/> + <insert-template name="shiboken_conversion_pydict_to_qmap"/> </add-conversion> </target-to-native> </conversion-rule> @@ -469,11 +456,11 @@ <include file-name="QMultiMap" location="global"/> <conversion-rule> <native-to-target> - <insert-template name="cppmultimap_to_pymap_conversion"/> + <insert-template name="shiboken_conversion_qmultimap_to_pydict"/> </native-to-target> <target-to-native> <add-conversion type="PyDict"> - <insert-template name="pydict_to_cppmultimap_conversion"/> + <insert-template name="shiboken_conversion_pydict_to_qmultihash"/> </add-conversion> </target-to-native> </conversion-rule> @@ -482,9 +469,13 @@ <container-type name="QPair" type="pair"> <include file-name="QPair" location="global"/> <conversion-rule> - <native-to-target file="../glue/qtcore.cpp" snippet="return-qpair"/> + <native-to-target> + <insert-template name="shiboken_conversion_cpppair_to_pytuple"/> + </native-to-target> <target-to-native> - <add-conversion type="PySequence" file="../glue/qtcore.cpp" snippet="conversion-qpair-pysequence"/> + <add-conversion type="PySequence"> + <insert-template name="shiboken_conversion_pysequence_to_cpppair"/> + </add-conversion> </target-to-native> </conversion-rule> </container-type> @@ -492,9 +483,13 @@ <!-- QPair is implemented with std::pair since Qt 6 --> <container-type name="std::pair" type="pair"> <conversion-rule> - <native-to-target file="../glue/qtcore.cpp" snippet="return-qpair"/> + <native-to-target> + <insert-template name="shiboken_conversion_cpppair_to_pytuple"/> + </native-to-target> <target-to-native> - <add-conversion type="PySequence" file="../glue/qtcore.cpp" snippet="conversion-qpair-pysequence"/> + <add-conversion type="PySequence"> + <insert-template name="shiboken_conversion_pysequence_to_cpppair"/> + </add-conversion> </target-to-native> </conversion-rule> </container-type> diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp index 76b0ec0d7..7229fd378 100644 --- a/sources/pyside6/PySide6/glue/qtcore.cpp +++ b/sources/pyside6/PySide6/glue/qtcore.cpp @@ -1669,10 +1669,6 @@ if (PyErr_WarnEx(PyExc_DeprecationWarning, %out = %OUTTYPE(%in == Py_True); // @snippet conversion-pybool -// @snippet conversion-pylong -%out = %OUTTYPE(PyLong_AsLong(%in)); -// @snippet conversion-pylong - // @snippet conversion-pylong-quintptr #if QT_POINTER_SIZE == 8 %out = %OUTTYPE(PyLong_AsUnsignedLongLong(%in)); @@ -1782,11 +1778,6 @@ QJsonValue val = QJsonValue::fromVariant(dict); %out = val.toObject(); // @snippet conversion-qjsonobject-pydict -// @snippet conversion-qpair-pysequence -%out.first = %CONVERTTOCPP[%OUTTYPE_0](PySequence_Fast_GET_ITEM(%in, 0)); -%out.second = %CONVERTTOCPP[%OUTTYPE_1](PySequence_Fast_GET_ITEM(%in, 1)); -// @snippet conversion-qpair-pysequence - // @snippet conversion-qdate-pydate int day = PyDateTime_GET_DAY(%in); int month = PyDateTime_GET_MONTH(%in); @@ -1912,13 +1903,6 @@ QVariant ret = val.toVariant(); return %CONVERTTOPYTHON[QVariant](ret); // @snippet return-qjsonobject -// @snippet return-qpair -PyObject *%out = PyTuple_New(2); -PyTuple_SET_ITEM(%out, 0, %CONVERTTOPYTHON[%INTYPE_0](%in.first)); -PyTuple_SET_ITEM(%out, 1, %CONVERTTOPYTHON[%INTYPE_1](%in.second)); -return %out; -// @snippet return-qpair - // @snippet qthread_pthread_cleanup #ifdef Q_OS_UNIX # include <stdio.h> diff --git a/sources/pyside6/PySide6/templates/core_common.xml b/sources/pyside6/PySide6/templates/core_common.xml index abc0d6425..e26771461 100644 --- a/sources/pyside6/PySide6/templates/core_common.xml +++ b/sources/pyside6/PySide6/templates/core_common.xml @@ -301,38 +301,6 @@ %PYARG_0 = Py_BuildValue("%TT_FORMAT", %TT_ARGS); </template> - <template name="cpplist_to_pylist_conversion"> - PyObject *%out = PyList_New(Py_ssize_t(%in.size())); - Py_ssize_t idx = 0; - for (auto it = %in.cbegin(), end = %in.cend(); it != end; ++it, ++idx) { - const auto &cppItem = *it; - PyList_SET_ITEM(%out, idx, %CONVERTTOPYTHON[%INTYPE_0](cppItem)); - } - return %out; - </template> - - <template name="pyseq_to_cpplist_conversion"> - // PYSIDE-795: Turn all sequences into iterables. - if (PyList_Check(%in)) { - const Py_ssize_t size = PySequence_Size(%in); - if (size > 10) - (%out).reserve(size); - } - - Shiboken::AutoDecRef it(PyObject_GetIter(%in)); - PyObject *(*iternext)(PyObject *) = *Py_TYPE(it)->tp_iternext; - for (;;) { - Shiboken::AutoDecRef pyItem(iternext(it)); - if (pyItem.isNull()) { - if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_StopIteration)) - PyErr_Clear(); - break; - } - %OUTTYPE_0 cppItem = %CONVERTTOCPP[%OUTTYPE_0](pyItem); - %out << cppItem; - } - </template> - <template name="checkPyCapsuleOrPyCObject_func"> static bool checkPyCapsuleOrPyCObject(PyObject* pyObj) { @@ -340,84 +308,6 @@ } </template> - <template name="cppmap_to_pymap_conversion"> - PyObject *%out = PyDict_New(); - for (auto it = %in.cbegin(), end = %in.cend(); it != end; ++it) { - const auto &key = it.key(); - const auto &value = it.value(); - PyObject *pyKey = %CONVERTTOPYTHON[%INTYPE_0](key); - PyObject *pyValue = %CONVERTTOPYTHON[%INTYPE_1](value); - PyDict_SetItem(%out, pyKey, pyValue); - Py_DECREF(pyKey); - Py_DECREF(pyValue); - } - return %out; - </template> - - <template name="pydict_to_cppmap_conversion"> - 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(cppKey, cppValue); - } - </template> - - <template name="cppmultimap_to_pymap_conversion"> - PyObject *%out = PyDict_New(); - for (auto it = %in.cbegin(), end = %in.cend(); it != end; ) { - const auto &key = it.key(); - PyObject *pyKey = %CONVERTTOPYTHON[%INTYPE_0](key); - %INTYPE::const_iterator keyEnd = %in.upperBound(key); - const auto count = Py_ssize_t(std::distance(it, keyEnd)); - PyObject *pyValues = PyList_New(count); - Py_ssize_t idx = 0; - for ( ; it != keyEnd; ++it, ++idx) { - const auto &cppItem = it.value(); - PyList_SET_ITEM(pyValues, idx, %CONVERTTOPYTHON[%INTYPE_1](cppItem)); - } - PyDict_SetItem(%out, pyKey, pyValues); - Py_DECREF(pyKey); - } - return %out; - </template> - - <template name="cppmultihash_to_pymap_conversion"> - PyObject *%out = PyDict_New(); - for (auto kit = %in.keyBegin(), end = %in.keyEnd(); kit != end; ++kit) { - const auto &key = *kit; - PyObject *pyKey = %CONVERTTOPYTHON[%INTYPE_0](key); - auto range = %in.equal_range(key); - const auto count = Py_ssize_t(std::distance(range.first, range.second)); - PyObject *pyValues = PyList_New(count); - Py_ssize_t idx = 0; - for (auto it = range.first; it != range.second; ++it, ++idx) { - const auto &cppItem = it.value(); - PyList_SET_ITEM(pyValues, idx, %CONVERTTOPYTHON[%INTYPE_1](cppItem)); - } - PyDict_SetItem(%out, pyKey, pyValues); - Py_DECREF(pyKey); - } - return %out; - </template> - - <template name="pydict_to_cppmultimap_conversion"> - PyObject *key; - PyObject *values; - Py_ssize_t pos = 0; - while (PyDict_Next(%in, &pos, &key, &values)) { - %OUTTYPE_0 cppKey = %CONVERTTOCPP[%OUTTYPE_0](key); - const Py_ssize_t size = PySequence_Size(values); - for (Py_ssize_t i = 0; i < size; ++i) { - Shiboken::AutoDecRef value(PySequence_GetItem(values, i)); - %OUTTYPE_1 cppValue = %CONVERTTOCPP[%OUTTYPE_1](value); - %out.insert(cppKey, cppValue); - } - } - </template> - <template name="pydatetime_importandcheck_function"> static bool PyDateTime_ImportAndCheck(PyObject *pyIn) { diff --git a/sources/shiboken6/ApiExtractor/CMakeLists.txt b/sources/shiboken6/ApiExtractor/CMakeLists.txt index 37f929531..1c20a8f2f 100644 --- a/sources/shiboken6/ApiExtractor/CMakeLists.txt +++ b/sources/shiboken6/ApiExtractor/CMakeLists.txt @@ -24,6 +24,7 @@ enclosingclassmixin.cpp fileout.cpp messages.cpp modifications.cpp +predefined_templates.cpp propertyspec.cpp reporthandler.cpp sourcelocation.cpp diff --git a/sources/shiboken6/ApiExtractor/predefined_templates.cpp b/sources/shiboken6/ApiExtractor/predefined_templates.cpp new file mode 100644 index 000000000..8545c2cd3 --- /dev/null +++ b/sources/shiboken6/ApiExtractor/predefined_templates.cpp @@ -0,0 +1,247 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "predefined_templates.h" + +static QString pySequenceToCppContainer(const QString &insertFunc, + bool reserve) +{ + QString result; + if (reserve) { + result += uR"(if (PyList_Check(%in)) { + const Py_ssize_t size = PySequence_Size(%in); + if (size > 10) + (%out).reserve(size); +} + +)"_qs; + } + + result += uR"(Shiboken::AutoDecRef it(PyObject_GetIter(%in)); +while (true) { + Shiboken::AutoDecRef pyItem(PyIter_Next(it.object())); + if (pyItem.isNull()) { + if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_StopIteration)) + PyErr_Clear(); + break; + } + %OUTTYPE_0 cppItem = %CONVERTTOCPP[%OUTTYPE_0](pyItem); + (%out).)"_qs; + + result += insertFunc; + result += uR"((cppItem); +} +)"_qs; + return result; +} + +static const char stlMapKeyAccessor[] = "->first"; +static const char stlMapValueAccessor[] = "->second"; +static const char qtMapKeyAccessor[] = ".key()"; +static const char qtMapValueAccessor[] = ".value()"; + +static QString cppMapToPyDict(bool isQMap) +{ + return uR"(PyObject *%out = PyDict_New(); +for (auto it = %in.cbegin(), end = %in.cend(); it != end; ++it) { + const auto &key = it)"_qs + + QLatin1String(isQMap ? qtMapKeyAccessor : stlMapKeyAccessor) + + uR"(; + const auto &value = it)"_qs + + QLatin1String(isQMap ? qtMapValueAccessor : stlMapValueAccessor) + + uR"(; + PyObject *pyKey = %CONVERTTOPYTHON[%INTYPE_0](key); + PyObject *pyValue = %CONVERTTOPYTHON[%INTYPE_1](value); + PyDict_SetItem(%out, pyKey, pyValue); + Py_DECREF(pyKey); + Py_DECREF(pyValue); +} +return %out; +)"_qs; +} + +static QString pyDictToCppMap(bool isQMap) +{ + return uR"(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()"_qs + // STL needs a pair + + (isQMap ? u"cppKey, cppValue"_qs : u"{cppKey, cppValue}"_qs) + uR"(); +} +)"_qs; +} + +// Convert a STL or Qt multi map to Dict of Lists using upperBound() +static QString cppMultiMapToPyDict(bool isQMultiMap) +{ + return uR"(PyObject *%out = PyDict_New(); + for (auto it = %in.cbegin(), end = %in.cend(); it != end; ++it) { + const auto &key = it)"_qs + + QLatin1String(isQMultiMap ? qtMapKeyAccessor : stlMapKeyAccessor) + + uR"(; + PyObject *pyKey = %CONVERTTOPYTHON[%INTYPE_0](key); + auto upper = %in.)"_qs + + (isQMultiMap ? u"upperBound"_qs : u"upper_bound"_qs) + + uR"((key); + const auto count = Py_ssize_t(std::distance(it, upper)); + PyObject *pyValues = PyList_New(count); + Py_ssize_t idx = 0; + for (; it != upper; ++it, ++idx) { + const auto &cppItem = it.value(); + PyList_SET_ITEM(pyValues, idx, %CONVERTTOPYTHON[%INTYPE_1](cppItem)); + } + PyDict_SetItem(%out, pyKey, pyValues); + Py_DECREF(pyKey); + } + return %out; +)"_qs; +} + +// Convert a STL or Qt multi hash to Dict of Lists using equalRange() +static QString cppMultiHashToPyDict(bool isQMultiHash) +{ + return uR"(PyObject *%out = PyDict_New(); + for (auto it = %in.cbegin(), end = %in.cend(); it != end; ++it) { + const auto &key = it)"_qs + + QLatin1String(isQMultiHash ? qtMapKeyAccessor : stlMapKeyAccessor) + + uR"(; + PyObject *pyKey = %CONVERTTOPYTHON[%INTYPE_0](key); + auto range = %in.equal_range(key); + const auto count = Py_ssize_t(std::distance(range.first, range.second)); + PyObject *pyValues = PyList_New(count); + Py_ssize_t idx = 0; + for (; it != range.second; ++it, ++idx) { + const auto &cppItem = it.value(); + PyList_SET_ITEM(pyValues, idx, %CONVERTTOPYTHON[%INTYPE_1](cppItem)); + } + PyDict_SetItem(%out, pyKey, pyValues); + Py_DECREF(pyKey); + } + return %out; +)"_qs; +} + +// Convert Dict of Lists to a STL or Qt multi hash/map +static QString pyDictToCppMultiHash(bool isQMultiHash) +{ + return uR"(PyObject *key; + PyObject *values; + Py_ssize_t pos = 0; + while (PyDict_Next(%in, &pos, &key, &values)) { + %OUTTYPE_0 cppKey = %CONVERTTOCPP[%OUTTYPE_0](key); + const Py_ssize_t size = PySequence_Size(values); + for (Py_ssize_t i = 0; i < size; ++i) { + Shiboken::AutoDecRef value(PySequence_GetItem(values, i)); + %OUTTYPE_1 cppValue = %CONVERTTOCPP[%OUTTYPE_1](value); + %out.insert()"_qs + + (isQMultiHash ? u"cppKey, cppValue"_qs : u"{cppKey, cppValue}"_qs) + + uR"(); + } + } +)"_qs; +} + +const PredefinedTemplates &predefinedTemplates() +{ + static const PredefinedTemplates result{ + {u"shiboken_conversion_pylong_to_cpp"_qs, + u"%out = %OUTTYPE(PyLong_AsLong(%in));\n"_qs}, + + // QPair/std::pair + {u"shiboken_conversion_pysequence_to_cpppair"_qs, + uR"(%out.first = %CONVERTTOCPP[%OUTTYPE_0](PySequence_Fast_GET_ITEM(%in, 0)); +%out.second = %CONVERTTOCPP[%OUTTYPE_1](PySequence_Fast_GET_ITEM(%in, 1)); +)"_qs}, + + {u"shiboken_conversion_cpppair_to_pytuple"_qs, + uR"(PyObject *%out = PyTuple_New(2); +PyTuple_SET_ITEM(%out, 0, %CONVERTTOPYTHON[%INTYPE_0](%in.first)); +PyTuple_SET_ITEM(%out, 1, %CONVERTTOPYTHON[%INTYPE_1](%in.second)); +return %out; +)"_qs}, + + // Sequential containers + {u"shiboken_conversion_cppsequence_to_pylist"_qs, + uR"(PyObject *%out = PyList_New(Py_ssize_t(%in.size())); +Py_ssize_t idx = 0; +for (auto it = %in.cbegin(), end = %in.cend(); it != end; ++it, ++idx) { + const auto &cppItem = *it; + PyList_SET_ITEM(%out, idx, %CONVERTTOPYTHON[%INTYPE_0](cppItem)); +} +return %out;)"_qs}, + + // PySet + {u"shiboken_conversion_cppsequence_to_pyset"_qs, + uR"(PyObject *%out = PySet_New(nullptr); +for (const auto &cppItem : %in) { + PySet_Add(%out, %CONVERTTOPYTHON[%INTYPE_0](cppItem)); +} +return %out;)"_qs}, + + {u"shiboken_conversion_pyiterable_to_cppsequentialcontainer"_qs, + pySequenceToCppContainer(u"push_back"_qs, false)}, + {u"shiboken_conversion_pyiterable_to_cppsequentialcontainer_reserve"_qs, + pySequenceToCppContainer(u"push_back"_qs, true)}, + {u"shiboken_conversion_pyiterable_to_cppsetcontainer"_qs, + pySequenceToCppContainer(u"insert"_qs, false)}, + + // Maps + {u"shiboken_conversion_stdmap_to_pydict"_qs, + cppMapToPyDict(false)}, + {u"shiboken_conversion_qmap_to_pydict"_qs, + cppMapToPyDict(true)}, + {u"shiboken_conversion_pydict_to_stdmap"_qs, + pyDictToCppMap(false)}, + {u"shiboken_conversion_pydict_to_qmap"_qs, + pyDictToCppMap(true)}, + + // Multi maps + {u"shiboken_conversion_stdmultimap_to_pydict"_qs, + cppMultiMapToPyDict(false)}, + {u"shiboken_conversion_qmultimap_to_pydict"_qs, + cppMultiMapToPyDict(true)}, + + // Multi hashes + {u"shiboken_conversion_stdunorderedmultimap_to_pydict"_qs, + cppMultiHashToPyDict(false)}, + {u"shiboken_conversion_qmultihash_to_pydict"_qs, + cppMultiHashToPyDict(true)}, + + // STL multi hash/map + {u"shiboken_conversion_pydict_to_stdmultimap"_qs, + pyDictToCppMultiHash(false)}, + {u"shiboken_conversion_pydict_to_qmultihash"_qs, + pyDictToCppMultiHash(true)} + }; + + return result; +} diff --git a/sources/shiboken6/ApiExtractor/predefined_templates.h b/sources/shiboken6/ApiExtractor/predefined_templates.h new file mode 100644 index 000000000..8c23fa3cf --- /dev/null +++ b/sources/shiboken6/ApiExtractor/predefined_templates.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PREDEFINED_TEMPLATES_H +#define PREDEFINED_TEMPLATES_H + +#include <QtCore/QList> +#include <QtCore/QString> + +struct PredefinedTemplate +{ + QString name; + QString content; +}; + +using PredefinedTemplates = QList<PredefinedTemplate>; + +const PredefinedTemplates &predefinedTemplates(); + +#endif // PREDEFINED_TEMPLATES_H diff --git a/sources/shiboken6/ApiExtractor/typedatabase.cpp b/sources/shiboken6/ApiExtractor/typedatabase.cpp index 12a89662b..65cb2b7ec 100644 --- a/sources/shiboken6/ApiExtractor/typedatabase.cpp +++ b/sources/shiboken6/ApiExtractor/typedatabase.cpp @@ -31,6 +31,7 @@ #include "typesystem.h" #include "typesystemparser.h" #include "conditionalstreamreader.h" +#include "predefined_templates.h" #include <QtCore/QFile> #include <QtCore/QDebug> @@ -105,6 +106,9 @@ TypeDatabase::TypeDatabase() addBuiltInType(new VarargsTypeEntry()); for (const auto &pt : builtinPythonTypes()) addBuiltInType(new PythonTypeEntry(pt.name, pt.checkFunction, pt.type)); + + for (const auto &p : predefinedTemplates()) + addTemplate(p.name, p.content); } TypeDatabase::~TypeDatabase() = default; @@ -592,6 +596,13 @@ void TypeDatabase::addTemplate(TemplateEntry *t) m_templates[t->name()] = t; } +void TypeDatabase::addTemplate(const QString &name, const QString &code) +{ + auto *te = new TemplateEntry(name); + te->addCode(code); + addTemplate(te); +} + void TypeDatabase::addGlobalUserFunctions(const AddedFunctionList &functions) { m_globalUserFunctions << functions; diff --git a/sources/shiboken6/ApiExtractor/typedatabase.h b/sources/shiboken6/ApiExtractor/typedatabase.h index 2eab21f32..d57433dbf 100644 --- a/sources/shiboken6/ApiExtractor/typedatabase.h +++ b/sources/shiboken6/ApiExtractor/typedatabase.h @@ -172,6 +172,7 @@ public: TemplateEntry *findTemplate(const QString &name) const { return m_templates[name]; } void addTemplate(TemplateEntry *t); + void addTemplate(const QString &name, const QString &code); AddedFunctionList globalUserFunctions() const { return m_globalUserFunctions; } diff --git a/sources/shiboken6/doc/typesystem_specifying_types.rst b/sources/shiboken6/doc/typesystem_specifying_types.rst index ade5e5aa6..527a4cff7 100644 --- a/sources/shiboken6/doc/typesystem_specifying_types.rst +++ b/sources/shiboken6/doc/typesystem_specifying_types.rst @@ -176,6 +176,9 @@ primitive-type be instantiated and passed to functions using the view class for argument types. + See :ref:`predefined_templates` for built-in templates for standard type + conversion rules. + .. _namespace: namespace-type @@ -474,6 +477,9 @@ container-type The *optional* **since** value is used to specify the API version of this container. + See :ref:`predefined_templates` for built-in templates for standard type + conversion rules. + .. _typedef-type: typedef-type diff --git a/sources/shiboken6/doc/typesystem_templates.rst b/sources/shiboken6/doc/typesystem_templates.rst index 795c9d97e..a0f52972a 100644 --- a/sources/shiboken6/doc/typesystem_templates.rst +++ b/sources/shiboken6/doc/typesystem_templates.rst @@ -56,3 +56,74 @@ replace This node will replace the attribute ``from`` with the value pointed by ``to``. +.. _predefined_templates: + +Predefined Templates +-------------------- + +There are a number of XML templates for conversion rules for STL and Qt types +built into shiboken. + +Templates for :ref:`primitive-type`: + + +---------------------------------------+--------------------------------+ + |Name | Description | + +---------------------------------------+--------------------------------+ + | ``shiboken_conversion_pylong_to_cpp`` | Convert a PyLong to a C++ type | + +---------------------------------------+--------------------------------+ + +Templates for :ref:`container-type`: + + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_pysequence_to_cpppair`` | Convert a PySequence to a C++ pair (std::pair/QPair) | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_cpppair_to_pytuple`` | Convert a C++ pair (std::pair/QPair) to a PyTuple | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_cppsequence_to_pylist`` | Convert a C++ sequential container to a PyList | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_cppsequence_to_pyset`` | Convert a C++ sequential container to a PySet | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_pyiterable_to_cppsequentialcontainer`` | Convert an iterable Python type to a C++ sequential container (STL/Qt) | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_pyiterable_to_cppsequentialcontainer_reserve`` | Convert an iterable Python type to a C++ sequential container supporting reserve() | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_pyiterable_to_cppsetcontainer`` | Convert a PySequence to a set-type C++ container (std::set/QSet) | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_stdmap_to_pydict`` | Convert a std::map/std::unordered_map to a PyDict | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_qmap_to_pydict`` | Convert a QMap/QHash to a PyDict | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_pydict_to_stdmap`` | Convert a PyDict to a std::map/std::unordered_map | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_pydict_to_qmap`` | Convert a PyDict to a QMap/QHash | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_stdmultimap_to_pydict`` | Convert a std::multimap to a PyDict of value lists | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_qmultimap_to_pydict`` | Convert a QMultiMap to a PyDict of value lists | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_stdunorderedmultimap_to_pydict`` | Convert a std::unordered_multimap to a PyDict of value lists | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_qmultihash_to_pydict`` | Convert a QMultiHash to a PyDict of value lists | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_pydict_to_stdmultimap`` | Convert a PyDict of value lists to std::multimap/std::unordered_multimap | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + | ``shiboken_conversion_pydict_to_qmultihash`` | Convert a PyDict of value lists to QMultiMap/QMultiHash | + +----------------------------------------------------------------------+------------------------------------------------------------------------------------+ + +An entry for the type ``std::list`` using these templates looks like: + +.. code-block:: xml + + <container-type name="std::list" type="list"> + <include file-name="list" location="global"/> + <conversion-rule> + <native-to-target> + <insert-template name="shiboken_conversion_cppsequence_to_pylist"/> + </native-to-target> + <target-to-native> + <add-conversion type="PySequence"> + <insert-template name="shiboken_conversion_pyiterable_to_cppsequentialcontainer"/> + </add-conversion> + </target-to-native> + </conversion-rule> + </container-type> diff --git a/sources/shiboken6/tests/minimalbinding/typesystem_minimal.xml b/sources/shiboken6/tests/minimalbinding/typesystem_minimal.xml index 9bb7e7b7e..7dd0a3a50 100644 --- a/sources/shiboken6/tests/minimalbinding/typesystem_minimal.xml +++ b/sources/shiboken6/tests/minimalbinding/typesystem_minimal.xml @@ -20,23 +20,11 @@ <include file-name="list" location="global"/> <conversion-rule> <native-to-target> - PyObject* %out = PyList_New(Py_ssize_t(%in.size())); - Py_ssize_t idx = 0; - for (auto it = %in.cbegin(), end = %in.cend(); it != end; ++it, ++idx) { - %INTYPE_0 cppItem(*it); - PyList_SET_ITEM(%out, idx, %CONVERTTOPYTHON[%INTYPE_0](cppItem)); - } - return %out; + <insert-template name="shiboken_conversion_cppsequence_to_pylist"/> </native-to-target> <target-to-native> <add-conversion type="PySequence"> - Shiboken::AutoDecRef seq(PySequence_Fast(%in, 0)); - const Py_ssize_t size = PySequence_Fast_GET_SIZE(seq.object()); - for (Py_ssize_t i = 0; i < size; ++i) { - PyObject* pyItem = PySequence_Fast_GET_ITEM(seq.object(), i); - %OUTTYPE_0 cppItem = %CONVERTTOCPP[%OUTTYPE_0](pyItem); - %out.push_back(cppItem); - } + <insert-template name="shiboken_conversion_pyiterable_to_cppsequentialcontainer"/> </add-conversion> </target-to-native> </conversion-rule> diff --git a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml index effadbc54..dd0d5f6e0 100644 --- a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml +++ b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml @@ -351,20 +351,16 @@ <include file-name="utility" location="global"/> <conversion-rule> <native-to-target> - PyObject* %out = PyTuple_New(2); - PyTuple_SET_ITEM(%out, 0, %CONVERTTOPYTHON[%INTYPE_0](%in.first)); - PyTuple_SET_ITEM(%out, 1, %CONVERTTOPYTHON[%INTYPE_1](%in.second)); - return %out; + <insert-template name="shiboken_conversion_cpppair_to_pytuple"/> </native-to-target> <target-to-native> <add-conversion type="PySequence"> - %out.first = %CONVERTTOCPP[%OUTTYPE_0](PySequence_Fast_GET_ITEM(%in, 0)); - %out.second = %CONVERTTOCPP[%OUTTYPE_1](PySequence_Fast_GET_ITEM(%in, 1)); + <insert-template name="shiboken_conversion_pysequence_to_cpppair"/> </add-conversion> </target-to-native> </conversion-rule> </container-type> - <template name="cpplist_to_pylist_convertion"> + <template name="cpp_indexed_list_to_pylist_conversion"> PyObject *%out = PyList_New(Py_ssize_t(%in.size())); Py_ssize_t idx = 0; for (auto it = %in.cbegin(), end = %in.cend(); it != end; ++it, ++idx) { @@ -373,24 +369,15 @@ } return %out; </template> - <template name="pyseq_to_cpplist_convertion"> - Shiboken::AutoDecRef seq(PySequence_Fast(%in, 0)); - const Py_ssize_t size = PySequence_Fast_GET_SIZE(seq.object()); - for (Py_ssize_t i = 0; i < size; ++i) { - PyObject* pyItem = PySequence_Fast_GET_ITEM(seq.object(), i); - %OUTTYPE_0 cppItem = %CONVERTTOCPP[%OUTTYPE_0](pyItem); - %out.push_back(cppItem); - } - </template> <container-type name="std::list" type="list"> <include file-name="list" location="global"/> <conversion-rule> <native-to-target> - <insert-template name="cpplist_to_pylist_convertion"/> + <insert-template name="shiboken_conversion_cppsequence_to_pylist"/> </native-to-target> <target-to-native> <add-conversion type="PySequence"> - <insert-template name="pyseq_to_cpplist_convertion"/> + <insert-template name="shiboken_conversion_pyiterable_to_cppsequentialcontainer"/> </add-conversion> </target-to-native> </conversion-rule> @@ -399,11 +386,11 @@ <include file-name="list" location="global"/> <conversion-rule> <native-to-target> - <insert-template name="cpplist_to_pylist_convertion"/> + <insert-template name="cpp_indexed_list_to_pylist_conversion"/> </native-to-target> <target-to-native> <add-conversion type="PySequence"> - <insert-template name="pyseq_to_cpplist_convertion"/> + <insert-template name="shiboken_conversion_pyiterable_to_cppsequentialcontainer"/> </add-conversion> </target-to-native> </conversion-rule> @@ -412,26 +399,11 @@ <include file-name="map" location="global"/> <conversion-rule> <native-to-target> - PyObject* %out = PyDict_New(); - for (auto it = %in.cbegin(), end = %in.cend(); it != 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; + <insert-template name="shiboken_conversion_stdmap_to_pydict"/> </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({cppKey, cppValue}); - } + <insert-template name="shiboken_conversion_pydict_to_stdmap"/> </add-conversion> </target-to-native> </conversion-rule> |