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>
29void 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 static_cast<void>(((Is == index ? (applier(std::integral_constant<size_t, Is>{}), true) : false)
37 || ...));
38}
39
40template <size_t IndexCount, typename Applier>
41void applyIndexSwitch(size_t index, Applier&& applier)
42{
43 QtPrivate::applyIndexSwitch(index, std::forward<Applier>(applier),
44 std::make_index_sequence<IndexCount>());
45}
46
47template <typename Interface>
49{
50private:
51 template <typename Arg>
52 static constexpr bool passArgAsValue = sizeof(Arg) <= sizeof(size_t)
54
55 template <typename C = Interface> using Methods = typename C::template MethodTemplates<C>;
56
57 template <typename ...>
58 struct MethodImpl;
59
60 template <typename M, typename R, typename I, typename... Args>
61 struct MethodImpl<M, R, I, Args...>
62 {
63 static_assert(std::is_base_of_v<I, Interface>, "The method must belong to the interface");
64 using return_type = R;
65 using call_args = std::tuple<std::conditional_t<passArgAsValue<Args>, Args, Args&&>...>;
66
67 static constexpr size_t index()
68 {
69 return index(std::make_index_sequence<std::tuple_size_v<Methods<>>>());
70 }
71
72 private:
73 template <size_t Ix>
74 static constexpr bool matchesAt()
75 {
76 return std::is_base_of_v<M, std::tuple_element_t<Ix, Methods<>>>;
77 }
78
79 template <size_t... Is>
80 static constexpr size_t index(std::index_sequence<Is...>)
81 {
82 constexpr size_t matchesCount = (size_t(matchesAt<Is>()) + ...);
83 static_assert(matchesCount == 1, "Expected exactly one match");
84 return ((size_t(matchesAt<Is>()) * Is) + ...);
85 }
86
87 static R invoke(I &intf /*const validation*/, Args... args)
88 {
89 Q_ASSERT(intf.m_callFN);
90
91 auto& baseIntf = static_cast<base_interface&>(const_cast<std::remove_const_t<I>&>(intf));
92 call_args callArgs(std::forward<Args>(args)...);
93 if constexpr (std::is_void_v<R>) {
94 intf.m_callFN(index(), baseIntf, nullptr, &callArgs);
95 } else {
96 alignas(R) std::byte buf[sizeof(R)];
97 intf.m_callFN(index(), baseIntf, buf, &callArgs);
98
99 R* result = std::launder(reinterpret_cast<R*>(buf));
100 QScopeGuard destroyBuffer([result]() { std::destroy_at(result); });
101 return std::forward<R>(*result);
102 }
103 }
104
105 friend class QQuasiVirtualInterface<Interface>;
106 };
107
108 template <typename M, typename R, typename I, typename... Args>
109 struct MethodImpl<M, R(I::*)(Args...)> : MethodImpl<M, R, I, Args...> {
110 template <typename Subclass>
111 using Overridden = R(Subclass::*)(Args...);
112 };
113
114 template <typename M, typename R, typename I, typename... Args>
115 struct MethodImpl<M, R(I::*)(Args...) const> : MethodImpl<M, R, const I, Args...> {
116 template <typename Subclass>
117 using Overridden = R(Subclass::*)(Args...) const;
118 };
119
120public:
121 template <auto prototype>
122 struct Method : MethodImpl<Method<prototype>, decltype(prototype)> {};
123
124 template <typename Method, typename... Args>
125 auto call(Args &&... args) const
126 {
127 return Method::invoke(static_cast<const Interface &>(*this), std::forward<Args>(args)...);
128 }
129
130 template <typename Method, typename... Args>
131 auto call(Args &&... args)
132 {
133 return Method::invoke(static_cast<Interface &>(*this), std::forward<Args>(args)...);
134 }
135
136 void destroy(); // quasi-virtual pure destructor
138
139 struct Deleter
140 {
141 void operator () (QQuasiVirtualInterface* self) const { self->template call<Destroy>(); }
142 };
143
144protected:
146 using CallFN = void (*)(size_t index, base_interface &intf, void *ret, void *args);
147 void initCallFN(CallFN func) { m_callFN = func; }
148
151
152private:
154 CallFN m_callFN = nullptr;
155};
156
157template <typename Subclass, typename Interface>
158class QQuasiVirtualSubclass : public Interface
159{
160private:
161 template <typename C = Subclass> using Methods = typename C::template MethodTemplates<C>;
162
163 template <size_t OverriddenIndex>
164 static constexpr size_t interfaceMethodIndex() {
165 return std::tuple_element_t<OverriddenIndex, Methods<>>::index();
166 }
167
168 template <size_t... Is>
169 static void callImpl(size_t index, Subclass &subclass, void *ret, void *args, std::index_sequence<Is...>)
170 {
171 constexpr auto methodIndexMask = []() {
172 std::array<bool, sizeof...(Is)> result = {};
173 (static_cast<void>(std::get<interfaceMethodIndex<Is>()>(result) = true), ...);
174 return result;
175 }();
176 static_assert((methodIndexMask[Is] && ...),
177 "Mapping between base and overridden methods is not unique");
178
179 auto doInvoke = [&](auto idxConstant) {
180 std::tuple_element_t<idxConstant.value, Methods<>>::doInvoke(subclass, ret, args);
181 };
182 QtPrivate::applyIndexSwitch(index, doInvoke,
183 std::index_sequence<interfaceMethodIndex<Is>()...>{});
184 }
185
186 static void callImpl(size_t index, typename Interface::base_interface &intf, void *ret, void *args)
187 {
188 constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Methods<>>>();
189 callImpl(index, static_cast<Subclass&>(intf), ret, args, seq);
190 }
191
192 template <typename BaseMethod>
193 using OverridenSignature = typename BaseMethod::template Overridden<Subclass>;
194
195protected:
196 template <typename... Args>
197 QQuasiVirtualSubclass(Args &&... args)
198 : Interface(std::forward<Args>(args)...)
199 {
200 Interface::initCallFN(&QQuasiVirtualSubclass::callImpl);
201 }
202
203public:
204 template <typename BaseMethod, OverridenSignature<BaseMethod> overridden>
205 struct Override : BaseMethod
206 {
207 private:
208 static constexpr void doInvoke(Subclass &subclass, void *ret, void *args)
209 {
210 using Return = typename BaseMethod::return_type;
211 using PackedArgs = typename BaseMethod::call_args;
212
213 Q_ASSERT(args);
214 Q_ASSERT(std::is_void_v<Return> == !ret);
215
216 auto invoke = [&subclass](auto &&...params)
217 {
218 return std::invoke(overridden, &subclass, std::forward<decltype(params)>(params)...);
219 };
220
221 if constexpr (std::is_void_v<Return>) {
222 std::apply(invoke, std::move(*static_cast<PackedArgs *>(args)));
223 } else {
224 q20::construct_at(static_cast<Return *>(ret),
225 std::apply(invoke, std::move(*static_cast<PackedArgs *>(args))));
226 }
227 }
228
229 friend class QQuasiVirtualSubclass<Subclass, Interface>;
230 };
231};
232
233} // namespace QtPrivate
234
235QT_END_NAMESPACE
236
237#endif // Q_DOC
238
239#endif // QQUASIVIRTUAL_IMPL_H
Method<&QQuasiVirtualInterface::destroy > Destroy
auto call(Args &&... args) const
QQuasiVirtualInterface< Interface > base_interface
void(*)(size_t index, base_interface &intf, void *ret, void *args) CallFN
void applyIndexSwitch(size_t index, Applier &&applier, std::index_sequence< Is... >)
void applyIndexSwitch(size_t index, Applier &&applier)
void operator()(QQuasiVirtualInterface *self) const