diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2021-07-13 23:52:13 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2021-07-16 00:24:22 +0000 |
commit | c268afbd0a23d04653833cf6db474535277075cf (patch) | |
tree | 20bb8510fd3917b9604d14981eb81ce18e79625d | |
parent | 751ec5f2ffe87a52702f025756b74bd7e5ac0e07 (diff) |
Improve error reporting when requesting unsupported native interface
By switching out the static_assert for an enable_if we end up producing
a clearer error, at the call site:
/qt/qtbase/examples/gui/rasterwindow/main.cpp:69:9: error: no matching member
function for call to 'nativeInterface'
app.nativeInterface<QNativeInterface::QCocoaGLContext>();
~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/qt/qtbase/src/gui/kernel/qguiapplication.h:176:5: note:
candidate template ignored: requirement
'NativeInterface<QNativeInterface::QCocoaGLContext>::isCompatibleWith<QGuiApplication>'
was not satisfied [with NativeInterface = QNativeInterface::QCocoaGLContext, TypeInfo =
QNativeInterface::Private::NativeInterface<QNativeInterface::QCocoaGLContext>, BaseType =
QGuiApplication]
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QGuiApplication)
^
By using SFINAE for the TypeInfo we can also ensure that it works for
types that are not native interfaces, such as if the user tries to
call nativeInterface<QString>().
Since we can no longer use decltype(*this) to resolve the base type
we need to change QT_DECLARE_NATIVE_INTERFACE_ACCESSOR to take the
type as an argument, as we do for other QT_DECLARE_FOO macros.
Change-Id: Ie3f7e01ab7c3eb3dcc2ef730834f268bb9e81e0c
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit 1ef305de158498ba58063b19a02e40c9f6348857)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | src/corelib/global/qnativeinterface.h | 108 | ||||
-rw-r--r-- | src/corelib/kernel/qcoreapplication.h | 2 | ||||
-rw-r--r-- | src/gui/kernel/qguiapplication.h | 2 | ||||
-rw-r--r-- | src/gui/kernel/qkeymapper_p.h | 2 | ||||
-rw-r--r-- | src/gui/kernel/qoffscreensurface.h | 2 | ||||
-rw-r--r-- | src/gui/kernel/qopenglcontext.h | 2 | ||||
-rw-r--r-- | src/gui/kernel/qscreen.h | 2 | ||||
-rw-r--r-- | src/gui/kernel/qwindow.h | 2 | ||||
-rw-r--r-- | src/widgets/kernel/qapplication.h | 2 | ||||
-rw-r--r-- | tests/auto/corelib/global/qnativeinterface/tst_qnativeinterface.cpp | 15 |
10 files changed, 113 insertions, 26 deletions
diff --git a/src/corelib/global/qnativeinterface.h b/src/corelib/global/qnativeinterface.h index d32e2cd982..395a4ba1da 100644 --- a/src/corelib/global/qnativeinterface.h +++ b/src/corelib/global/qnativeinterface.h @@ -61,11 +61,21 @@ QT_BEGIN_NAMESPACE #define QT_DECLARE_NATIVE_INTERFACE_3(NativeInterface, Revision, BaseType) \ protected: \ virtual ~NativeInterface(); \ + \ struct TypeInfo { \ using baseType = BaseType; \ static constexpr char const *name = QT_STRINGIFY(NativeInterface); \ static constexpr int revision = Revision; \ }; \ + \ + template <typename, typename> \ + friend struct QNativeInterface::Private::has_type_info; \ + \ + template <typename> \ + friend bool constexpr QNativeInterface::Private::hasTypeInfo(); \ + \ + template <typename> \ + friend struct QNativeInterface::Private::TypeInfo; \ public: \ // Revisioned interfaces only make sense when exposed through a base @@ -81,35 +91,97 @@ QT_BEGIN_NAMESPACE QT_OVERLOADED_MACRO(QT_DECLARE_NATIVE_INTERFACE, __VA_ARGS__) namespace QNativeInterface::Private { + + // Basic type-trait to verify that a given native interface has + // all the required type information for us to evaluate it. + template <typename NativeInterface, typename = void> + struct has_type_info : std::false_type {}; + + // The type-trait is friended by TypeInfo, so that we can + // evaluate TypeInfo in the template arguments. + template <typename NativeInterface> + struct has_type_info<NativeInterface, std::void_t< + typename NativeInterface::TypeInfo, + typename NativeInterface::TypeInfo::baseType, + decltype(&NativeInterface::TypeInfo::name), + decltype(&NativeInterface::TypeInfo::revision) + >> : std::true_type {}; + + // We need to wrap the instantiation of has_type_info in a + // function friended by TypeInfo, otherwise MSVC will not + // let us evaluate TypeInfo in the template arguments. template <typename NativeInterface> - struct TypeInfo : private NativeInterface + bool constexpr hasTypeInfo() { - static constexpr char const *name() { return NativeInterface::TypeInfo::name; } - static constexpr int revision() { return NativeInterface::TypeInfo::revision; } + return has_type_info<NativeInterface>::value; + } - template<typename BaseType> - static constexpr bool isCompatibleWith = - std::is_base_of<typename NativeInterface::TypeInfo::baseType, BaseType>::value; + template <typename NativeInterface> + struct TypeInfo + { + // To ensure SFINAE works for hasTypeInfo we can't use it in a constexpr + // variable that also includes an expression that relies on the type + // info. This helper variable is okey, as it it self contained. + static constexpr bool haveTypeInfo = hasTypeInfo<NativeInterface>(); + + // We can then use the helper variable in a constexpr condition in a + // function, which does not break SFINAE if haveTypeInfo is false. + template <typename BaseType> + static constexpr bool isCompatibleHelper() + { + if constexpr (haveTypeInfo) + return std::is_base_of<typename NativeInterface::TypeInfo::baseType, BaseType>::value; + else + return false; + } + + // MSVC however doesn't like constexpr functions in enable_if_t conditions, + // so we need to wrap it yet again in a constexpr variable. This is fine, + // as all the SFINAE magic has been resolved at this point. + template <typename BaseType> + static constexpr bool isCompatibleWith = isCompatibleHelper<BaseType>(); + + // The revision and name accessors are not used in enable_if_t conditions, + // so we can leave them as constexpr functions. As this class template is + // friended by TypeInfo we can access the protected members of TypeInfo. + static constexpr int revision() + { + if constexpr (haveTypeInfo) + return NativeInterface::TypeInfo::revision; + else + return 0; + } + + static constexpr char const *name() + { + if constexpr (haveTypeInfo) + return NativeInterface::TypeInfo::name; + else + return nullptr; + } }; + // Wrapper type to make the error message in case + // of incompatible interface types read better. + template <typename I> + struct NativeInterface : TypeInfo<I> {}; + template <typename T> Q_NATIVE_INTERFACE_IMPORT void *resolveInterface(const T *that, const char *name, int revision); Q_CORE_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcNativeInterface) -} + +} // QNativeInterface::Private // Declares an accessor for the native interface -#define QT_DECLARE_NATIVE_INTERFACE_ACCESSOR \ - template <typename I> \ - I *nativeInterface() const \ +#define QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(T) \ + template <typename NativeInterface, typename TypeInfo = QNativeInterface::Private::NativeInterface<NativeInterface>, \ + typename BaseType = T, std::enable_if_t<TypeInfo::template isCompatibleWith<T>, bool> = true> \ + NativeInterface *nativeInterface() const \ { \ - using T = std::decay_t<decltype(*this)>; \ - using NativeInterface = QNativeInterface::Private::TypeInfo<I>; \ - static_assert(NativeInterface::template isCompatibleWith<T>, \ - "T::nativeInterface<I>() requires that native interface I is compatible with T"); \ - \ - return static_cast<I*>(QNativeInterface::Private::resolveInterface(this, \ - NativeInterface::name(), NativeInterface::revision())); \ + return static_cast<NativeInterface*>( \ + QNativeInterface::Private::resolveInterface(this, \ + TypeInfo::name(), TypeInfo::revision())); \ } // Provides a definition for the interface destructor @@ -134,6 +206,8 @@ namespace QNativeInterface::Private { revision, TypeInfo<NativeInterface>::revision(), name); \ return nullptr; \ } \ + } else { \ + qCDebug(lcNativeInterface, "No match for requested interface name %s", name); \ } QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h index cbb9d12040..873c0acf1f 100644 --- a/src/corelib/kernel/qcoreapplication.h +++ b/src/corelib/kernel/qcoreapplication.h @@ -164,7 +164,7 @@ public: const char * disambiguation = nullptr, int n = -1); - QT_DECLARE_NATIVE_INTERFACE_ACCESSOR + QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QCoreApplication) #ifndef QT_NO_QOBJECT #if QT_CONFIG(future) diff --git a/src/gui/kernel/qguiapplication.h b/src/gui/kernel/qguiapplication.h index ba3d464bc8..795a88f2b0 100644 --- a/src/gui/kernel/qguiapplication.h +++ b/src/gui/kernel/qguiapplication.h @@ -173,7 +173,7 @@ public: bool isSavingSession() const; #endif - QT_DECLARE_NATIVE_INTERFACE_ACCESSOR + QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QGuiApplication) static void sync(); Q_SIGNALS: diff --git a/src/gui/kernel/qkeymapper_p.h b/src/gui/kernel/qkeymapper_p.h index 9f061a9eb7..0297a69e28 100644 --- a/src/gui/kernel/qkeymapper_p.h +++ b/src/gui/kernel/qkeymapper_p.h @@ -73,7 +73,7 @@ public: static void changeKeyboard(); static QList<int> possibleKeys(QKeyEvent *e); - QT_DECLARE_NATIVE_INTERFACE_ACCESSOR + QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QKeyMapper) private: friend QKeyMapperPrivate *qt_keymapper_private(); diff --git a/src/gui/kernel/qoffscreensurface.h b/src/gui/kernel/qoffscreensurface.h index e63c85cb72..6ed063b8c9 100644 --- a/src/gui/kernel/qoffscreensurface.h +++ b/src/gui/kernel/qoffscreensurface.h @@ -80,7 +80,7 @@ public: QPlatformOffscreenSurface *handle() const; - QT_DECLARE_NATIVE_INTERFACE_ACCESSOR + QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QOffscreenSurface) Q_SIGNALS: void screenChanged(QScreen *screen); diff --git a/src/gui/kernel/qopenglcontext.h b/src/gui/kernel/qopenglcontext.h index 4d8c568ed5..b258fe0b7b 100644 --- a/src/gui/kernel/qopenglcontext.h +++ b/src/gui/kernel/qopenglcontext.h @@ -154,7 +154,7 @@ public: static bool supportsThreadedOpenGL(); static QOpenGLContext *globalShareContext(); - QT_DECLARE_NATIVE_INTERFACE_ACCESSOR + QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QOpenGLContext) Q_SIGNALS: void aboutToBeDestroyed(); diff --git a/src/gui/kernel/qscreen.h b/src/gui/kernel/qscreen.h index 33778786e5..8c2f0cf10f 100644 --- a/src/gui/kernel/qscreen.h +++ b/src/gui/kernel/qscreen.h @@ -153,7 +153,7 @@ public: qreal refreshRate() const; - QT_DECLARE_NATIVE_INTERFACE_ACCESSOR + QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QScreen) Q_SIGNALS: void geometryChanged(const QRect &geometry); diff --git a/src/gui/kernel/qwindow.h b/src/gui/kernel/qwindow.h index d20694f625..102a324366 100644 --- a/src/gui/kernel/qwindow.h +++ b/src/gui/kernel/qwindow.h @@ -289,7 +289,7 @@ public: QVulkanInstance *vulkanInstance() const; #endif - QT_DECLARE_NATIVE_INTERFACE_ACCESSOR + QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QWindow) public Q_SLOTS: Q_REVISION(2, 1) void requestActivate(); diff --git a/src/widgets/kernel/qapplication.h b/src/widgets/kernel/qapplication.h index 036526a407..97e07bd17a 100644 --- a/src/widgets/kernel/qapplication.h +++ b/src/widgets/kernel/qapplication.h @@ -154,7 +154,7 @@ public: static Qt::NavigationMode navigationMode(); #endif - QT_DECLARE_NATIVE_INTERFACE_ACCESSOR + QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(QApplication) Q_SIGNALS: void focusChanged(QWidget *old, QWidget *now); diff --git a/tests/auto/corelib/global/qnativeinterface/tst_qnativeinterface.cpp b/tests/auto/corelib/global/qnativeinterface/tst_qnativeinterface.cpp index 0da3108817..dfd8d4fca1 100644 --- a/tests/auto/corelib/global/qnativeinterface/tst_qnativeinterface.cpp +++ b/tests/auto/corelib/global/qnativeinterface/tst_qnativeinterface.cpp @@ -44,7 +44,7 @@ struct InterfaceImplementation; struct PublicClass { PublicClass(); - QT_DECLARE_NATIVE_INTERFACE_ACCESSOR + QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(PublicClass) std::unique_ptr<InterfaceImplementation> m_implementation; }; @@ -66,6 +66,15 @@ QT_DEFINE_NATIVE_INTERFACE(Interface); QT_DEFINE_NATIVE_INTERFACE(OtherInterface); QT_END_NAMESPACE +struct NotInterface {}; + +struct AlmostInterface +{ + struct TypeInfo { + // Missing required members + }; +}; + using namespace QNativeInterface; struct InterfaceImplementation : public Interface @@ -89,6 +98,10 @@ void tst_QNativeInterface::typeInfo() const { using namespace QNativeInterface::Private; + QCOMPARE(TypeInfo<Interface>::haveTypeInfo, true); + QCOMPARE(TypeInfo<NotInterface>::haveTypeInfo, false); + QCOMPARE(TypeInfo<AlmostInterface>::haveTypeInfo, false); + QCOMPARE(TypeInfo<Interface>::isCompatibleWith<PublicClass>, true); QCOMPARE(TypeInfo<Interface>::isCompatibleWith<QObject>, false); QCOMPARE(TypeInfo<Interface>::isCompatibleWith<int>, false); |