File indexing completed on 2026-08-02 09:27:22
0001
0002
0003
0004
0005 #ifndef QQUASIVIRTUAL_IMPL_H
0006 #define QQUASIVIRTUAL_IMPL_H
0007
0008 #if 0
0009 #pragma qt_sync_skip_header_check
0010 #pragma qt_sync_stop_processing
0011 #endif
0012
0013 #include <QtCore/qscopeguard.h>
0014 #include <QtCore/qassert.h>
0015 #include <QtCore/qtclasshelpermacros.h>
0016
0017 #include <algorithm>
0018 #include <QtCore/q20memory.h>
0019 #include <type_traits>
0020 #include <tuple>
0021
0022 #ifndef Q_QDOC
0023
0024 QT_BEGIN_NAMESPACE
0025
0026 namespace QtPrivate {
0027
0028 template <typename Applier, size_t ...Is>
0029 void applyIndexSwitch(size_t index, Applier&& applier, std::index_sequence<Is...>)
0030 {
0031
0032
0033
0034
0035
0036 static_cast<void>(((Is == index && (applier(std::integral_constant<size_t, Is>{}), true)) || ...));
0037 }
0038
0039 template <size_t IndexCount, typename Applier>
0040 void applyIndexSwitch(size_t index, Applier&& applier)
0041 {
0042 QtPrivate::applyIndexSwitch(index, std::forward<Applier>(applier),
0043 std::make_index_sequence<IndexCount>());
0044 }
0045
0046 template <typename Interface>
0047 class QQuasiVirtualInterface
0048 {
0049 private:
0050 template <typename Arg>
0051 static constexpr bool passArgAsValue = sizeof(Arg) <= sizeof(size_t)
0052 && std::is_trivially_destructible_v<Arg>;
0053
0054 template <typename C = Interface> using Methods = typename C::template MethodTemplates<C>;
0055
0056 template <typename ...>
0057 struct MethodImpl;
0058
0059 template <typename M, typename R, typename I, typename... Args>
0060 struct MethodImpl<M, R, I, Args...>
0061 {
0062 static_assert(std::is_base_of_v<I, Interface>, "The method must belong to the interface");
0063 using return_type = R;
0064 using call_args = std::tuple<std::conditional_t<passArgAsValue<Args>, Args, Args&&>...>;
0065
0066 static constexpr size_t index()
0067 {
0068 return index(std::make_index_sequence<std::tuple_size_v<Methods<>>>());
0069 }
0070
0071 private:
0072 template <size_t Ix>
0073 static constexpr bool matchesAt()
0074 {
0075 return std::is_base_of_v<M, std::tuple_element_t<Ix, Methods<>>>;
0076 }
0077
0078 template <size_t... Is>
0079 static constexpr size_t index(std::index_sequence<Is...>)
0080 {
0081 constexpr size_t matchesCount = (size_t(matchesAt<Is>()) + ...);
0082 static_assert(matchesCount == 1, "Expected exactly one match");
0083 return ((size_t(matchesAt<Is>()) * Is) + ...);
0084 }
0085
0086 static R invoke(I &intf , Args... args)
0087 {
0088 Q_ASSERT(intf.m_callFN);
0089
0090 auto& baseIntf = static_cast<base_interface&>(const_cast<std::remove_const_t<I>&>(intf));
0091 call_args callArgs(std::forward<Args>(args)...);
0092 if constexpr (std::is_void_v<R>) {
0093 intf.m_callFN(index(), baseIntf, nullptr, &callArgs);
0094 } else {
0095 alignas(R) std::byte buf[sizeof(R)];
0096 intf.m_callFN(index(), baseIntf, buf, &callArgs);
0097
0098 R* result = std::launder(reinterpret_cast<R*>(buf));
0099 QScopeGuard destroyBuffer([result]() { std::destroy_at(result); });
0100 return std::forward<R>(*result);
0101 }
0102 }
0103
0104 friend class QQuasiVirtualInterface<Interface>;
0105 };
0106
0107 template <typename M, typename R, typename I, typename... Args>
0108 struct MethodImpl<M, R(I::*)(Args...)> : MethodImpl<M, R, I, Args...> {
0109 template <typename Subclass>
0110 using Overridden = R(Subclass::*)(Args...);
0111 };
0112
0113 template <typename M, typename R, typename I, typename... Args>
0114 struct MethodImpl<M, R(I::*)(Args...) const> : MethodImpl<M, R, const I, Args...> {
0115 template <typename Subclass>
0116 using Overridden = R(Subclass::*)(Args...) const;
0117 };
0118
0119 public:
0120 template <auto prototype>
0121 struct Method : MethodImpl<Method<prototype>, decltype(prototype)> {};
0122
0123 template <typename Method, typename... Args>
0124 auto call(Args &&... args) const
0125 {
0126 return Method::invoke(static_cast<const Interface &>(*this), std::forward<Args>(args)...);
0127 }
0128
0129 template <typename Method, typename... Args>
0130 auto call(Args &&... args)
0131 {
0132 return Method::invoke(static_cast<Interface &>(*this), std::forward<Args>(args)...);
0133 }
0134
0135 void destroy();
0136 using Destroy = Method<&QQuasiVirtualInterface::destroy>;
0137
0138 struct Deleter
0139 {
0140 void operator () (QQuasiVirtualInterface* self) const { self->template call<Destroy>(); }
0141 };
0142
0143 protected:
0144 using base_interface = QQuasiVirtualInterface<Interface>;
0145 using CallFN = void (*)(size_t index, base_interface &intf, void *ret, void *args);
0146 void initCallFN(CallFN func) { m_callFN = func; }
0147
0148 QQuasiVirtualInterface() = default;
0149 ~QQuasiVirtualInterface() = default;
0150
0151 private:
0152 Q_DISABLE_COPY_MOVE(QQuasiVirtualInterface)
0153 CallFN m_callFN = nullptr;
0154 };
0155
0156 template <typename Subclass, typename Interface>
0157 class QQuasiVirtualSubclass : public Interface
0158 {
0159 private:
0160 template <typename C = Subclass> using Methods = typename C::template MethodTemplates<C>;
0161
0162 template <size_t OverriddenIndex>
0163 static constexpr size_t interfaceMethodIndex() {
0164 return std::tuple_element_t<OverriddenIndex, Methods<>>::index();
0165 }
0166
0167 template <size_t... Is>
0168 static void callImpl(size_t index, Subclass &subclass, void *ret, void *args, std::index_sequence<Is...>)
0169 {
0170 constexpr auto methodIndexMask = []() {
0171 std::array<bool, sizeof...(Is)> result = {};
0172 (static_cast<void>(std::get<interfaceMethodIndex<Is>()>(result) = true), ...);
0173 return result;
0174 }();
0175 static_assert((methodIndexMask[Is] && ...),
0176 "Mapping between base and overridden methods is not unique");
0177
0178 auto doInvoke = [&](auto idxConstant) {
0179 std::tuple_element_t<idxConstant.value, Methods<>>::doInvoke(subclass, ret, args);
0180 };
0181 QtPrivate::applyIndexSwitch(index, doInvoke,
0182 std::index_sequence<interfaceMethodIndex<Is>()...>{});
0183 }
0184
0185 static void callImpl(size_t index, typename Interface::base_interface &intf, void *ret, void *args)
0186 {
0187 constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Methods<>>>();
0188 callImpl(index, static_cast<Subclass&>(intf), ret, args, seq);
0189 }
0190
0191 template <typename BaseMethod>
0192 using OverridenSignature = typename BaseMethod::template Overridden<Subclass>;
0193
0194 protected:
0195 template <typename... Args>
0196 QQuasiVirtualSubclass(Args &&... args)
0197 : Interface(std::forward<Args>(args)...)
0198 {
0199 Interface::initCallFN(&QQuasiVirtualSubclass::callImpl);
0200 }
0201
0202 public:
0203 template <typename BaseMethod, OverridenSignature<BaseMethod> overridden>
0204 struct Override : BaseMethod
0205 {
0206 private:
0207 static constexpr void doInvoke(Subclass &subclass, void *ret, void *args)
0208 {
0209 using Return = typename BaseMethod::return_type;
0210 using PackedArgs = typename BaseMethod::call_args;
0211
0212 Q_ASSERT(args);
0213 Q_ASSERT(std::is_void_v<Return> == !ret);
0214
0215 auto invoke = [&subclass](auto &&...params)
0216 {
0217 return std::invoke(overridden, &subclass, std::forward<decltype(params)>(params)...);
0218 };
0219
0220 if constexpr (std::is_void_v<Return>) {
0221 std::apply(invoke, std::move(*static_cast<PackedArgs *>(args)));
0222 } else {
0223 q20::construct_at(static_cast<Return *>(ret),
0224 std::apply(invoke, std::move(*static_cast<PackedArgs *>(args))));
0225 }
0226 }
0227
0228 friend class QQuasiVirtualSubclass<Subclass, Interface>;
0229 };
0230 };
0231
0232 }
0233
0234 QT_END_NAMESPACE
0235
0236 #endif
0237
0238 #endif