1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QNATIVEINTERFACE_H
5#define QNATIVEINTERFACE_H
6
7#include <QtCore/qglobal.h>
8
9QT_BEGIN_NAMESPACE
10
11// We declare a virtual non-inline function in the form
12// of the destructor, making it the key function. This
13// ensures that the typeinfo of the class is exported.
14// By being protected, we also ensure that pointers to
15// the interface can't be deleted.
16#define QT_DECLARE_NATIVE_INTERFACE_3(NativeInterface, Revision, BaseType) \
17 protected: \
18 virtual ~NativeInterface(); \
19 \
20 struct TypeInfo { \
21 using baseType = BaseType; \
22 static constexpr char const *name = QT_STRINGIFY(NativeInterface); \
23 static constexpr int revision = Revision; \
24 }; \
25 \
26 template <typename, typename> \
27 friend struct QNativeInterface::Private::has_type_info; \
28 \
29 template <typename> \
30 friend bool constexpr QNativeInterface::Private::hasTypeInfo(); \
31 \
32 template <typename> \
33 friend struct QNativeInterface::Private::TypeInfo; \
34 public: \
35 NativeInterface() = default; \
36 Q_DISABLE_COPY_MOVE(NativeInterface)
37
38// Revisioned interfaces only make sense when exposed through a base
39// type via QT_DECLARE_NATIVE_INTERFACE_ACCESSOR, as the revision
40// checks happen at that level (and not for normal dynamic_casts).
41#define QT_DECLARE_NATIVE_INTERFACE_2(NativeInterface, Revision) \
42 static_assert(false, "Must provide a base type when specifying revision");
43
44#define QT_DECLARE_NATIVE_INTERFACE_1(NativeInterface) \
45 QT_DECLARE_NATIVE_INTERFACE_3(NativeInterface, 0, void)
46
47#define QT_DECLARE_NATIVE_INTERFACE(...) \
48 QT_OVERLOADED_MACRO(QT_DECLARE_NATIVE_INTERFACE, __VA_ARGS__)
49
50namespace QNativeInterface::Private {
51
52 // Basic type-trait to verify that a given native interface has
53 // all the required type information for us to evaluate it.
54 template <typename NativeInterface, typename = void>
55 struct has_type_info : std::false_type {};
56
57 // The type-trait is friended by TypeInfo, so that we can
58 // evaluate TypeInfo in the template arguments.
59 template <typename NativeInterface>
60 struct has_type_info<NativeInterface, std::void_t<
61 typename NativeInterface::TypeInfo,
62 typename NativeInterface::TypeInfo::baseType,
63 decltype(&NativeInterface::TypeInfo::name),
64 decltype(&NativeInterface::TypeInfo::revision)
65 >> : std::true_type {};
66
67 // We need to wrap the instantiation of has_type_info in a
68 // function friended by TypeInfo, otherwise MSVC will not
69 // let us evaluate TypeInfo in the template arguments.
70 template <typename NativeInterface>
71 bool constexpr hasTypeInfo()
72 {
73 return has_type_info<NativeInterface>::value;
74 }
75
76 template <typename NativeInterface>
77 struct TypeInfo
78 {
79 // To ensure SFINAE works for hasTypeInfo we can't use it in a constexpr
80 // variable that also includes an expression that relies on the type
81 // info. This helper variable is okey, as it it self contained.
82 static constexpr bool haveTypeInfo = hasTypeInfo<NativeInterface>();
83
84 // We can then use the helper variable in a constexpr condition in a
85 // function, which does not break SFINAE if haveTypeInfo is false.
86 template <typename BaseType>
87 static constexpr bool isCompatibleHelper()
88 {
89 if constexpr (haveTypeInfo)
90 return std::is_base_of<typename NativeInterface::TypeInfo::baseType, BaseType>::value;
91 else
92 return false;
93 }
94
95 // MSVC however doesn't like constexpr functions in enable_if_t conditions,
96 // so we need to wrap it yet again in a constexpr variable. This is fine,
97 // as all the SFINAE magic has been resolved at this point.
98 template <typename BaseType>
99 static constexpr bool isCompatibleWith = isCompatibleHelper<BaseType>();
100
101 // The revision and name accessors are not used in enable_if_t conditions,
102 // so we can leave them as constexpr functions. As this class template is
103 // friended by TypeInfo we can access the protected members of TypeInfo.
104 static constexpr int revision()
105 {
106 if constexpr (haveTypeInfo)
107 return NativeInterface::TypeInfo::revision;
108 else
109 return 0;
110 }
111
112 static constexpr char const *name()
113 {
114 if constexpr (haveTypeInfo)
115 return NativeInterface::TypeInfo::name;
116 else
117 return nullptr;
118 }
119 };
120
121 // Wrapper type to make the error message in case
122 // of incompatible interface types read better.
123 template <typename I>
124 struct NativeInterface : TypeInfo<I> {};
125} // QNativeInterface::Private
126
127// Declares an accessor for the native interface
128#ifdef Q_QDOC
129#define QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(T) \
130 template <typename QNativeInterface> \
131 QNativeInterface *nativeInterface() const;
132#else
133#define QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(T) \
134 template <typename NativeInterface, typename TypeInfo = QNativeInterface::Private::NativeInterface<NativeInterface>, \
135 typename BaseType = T, std::enable_if_t<TypeInfo::template isCompatibleWith<T>, bool> = true> \
136 NativeInterface *nativeInterface() const \
137 { \
138 return static_cast<NativeInterface*>(resolveInterface( \
139 TypeInfo::name(), TypeInfo::revision())); \
140 } \
141 protected: \
142 void *resolveInterface(const char *name, int revision) const; \
143 public:
144#endif
145
146QT_END_NAMESPACE
147
148#endif // QNATIVEINTERFACE_H
149

source code of qtbase/src/corelib/global/qnativeinterface.h