Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquasivirtual_impl.h
Go to the documentation of this file.
1// Copyright (C) 2025 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// Qt-Security score:significant reason:default
4
5#ifndef QQUASIVIRTUAL_IMPL_H
6#define QQUASIVIRTUAL_IMPL_H
7
8#if 0
9#pragma qt_sync_skip_header_check
10#pragma qt_sync_stop_processing
11#endif
12
13#include <QtCore/qscopeguard.h>
14#include <QtCore/qassert.h>
15#include <QtCore/qtclasshelpermacros.h>
16
17#include <algorithm>
18#include <QtCore/q20memory.h>
19#include <type_traits>
20#include <tuple>
21
22#ifndef Q_QDOC
23
24QT_BEGIN_NAMESPACE
25
26namespace QtPrivate {
27
28template <typename Applier, size_t ...Is>
29bool applyIndexSwitch(size_t index, Applier&& applier, std::index_sequence<Is...>)
30{
31 // Performance considerations:
32 // The folding expression used here represents the same logic as a sequence of
33 // linear if/else if/... statements. Experiments show that Clang, GCC, and MSVC
34 // optimize it to essentially the same bytecode as a normal C++ switch,
35 // ensuring O(1) lookup complexity.
36 constexpr bool boolResult = (std::is_same_v<
37 std::invoke_result_t<Applier, std::integral_constant<size_t, Is>>,
38 bool> && ...);
39 auto doApply = [&applier](auto i) {
40 if constexpr (boolResult)
41 return std::forward<Applier>(applier)(i);
42 else
43 return (std::forward<Applier>(applier)(i), true);
44 };
45
46 return ((Is == index && doApply(std::integral_constant<size_t, Is>{})) || ...);
47}
48
49template <size_t IndexCount, typename Applier>
50bool applyIndexSwitch(size_t index, Applier&& applier)
51{
52 return QtPrivate::applyIndexSwitch(index, std::forward<Applier>(applier),
53 std::make_index_sequence<IndexCount>());
54}
55
56template <typename Interface>
58{
59private:
60 template <typename Arg>
61 static constexpr bool passArgAsValue = sizeof(Arg) <= sizeof(size_t)
63
64 template <typename C = Interface> using Methods = typename C::template MethodTemplates<C>;
65
66 template <typename ...>
67 struct MethodImpl;
68
69 template <typename M, typename R, typename I, typename... Args>
70 struct MethodImpl<M, R, I, Args...>
71 {
72 static_assert(std::is_base_of_v<I, Interface>, "The method must belong to the interface");
73 using return_type = R;
74 using call_args = std::tuple<std::conditional_t<passArgAsValue<Args>, Args, Args&&>...>;
75
76 static constexpr size_t index()
77 {
78 return index(std::make_index_sequence<std::tuple_size_v<Methods<>>>());
79 }
80
81 private:
82 template <size_t Ix>
83 static constexpr bool matchesAt()
84 {
85 return std::is_base_of_v<M, std::tuple_element_t<Ix, Methods<>>>;
86 }
87
88 template <size_t... Is>
89 static constexpr size_t index(std::index_sequence<Is...>)
90 {
91 constexpr size_t matchesCount = (size_t(matchesAt<Is>()) + ...);
92 static_assert(matchesCount == 1, "Expected exactly one match");
93 return ((size_t(matchesAt<Is>()) * Is) + ...);
94 }
95
96 static R invoke(I &intf /*const validation*/, Args... args)
97 {
98 Q_ASSERT(intf.m_callFN);
99
100 auto& baseIntf = static_cast<base_interface&>(const_cast<std::remove_const_t<I>&>(intf));
101 call_args callArgs(std::forward<Args>(args)...);
102 if constexpr (std::is_void_v<R>) {
103 intf.m_callFN(index(), baseIntf, nullptr, &callArgs);
104 } else {
105 alignas(R) std::byte buf[sizeof(R)];
106 intf.m_callFN(index(), baseIntf, buf, &callArgs);
107
108 R* result = std::launder(reinterpret_cast<R*>(buf));
109 QScopeGuard destroyBuffer([result]() { std::destroy_at(result); });
110 return std::forward<R>(*result);
111 }
112 }
113
114 friend class QQuasiVirtualInterface<Interface>;
115 };
116
117 template <typename M, typename R, typename I, typename... Args>
118 struct MethodImpl<M, R(I::*)(Args...)> : MethodImpl<M, R, I, Args...> {
119 template <typename Subclass>
120 using Overridden = R(Subclass::*)(Args...);
121 };
122
123 template <typename M, typename R, typename I, typename... Args>
124 struct MethodImpl<M, R(I::*)(Args...) const> : MethodImpl<M, R, const I, Args...> {
125 template <typename Subclass>
126 using Overridden = R(Subclass::*)(Args...) const;
127 };
128
129public:
130 template <auto prototype>
131 struct Method : MethodImpl<Method<prototype>, decltype(prototype)> {};
132
133 template <typename Method, typename... Args>
134 auto call(Args &&... args) const
135 {
136 return Method::invoke(static_cast<const Interface &>(*this), std::forward<Args>(args)...);
137 }
138
139 template <typename Method, typename... Args>
140 auto call(Args &&... args)
141 {
142 return Method::invoke(static_cast<Interface &>(*this), std::forward<Args>(args)...);
143 }
144
145 void destroy(); // quasi-virtual pure destructor
147
148 struct Deleter
149 {
150 void operator () (QQuasiVirtualInterface* self) const { self->template call<Destroy>(); }
151 };
152
153protected:
155 using CallFN = void (*)(size_t index, base_interface &intf, void *ret, void *args);
156 void initCallFN(CallFN func) { m_callFN = func; }
157
160
161private:
163 CallFN m_callFN = nullptr;
164};
165
166template <typename Subclass, typename Interface>
167class QQuasiVirtualSubclass : public Interface
168{
169private:
170 template <typename C = Subclass> using Methods = typename C::template MethodTemplates<C>;
171
172 template <size_t OverriddenIndex>
173 static constexpr size_t interfaceMethodIndex() {
174 return std::tuple_element_t<OverriddenIndex, Methods<>>::index();
175 }
176
177 template <size_t... Is>
178 static void callImpl(size_t index, Subclass &subclass, void *ret, void *args, std::index_sequence<Is...>)
179 {
180 constexpr auto methodIndexMask = []() {
181 std::array<bool, sizeof...(Is)> result = {};
182 (static_cast<void>(std::get<interfaceMethodIndex<Is>()>(result) = true), ...);
183 return result;
184 }();
185 static_assert((methodIndexMask[Is] && ...),
186 "Mapping between base and overridden methods is not unique");
187
188 auto doInvoke = [&](auto idxConstant) {
189 std::tuple_element_t<idxConstant.value, Methods<>>::doInvoke(subclass, ret, args);
190 };
191 QtPrivate::applyIndexSwitch(index, doInvoke,
192 std::index_sequence<interfaceMethodIndex<Is>()...>{});
193 }
194
195 static void callImpl(size_t index, typename Interface::base_interface &intf, void *ret, void *args)
196 {
197 constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Methods<>>>();
198 callImpl(index, static_cast<Subclass&>(intf), ret, args, seq);
199 }
200
201 template <typename BaseMethod>
202 using OverridenSignature = typename BaseMethod::template Overridden<Subclass>;
203
204protected:
205 template <typename... Args>
206 QQuasiVirtualSubclass(Args &&... args)
207 : Interface(std::forward<Args>(args)...)
208 {
209 Interface::initCallFN(&QQuasiVirtualSubclass::callImpl);
210 }
211
212public:
213 template <typename BaseMethod, OverridenSignature<BaseMethod> overridden>
214 struct Override : BaseMethod
215 {
216 private:
217 static constexpr void doInvoke(Subclass &subclass, void *ret, void *args)
218 {
219 using Return = typename BaseMethod::return_type;
220 using PackedArgs = typename BaseMethod::call_args;
221
222 Q_ASSERT(args);
223 Q_ASSERT(std::is_void_v<Return> == !ret);
224
225 auto invoke = [&subclass](auto &&...params)
226 {
227 return std::invoke(overridden, &subclass, std::forward<decltype(params)>(params)...);
228 };
229
230 if constexpr (std::is_void_v<Return>) {
231 std::apply(invoke, std::move(*static_cast<PackedArgs *>(args)));
232 } else {
233 q20::construct_at(static_cast<Return *>(ret),
234 std::apply(invoke, std::move(*static_cast<PackedArgs *>(args))));
235 }
236 }
237
238 friend class QQuasiVirtualSubclass<Subclass, Interface>;
239 };
240};
241
242} // namespace QtPrivate
243
244QT_END_NAMESPACE
245
246#endif // Q_DOC
247
248#endif // QQUASIVIRTUAL_IMPL_H
auto call(Args &&... args) const
QQuasiVirtualInterface< Interface > base_interface
void(*)(size_t index, base_interface &intf, void *ret, void *args) CallFN
bool applyIndexSwitch(size_t index, Applier &&applier)
bool applyIndexSwitch(size_t index, Applier &&applier, std::index_sequence< Is... >)
void operator()(QQuasiVirtualInterface *self) const