summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2021-07-13 23:52:13 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-07-16 00:24:22 +0000
commitc268afbd0a23d04653833cf6db474535277075cf (patch)
tree20bb8510fd3917b9604d14981eb81ce18e79625d
parent751ec5f2ffe87a52702f025756b74bd7e5ac0e07 (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.h108
-rw-r--r--src/corelib/kernel/qcoreapplication.h2
-rw-r--r--src/gui/kernel/qguiapplication.h2
-rw-r--r--src/gui/kernel/qkeymapper_p.h2
-rw-r--r--src/gui/kernel/qoffscreensurface.h2
-rw-r--r--src/gui/kernel/qopenglcontext.h2
-rw-r--r--src/gui/kernel/qscreen.h2
-rw-r--r--src/gui/kernel/qwindow.h2
-rw-r--r--src/widgets/kernel/qapplication.h2
-rw-r--r--tests/auto/corelib/global/qnativeinterface/tst_qnativeinterface.cpp15
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);