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 | |
9 | QT_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 | |
50 | namespace 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 | |
146 | QT_END_NAMESPACE |
147 | |
148 | #endif // QNATIVEINTERFACE_H |
149 | |