summaryrefslogtreecommitdiffstats
path: root/src/corelib/global/qnativeinterface.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/global/qnativeinterface.h')
-rw-r--r--src/corelib/global/qnativeinterface.h148
1 files changed, 148 insertions, 0 deletions
diff --git a/src/corelib/global/qnativeinterface.h b/src/corelib/global/qnativeinterface.h
new file mode 100644
index 0000000000..89f33651c5
--- /dev/null
+++ b/src/corelib/global/qnativeinterface.h
@@ -0,0 +1,148 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNATIVEINTERFACE_H
+#define QNATIVEINTERFACE_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+// We declare a virtual non-inline function in the form
+// of the destructor, making it the key function. This
+// ensures that the typeinfo of the class is exported.
+// By being protected, we also ensure that pointers to
+// the interface can't be deleted.
+#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: \
+ NativeInterface() = default; \
+ Q_DISABLE_COPY_MOVE(NativeInterface)
+
+// Revisioned interfaces only make sense when exposed through a base
+// type via QT_DECLARE_NATIVE_INTERFACE_ACCESSOR, as the revision
+// checks happen at that level (and not for normal dynamic_casts).
+#define QT_DECLARE_NATIVE_INTERFACE_2(NativeInterface, Revision) \
+ static_assert(false, "Must provide a base type when specifying revision");
+
+#define QT_DECLARE_NATIVE_INTERFACE_1(NativeInterface) \
+ QT_DECLARE_NATIVE_INTERFACE_3(NativeInterface, 0, void)
+
+#define QT_DECLARE_NATIVE_INTERFACE(...) \
+ 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>
+ bool constexpr hasTypeInfo()
+ {
+ return has_type_info<NativeInterface>::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> {};
+} // QNativeInterface::Private
+
+// Declares an accessor for the native interface
+#ifdef Q_QDOC
+#define QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(T) \
+ template <typename QNativeInterface> \
+ QNativeInterface *nativeInterface() const;
+#else
+#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 \
+ { \
+ return static_cast<NativeInterface*>(resolveInterface( \
+ TypeInfo::name(), TypeInfo::revision())); \
+ } \
+ protected: \
+ void *resolveInterface(const char *name, int revision) const; \
+ public:
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QNATIVEINTERFACE_H