Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-02 09:27:22

0001 // Copyright (C) 2025 The Qt Company Ltd.
0002 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
0003 // Qt-Security score:significant reason:default
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     // Performance considerations:
0032     // The folding expression used here represents the same logic as a sequence of
0033     // linear if/else if/... statements. Experiments show that Clang, GCC, and MSVC
0034     // optimize it to essentially the same bytecode as a normal C++ switch,
0035     // ensuring O(1) lookup complexity.
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 /*const validation*/, 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(); // quasi-virtual pure destructor
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 } // namespace QtPrivate
0233 
0234 QT_END_NAMESPACE
0235 
0236 #endif // Q_DOC
0237 
0238 #endif // QQUASIVIRTUAL_IMPL_H