Revision control
Copy as Markdown
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#ifndef QQUASIVIRTUAL_IMPL_H
#define QQUASIVIRTUAL_IMPL_H
#if 0
#pragma qt_sync_skip_header_check
#pragma qt_sync_stop_processing
#endif
#include <QtCore/qscopeguard.h>
#include <QtCore/qassert.h>
#include <QtCore/qtclasshelpermacros.h>
#include <algorithm>
#include <QtCore/q20memory.h>
#include <type_traits>
#include <tuple>
#ifndef Q_QDOC
QT_BEGIN_NAMESPACE
namespace QtPrivate {
template <typename Applier, size_t ...Is>
bool applyIndexSwitch(size_t index, Applier&& applier, std::index_sequence<Is...>)
{
// Performance considerations:
// The folding expression used here represents the same logic as a sequence of
// linear if/else if/... statements. Experiments show that Clang, GCC, and MSVC
// optimize it to essentially the same bytecode as a normal C++ switch,
// ensuring O(1) lookup complexity.
constexpr bool boolResult = (std::is_same_v<
std::invoke_result_t<Applier, std::integral_constant<size_t, Is>>,
bool> && ...);
auto doApply = [&applier](auto i) {
if constexpr (boolResult)
return std::forward<Applier>(applier)(i);
else
return (std::forward<Applier>(applier)(i), true);
};
return ((Is == index && doApply(std::integral_constant<size_t, Is>{})) || ...);
}
template <size_t IndexCount, typename Applier>
bool applyIndexSwitch(size_t index, Applier&& applier)
{
return QtPrivate::applyIndexSwitch(index, std::forward<Applier>(applier),
std::make_index_sequence<IndexCount>());
}
template <typename Interface>
class QQuasiVirtualInterface
{
private:
template <typename Arg>
static constexpr bool passArgAsValue = sizeof(Arg) <= sizeof(size_t)
&& std::is_trivially_destructible_v<Arg>;
template <typename C = Interface> using Methods = typename C::template MethodTemplates<C>;
template <typename ...>
struct MethodImpl;
template <typename M, typename R, typename I, typename... Args>
struct MethodImpl<M, R, I, Args...>
{
static_assert(std::is_base_of_v<I, Interface>, "The method must belong to the interface");
using return_type = R;
using call_args = std::tuple<std::conditional_t<passArgAsValue<Args>, Args, Args&&>...>;
static constexpr size_t index()
{
return index(std::make_index_sequence<std::tuple_size_v<Methods<>>>());
}
private:
template <size_t Ix>
static constexpr bool matchesAt()
{
return std::is_base_of_v<M, std::tuple_element_t<Ix, Methods<>>>;
}
template <size_t... Is>
static constexpr size_t index(std::index_sequence<Is...>)
{
constexpr size_t matchesCount = (size_t(matchesAt<Is>()) + ...);
static_assert(matchesCount == 1, "Expected exactly one match");
return ((size_t(matchesAt<Is>()) * Is) + ...);
}
static R invoke(I &intf /*const validation*/, Args... args)
{
Q_ASSERT(intf.m_callFN);
auto& baseIntf = static_cast<base_interface&>(const_cast<std::remove_const_t<I>&>(intf));
call_args callArgs(std::forward<Args>(args)...);
if constexpr (std::is_void_v<R>) {
intf.m_callFN(index(), baseIntf, nullptr, &callArgs);
} else {
alignas(R) std::byte buf[sizeof(R)];
intf.m_callFN(index(), baseIntf, buf, &callArgs);
R* result = std::launder(reinterpret_cast<R*>(buf));
QScopeGuard destroyBuffer([result]() { std::destroy_at(result); });
return std::forward<R>(*result);
}
}
friend class QQuasiVirtualInterface<Interface>;
};
template <typename M, typename R, typename I, typename... Args>
struct MethodImpl<M, R(I::*)(Args...)> : MethodImpl<M, R, I, Args...> {
template <typename Subclass>
using Overridden = R(Subclass::*)(Args...);
};
template <typename M, typename R, typename I, typename... Args>
struct MethodImpl<M, R(I::*)(Args...) const> : MethodImpl<M, R, const I, Args...> {
template <typename Subclass>
using Overridden = R(Subclass::*)(Args...) const;
};
public:
template <auto prototype>
struct Method : MethodImpl<Method<prototype>, decltype(prototype)> {};
template <typename Method, typename... Args>
auto call(Args &&... args) const
{
return Method::invoke(static_cast<const Interface &>(*this), std::forward<Args>(args)...);
}
template <typename Method, typename... Args>
auto call(Args &&... args)
{
return Method::invoke(static_cast<Interface &>(*this), std::forward<Args>(args)...);
}
void destroy(); // quasi-virtual pure destructor
using Destroy = Method<&QQuasiVirtualInterface::destroy>;
struct Deleter
{
void operator () (QQuasiVirtualInterface* self) const { self->template call<Destroy>(); }
};
protected:
using base_interface = QQuasiVirtualInterface<Interface>;
using CallFN = void (*)(size_t index, base_interface &intf, void *ret, void *args);
void initCallFN(CallFN func) { m_callFN = func; }
QQuasiVirtualInterface() = default;
~QQuasiVirtualInterface() = default;
private:
Q_DISABLE_COPY_MOVE(QQuasiVirtualInterface)
CallFN m_callFN = nullptr;
};
template <typename Subclass, typename Interface>
class QQuasiVirtualSubclass : public Interface
{
private:
template <typename C = Subclass> using Methods = typename C::template MethodTemplates<C>;
template <size_t OverriddenIndex>
static constexpr size_t interfaceMethodIndex() {
return std::tuple_element_t<OverriddenIndex, Methods<>>::index();
}
template <size_t... Is>
static void callImpl(size_t index, Subclass &subclass, void *ret, void *args, std::index_sequence<Is...>)
{
constexpr auto methodIndexMask = []() {
std::array<bool, sizeof...(Is)> result = {};
(static_cast<void>(std::get<interfaceMethodIndex<Is>()>(result) = true), ...);
return result;
}();
static_assert((methodIndexMask[Is] && ...),
"Mapping between base and overridden methods is not unique");
auto doInvoke = [&](auto idxConstant) {
std::tuple_element_t<idxConstant.value, Methods<>>::doInvoke(subclass, ret, args);
};
QtPrivate::applyIndexSwitch(index, doInvoke,
std::index_sequence<interfaceMethodIndex<Is>()...>{});
}
static void callImpl(size_t index, typename Interface::base_interface &intf, void *ret, void *args)
{
constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Methods<>>>();
callImpl(index, static_cast<Subclass&>(intf), ret, args, seq);
}
template <typename BaseMethod>
using OverridenSignature = typename BaseMethod::template Overridden<Subclass>;
protected:
template <typename... Args>
QQuasiVirtualSubclass(Args &&... args)
: Interface(std::forward<Args>(args)...)
{
Interface::initCallFN(&QQuasiVirtualSubclass::callImpl);
}
public:
template <typename BaseMethod, OverridenSignature<BaseMethod> overridden>
struct Override : BaseMethod
{
private:
static constexpr void doInvoke(Subclass &subclass, void *ret, void *args)
{
using Return = typename BaseMethod::return_type;
using PackedArgs = typename BaseMethod::call_args;
Q_ASSERT(args);
Q_ASSERT(std::is_void_v<Return> == !ret);
auto invoke = [&subclass](auto &&...params)
{
return std::invoke(overridden, &subclass, std::forward<decltype(params)>(params)...);
};
if constexpr (std::is_void_v<Return>) {
std::apply(invoke, std::move(*static_cast<PackedArgs *>(args)));
} else {
q20::construct_at(static_cast<Return *>(ret),
std::apply(invoke, std::move(*static_cast<PackedArgs *>(args))));
}
}
friend class QQuasiVirtualSubclass<Subclass, Interface>;
};
};
} // namespace QtPrivate
QT_END_NAMESPACE
#endif // Q_DOC
#endif // QQUASIVIRTUAL_IMPL_H