diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2023-10-31 10:42:28 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-11-01 20:14:17 +0000 |
commit | a636c056c9d0d0f63641ac1c40f6267c9b98c549 (patch) | |
tree | c32e8b415b56718c509f0b48e9c216c238fde866 | |
parent | 6bafa39e119f9702ddf8be9c5fc7c00c54ed7e1e (diff) |
Fix QMetaObject method invocation to accept all of the 10 arguments
Refactor the helper injected into the QMetaObject wrapper code into
helper functions in the QtCore glue code that handle all arguments of
the QMetaObject::invokeMethod() overloads.
Extract a std::function for the actuall call that helps to implement
QMetaMethod::invoke() in a follow-up change.
Task-number: PYSIDE-2500
Change-Id: I4f2b509a13d252dad92e388e1dfd607ae3eafcd1
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
(cherry picked from commit dfbf935717996dd22e0dcf48f199492bf0935a1f)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp | 84 | ||||
-rw-r--r-- | sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h | 48 | ||||
-rw-r--r-- | sources/pyside6/PySide6/QtCore/typesystem_core_common.xml | 51 | ||||
-rw-r--r-- | sources/pyside6/PySide6/glue/qtcore.cpp | 106 |
4 files changed, 220 insertions, 69 deletions
diff --git a/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp b/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp index b702f8df6..4266e868c 100644 --- a/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp +++ b/sources/pyside6/PySide6/QtCore/glue/core_snippets.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "core_snippets_p.h" +#include "qtcorehelper.h" #include "pysideqobject.h" #include "shiboken.h" @@ -10,6 +11,7 @@ #endif #include "basewrapper.h" #include "autodecref.h" +#include "pysideutils.h" #include <QtCore/QCoreApplication> #include <QtCore/QDebug> @@ -262,3 +264,85 @@ bool PyTime_ImportAndCheck(PyObject *pyIn) PyDateTime_IMPORT; return PyTime_Check(pyIn); } + +PyObject *invokeMetaMethod(const InvokeMetaMethodFunc &f, + const QtCoreHelper::QGenericArgumentHolder &a0, + const QtCoreHelper::QGenericArgumentHolder &a1, + const QtCoreHelper::QGenericArgumentHolder &a2, + const QtCoreHelper::QGenericArgumentHolder &a3, + const QtCoreHelper::QGenericArgumentHolder &a4, + const QtCoreHelper::QGenericArgumentHolder &a5, + const QtCoreHelper::QGenericArgumentHolder &a6, + const QtCoreHelper::QGenericArgumentHolder &a7, + const QtCoreHelper::QGenericArgumentHolder &a8, + const QtCoreHelper::QGenericArgumentHolder &a9) +{ + PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS + const bool resultB = f(a0.toGenericArgument(), a1.toGenericArgument(), a2.toGenericArgument(), + a3.toGenericArgument(), a4.toGenericArgument(), a5.toGenericArgument(), + a6.toGenericArgument(), a7.toGenericArgument(), a8.toGenericArgument(), + a9.toGenericArgument()); + PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS + PyObject *result = resultB ? Py_True : Py_False; + Py_INCREF(result); + return result; +} + +// Convert a QGenericReturnArgument to Python for QMetaObject::invokeMethod +static PyObject *convertGenericReturnArgument(const void *retData, QMetaType metaType) +{ + PyObject *result = nullptr; + switch (metaType.id()) { + case QMetaType::Bool: + result = *reinterpret_cast<const bool *>(retData) ? Py_True : Py_False; + Py_INCREF(result); + break; + case QMetaType::Int: + result = PyLong_FromLong(*reinterpret_cast<const int *>(retData)); + break; + case QMetaType::Double: + result = PyFloat_FromDouble(*reinterpret_cast<const double *>(retData)); + break; + case QMetaType::QString: + result = PySide::qStringToPyUnicode(*reinterpret_cast<const QString *>(retData)); + break; + default: { + Shiboken::Conversions::SpecificConverter converter(metaType.name()); + const auto type = converter.conversionType(); + if (type == Shiboken::Conversions::SpecificConverter::InvalidConversion) { + PyErr_Format(PyExc_RuntimeError, "%s: Unable to find converter for \"%s\".", + __FUNCTION__, metaType.name()); + return nullptr; + } + result = converter.toPython(retData); + } + } + return result; +} + +PyObject *invokeMetaMethodWithReturn(const InvokeMetaMethodFuncWithReturn &f, + const QtCoreHelper::QGenericReturnArgumentHolder &r, + const QtCoreHelper::QGenericArgumentHolder &a0, + const QtCoreHelper::QGenericArgumentHolder &a1, + const QtCoreHelper::QGenericArgumentHolder &a2, + const QtCoreHelper::QGenericArgumentHolder &a3, + const QtCoreHelper::QGenericArgumentHolder &a4, + const QtCoreHelper::QGenericArgumentHolder &a5, + const QtCoreHelper::QGenericArgumentHolder &a6, + const QtCoreHelper::QGenericArgumentHolder &a7, + const QtCoreHelper::QGenericArgumentHolder &a8, + const QtCoreHelper::QGenericArgumentHolder &a9) +{ + PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS + const bool callResult = f(r.toGenericReturnArgument(), + a0.toGenericArgument(), a1.toGenericArgument(), a2.toGenericArgument(), + a3.toGenericArgument(), a4.toGenericArgument(), a5.toGenericArgument(), + a6.toGenericArgument(), a7.toGenericArgument(), a8.toGenericArgument(), + a9.toGenericArgument()); + PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS + if (!callResult) { + PyErr_SetString(PyExc_RuntimeError, "QMetaMethod invocation failed."); + return nullptr; + } + return convertGenericReturnArgument(r.data(), r.metaType()); +} diff --git a/sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h b/sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h index 9beddf376..eeeed98e7 100644 --- a/sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h +++ b/sources/pyside6/PySide6/QtCore/glue/core_snippets_p.h @@ -12,11 +12,18 @@ #include <functional> +QT_FORWARD_DECLARE_CLASS(QGenericArgument) +QT_FORWARD_DECLARE_CLASS(QGenericReturnArgument) QT_FORWARD_DECLARE_CLASS(QMetaType) QT_FORWARD_DECLARE_CLASS(QObject) QT_FORWARD_DECLARE_CLASS(QRegularExpression) QT_FORWARD_DECLARE_CLASS(QVariant); +namespace QtCoreHelper { +class QGenericArgumentHolder; +class QGenericReturnArgumentHolder; +} + // Helpers for QVariant conversion QMetaType QVariant_resolveMetaType(PyTypeObject *type); @@ -52,4 +59,45 @@ bool PyDate_ImportAndCheck(PyObject *pyIn); bool PyDateTime_ImportAndCheck(PyObject *pyIn); bool PyTime_ImportAndCheck(PyObject *pyIn); +// Helpers for QMetaObject::invokeMethod(), QMetaMethod::invoke(). The std::function +// serves to abstract from QMetaObject/QMetaMethod invocation parameters. +using InvokeMetaMethodFunc = + std::function<bool(QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument, + QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument, + QGenericArgument,QGenericArgument)>; + +using InvokeMetaMethodFuncWithReturn = + std::function<bool(QGenericReturnArgument, + QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument, + QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument, + QGenericArgument,QGenericArgument)>; + +// Call a void meta method from Python passing the argument holder helpers. +PyObject *invokeMetaMethod(const InvokeMetaMethodFunc &f, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &); + +// Call a meta method with a return value from Python passing the argument holder +// helpers. +PyObject *invokeMetaMethodWithReturn(const InvokeMetaMethodFuncWithReturn &f, + const QtCoreHelper::QGenericReturnArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &, + const QtCoreHelper::QGenericArgumentHolder &); + #endif // CORE_SNIPPETS_P_H diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml index e533b8fdc..411f55149 100644 --- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml +++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml @@ -3106,6 +3106,7 @@ <extra-includes> <include file-name="dynamicqmetaobject.h" location="global"/> <include file-name="pysidemetatype.h" location="global"/> + <include file-name="glue/core_snippets_p.h" location="local"/> <include file-name="pysideutils.h" location="global"/> <!-- QString conversion --> </extra-includes> <inject-code class="native" position="beginning" file="../glue/qtcore.cpp" @@ -3118,22 +3119,64 @@ <declare-function signature="operator bool() const" return-type="bool"/> </value-type> <modify-function signature="^invokeMethod\(" allow-thread="yes"/> - <add-function signature="invokeMethod(QObject*@object@,const char *@member@,Qt::ConnectionType@type@,QtCoreHelper::QGenericArgumentHolder@val0@={},QtCoreHelper::QGenericArgumentHolder@val1@={},QtCoreHelper::QGenericArgumentHolder@val2@={})" + <add-function signature="invokeMethod(QObject*@object@,const char *@member@,Qt::ConnectionType@type@, + QtCoreHelper::QGenericArgumentHolder@val0@={}, + QtCoreHelper::QGenericArgumentHolder@val1@={}, + QtCoreHelper::QGenericArgumentHolder@val2@={}, + QtCoreHelper::QGenericArgumentHolder@val3@={}, + QtCoreHelper::QGenericArgumentHolder@val4@={}, + QtCoreHelper::QGenericArgumentHolder@val5@={}, + QtCoreHelper::QGenericArgumentHolder@val6@={}, + QtCoreHelper::QGenericArgumentHolder@val7@={}, + QtCoreHelper::QGenericArgumentHolder@val8@={}, + QtCoreHelper::QGenericArgumentHolder@val9@={})" static="yes" return-type="bool"> <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qmetaobject-invokemethod-conn-type-arg"/> </add-function> - <add-function signature="invokeMethod(QObject*@object@,const char *@member@,QtCoreHelper::QGenericArgumentHolder@val0@={},QtCoreHelper::QGenericArgumentHolder@val1@={},QtCoreHelper::QGenericArgumentHolder@val2@={})" + <add-function signature="invokeMethod(QObject*@object@,const char *@member@, + QtCoreHelper::QGenericArgumentHolder@val0@={}, + QtCoreHelper::QGenericArgumentHolder@val1@={}, + QtCoreHelper::QGenericArgumentHolder@val2@={}, + QtCoreHelper::QGenericArgumentHolder@val3@={}, + QtCoreHelper::QGenericArgumentHolder@val4@={}, + QtCoreHelper::QGenericArgumentHolder@val5@={}, + QtCoreHelper::QGenericArgumentHolder@val6@={}, + QtCoreHelper::QGenericArgumentHolder@val7@={}, + QtCoreHelper::QGenericArgumentHolder@val8@={}, + QtCoreHelper::QGenericArgumentHolder@val9@={})" static="yes" return-type="bool"> <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qmetaobject-invokemethod-arg"/> </add-function> - <add-function signature="invokeMethod(QObject*@object@,const char *@member@,Qt::ConnectionType@type@,QtCoreHelper::QGenericReturnArgumentHolder@ret@,QtCoreHelper::QGenericArgumentHolder@val0@={},QtCoreHelper::QGenericArgumentHolder@val1@={},QtCoreHelper::QGenericArgumentHolder@val2@={})" + <add-function signature="invokeMethod(QObject*@object@,const char *@member@,Qt::ConnectionType@type@, + QtCoreHelper::QGenericReturnArgumentHolder@ret@, + QtCoreHelper::QGenericArgumentHolder@val0@={}, + QtCoreHelper::QGenericArgumentHolder@val1@={}, + QtCoreHelper::QGenericArgumentHolder@val2@={}, + QtCoreHelper::QGenericArgumentHolder@val3@={}, + QtCoreHelper::QGenericArgumentHolder@val4@={}, + QtCoreHelper::QGenericArgumentHolder@val5@={}, + QtCoreHelper::QGenericArgumentHolder@val6@={}, + QtCoreHelper::QGenericArgumentHolder@val7@={}, + QtCoreHelper::QGenericArgumentHolder@val8@={}, + QtCoreHelper::QGenericArgumentHolder@val9@={})" static="yes" return-type="PyObject*"> <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qmetaobject-invokemethod-conn-type-return-arg"/> </add-function> - <add-function signature="invokeMethod(QObject*@object@,const char *@member@,QtCoreHelper::QGenericReturnArgumentHolder@ret@,QtCoreHelper::QGenericArgumentHolder@val0@={},QtCoreHelper::QGenericArgumentHolder@val1@={},QtCoreHelper::QGenericArgumentHolder@val2@={})" + <add-function signature="invokeMethod(QObject*@object@,const char *@member@, + QtCoreHelper::QGenericReturnArgumentHolder@ret@, + QtCoreHelper::QGenericArgumentHolder@val0@={}, + QtCoreHelper::QGenericArgumentHolder@val1@={}, + QtCoreHelper::QGenericArgumentHolder@val2@={}, + QtCoreHelper::QGenericArgumentHolder@val3@={}, + QtCoreHelper::QGenericArgumentHolder@val4@={}, + QtCoreHelper::QGenericArgumentHolder@val5@={}, + QtCoreHelper::QGenericArgumentHolder@val6@={}, + QtCoreHelper::QGenericArgumentHolder@val7@={}, + QtCoreHelper::QGenericArgumentHolder@val8@={}, + QtCoreHelper::QGenericArgumentHolder@val9@={})" static="yes" return-type="PyObject*"> <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qmetaobject-invokemethod-return-arg"/> diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp index 6bf03a6bb..0dcd458bd 100644 --- a/sources/pyside6/PySide6/glue/qtcore.cpp +++ b/sources/pyside6/PySide6/glue/qtcore.cpp @@ -1750,85 +1750,61 @@ QtCoreHelper::QGenericReturnArgumentHolder result(qArgData.metaType, qArgData.da %PYARG_0 = %CONVERTTOPYTHON[QtCoreHelper::QGenericReturnArgumentHolder](result); // @snippet q_return_arg +// @snippet qmetaobject-invokemethod-helpers +static InvokeMetaMethodFunc + createInvokeMetaMethodFunc(QObject *object, const char *methodName, + Qt::ConnectionType type = Qt::AutoConnection) +{ + return [object, methodName, type](QGenericArgument a0, QGenericArgument a1, + QGenericArgument a2, QGenericArgument a3, + QGenericArgument a4, QGenericArgument a5, + QGenericArgument a6, QGenericArgument a7, + QGenericArgument a8, QGenericArgument a9) -> bool + { + return QMetaObject::invokeMethod(object, methodName, type, + a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + }; +} + +static InvokeMetaMethodFuncWithReturn + createInvokeMetaMethodFuncWithReturn(QObject *object, const char *methodName, + Qt::ConnectionType type = Qt::AutoConnection) +{ + return [object, methodName, type](QGenericReturnArgument r, + QGenericArgument a0, QGenericArgument a1, + QGenericArgument a2, QGenericArgument a3, + QGenericArgument a4, QGenericArgument a5, + QGenericArgument a6, QGenericArgument a7, + QGenericArgument a8, QGenericArgument a9) -> bool + { + return QMetaObject::invokeMethod(object, methodName, type, + r, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); + }; +} +// @snippet qmetaobject-invokemethod-helpers + // invokeMethod(QObject *,const char *, QGenericArgument a0, a1, a2 ) // @snippet qmetaobject-invokemethod-arg -PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS -const bool result = %CPPSELF.invokeMethod(%1, %2, %3.toGenericArgument(), %4.toGenericArgument(), - %5.toGenericArgument()); -PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS -%PYARG_0 = %CONVERTTOPYTHON[bool](result); +%PYARG_0 = invokeMetaMethod(createInvokeMetaMethodFunc(%1, %2), + %3, %4, %5, %6, %7, %8, %9, %10, %11, %12); // @snippet qmetaobject-invokemethod-arg // invokeMethod(QObject *,const char *,Qt::ConnectionType, QGenericArgument a0, a1, a2 ) // @snippet qmetaobject-invokemethod-conn-type-arg -PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS -const bool result = %CPPSELF.invokeMethod(%1, %2, %3, - %4.toGenericArgument(), %5.toGenericArgument(), - %6.toGenericArgument()); -PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS -%PYARG_0 = %CONVERTTOPYTHON[bool](result); +%PYARG_0 = invokeMetaMethod(createInvokeMetaMethodFunc(%1, %2, %3), + %4, %5, %6, %7, %8, %9, %10, %11, %12, %13); // @snippet qmetaobject-invokemethod-conn-type-arg -// @snippet qmetaobject-invokemethod-helpers -static PyObject *invokeMethodHelper(QObject *obj, const char *member, Qt::ConnectionType type, - const QtCoreHelper::QGenericReturnArgumentHolder &returnArg, - const QtCoreHelper::QGenericArgumentHolder &v1, - const QtCoreHelper::QGenericArgumentHolder &v2, - const QtCoreHelper::QGenericArgumentHolder &v3) - -{ - PyThreadState *_save = PyEval_SaveThread(); // Py_BEGIN_ALLOW_THREADS - const bool callResult = QMetaObject::invokeMethod(obj, member, type, - returnArg.toGenericReturnArgument(), - v1.toGenericArgument(), v2.toGenericArgument(), - v3.toGenericArgument()); - PyEval_RestoreThread(_save); // Py_END_ALLOW_THREADS - if (!callResult) { - PyErr_Format(PyExc_RuntimeError, "QMetaObject::invokeMethod(): Invocation of %s::%s() failed.", - obj->metaObject()->className(), member); - return nullptr; - } - - PyObject *result = nullptr; - const void *retData = returnArg.data(); - const QMetaType metaType = returnArg.metaType(); - switch (metaType.id()) { - case QMetaType::Bool: - result = *reinterpret_cast<const bool *>(retData) ? Py_True : Py_False; - Py_INCREF(result); - break; - case QMetaType::Int: - result = PyLong_FromLong(*reinterpret_cast<const int *>(retData)); - break; - case QMetaType::Double: - result = PyFloat_FromDouble(*reinterpret_cast<const double *>(retData)); - break; - case QMetaType::QString: - result = PySide::qStringToPyUnicode(*reinterpret_cast<const QString *>(retData)); - break; - default: { - Shiboken::Conversions::SpecificConverter converter(metaType.name()); - const auto type = converter.conversionType(); - if (type == Shiboken::Conversions::SpecificConverter::InvalidConversion) { - PyErr_Format(PyExc_RuntimeError, "%s: Unable to find converter for \"%s\".", - __FUNCTION__, metaType.name()); - return nullptr; - } - result = converter.toPython(retData); - } - } - return result; -} -// @snippet qmetaobject-invokemethod-helpers - // invokeMethod(QObject *,const char *, Qt::ConnectionType, QGenericReturnArgument,QGenericArgument a0, a1, a2 ) // @snippet qmetaobject-invokemethod-conn-type-return-arg -%PYARG_0 = invokeMethodHelper(%1, %2, %3, %4, %5, %6, %7); +%PYARG_0 = invokeMetaMethodWithReturn(createInvokeMetaMethodFuncWithReturn(%1, %2, %3), + %4, %5, %6, %7, %8, %9, %10, %11, %12, %13, %14); // @snippet qmetaobject-invokemethod-conn-type-return-arg // invokeMethod(QObject *,const char *, QGenericReturnArgument,QGenericArgument a0, a1, a2 ) // @snippet qmetaobject-invokemethod-return-arg -%PYARG_0 = invokeMethodHelper(%1, %2, Qt::AutoConnection, %3, %4, %5, %6); +%PYARG_0 = invokeMetaMethodWithReturn(createInvokeMetaMethodFuncWithReturn(%1, %2), + %3, %4, %5, %6, %7, %8, %9, %10, %11, %12, %13); // @snippet qmetaobject-invokemethod-return-arg // @snippet keycombination-from-keycombination |