123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 | // 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__)namespaceQNativeInterface::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>boolconstexprhasTypeInfo(){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.staticconstexprbool 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>staticconstexprboolisCompatibleHelper(){ifconstexpr(haveTypeInfo)returnstd::is_base_of<typename NativeInterface::TypeInfo::baseType, BaseType>::value;elsereturn 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>staticconstexprbool 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.staticconstexprintrevision(){ifconstexpr(haveTypeInfo)returnNativeInterface::TypeInfo::revision;elsereturn0;}staticconstexprchar const*name(){ifconstexpr(haveTypeInfo)returnNativeInterface::TypeInfo::name;elsereturnnullptr;}};// 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
|