diff options
Diffstat (limited to 'src/corelib/global/qnativeinterface.h')
-rw-r--r-- | src/corelib/global/qnativeinterface.h | 148 |
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 |