// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2013 Olivier Goffart // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QOBJECTDEFS_H #error Do not include qobjectdefs_impl.h directly #include #endif #if 0 #pragma qt_sync_skip_header_check #pragma qt_sync_stop_processing #endif #include #include QT_BEGIN_NAMESPACE class QObject; class QObjectPrivate; class QMetaMethod; class QByteArray; namespace QtPrivate { template struct RemoveRef { typedef T Type; }; template struct RemoveRef { typedef T Type; }; template struct RemoveConstRef { typedef T Type; }; template struct RemoveConstRef { typedef T Type; }; /* The following List classes are used to help to handle the list of arguments. It follow the same principles as the lisp lists. List_Left take a list and a number as a parameter and returns (via the Value typedef, the list composed of the first N element of the list */ // With variadic template, lists are represented using a variadic template argument instead of the lisp way template struct List { static constexpr size_t size = sizeof...(Ts); }; template struct SizeOfList { static constexpr size_t value = 1; }; template<> struct SizeOfList> { static constexpr size_t value = 0; }; template struct SizeOfList> { static constexpr size_t value = List::size; }; template struct List { static constexpr size_t size = 1 + sizeof...(Tail); typedef Head Car; typedef List Cdr; }; template struct List_Append; template struct List_Append, List> { typedef List Value; }; template struct List_Left { typedef typename List_Append,typename List_Left::Value>::Value Value; }; template struct List_Left { typedef List<> Value; }; /* This is used to store the return value from a slot, whether the caller wants to store this value (QMetaObject::invokeMethod() with qReturnArg() or non-void signal ) or not. */ struct FunctorCallBase { template static void call_internal(void **args, Lambda &&fn) noexcept(noexcept(fn())) { using SlotRet = decltype(fn()); if constexpr (std::is_void_v || std::is_void_v) { Q_UNUSED(args); } else { if (args[0]) { *reinterpret_cast(args[0]) = fn(); return; } } fn(); } }; /* The FunctionPointer struct is a type trait for function pointer. - ArgumentCount is the number of argument, or -1 if it is unknown - the Object typedef is the Object of a pointer to member function - the Arguments typedef is the list of argument (in a QtPrivate::List) - the Function typedef is an alias to the template parameter Func - the call(f,o,args) method is used to call that slot Args is the list of argument of the signal R is the return type of the signal f is the function pointer o is the receiver object and args is the array of pointer to arguments, as used in qt_metacall The Functor struct is the helper to call a functor of N argument. Its call function is the same as the FunctionPointer::call function. */ template using InvokeGenSeq = typename T::Type; template struct IndexesList { using Type = IndexesList; }; template struct ConcatSeqImpl; template struct ConcatSeqImpl, IndexesList> : IndexesList{}; template using ConcatSeq = InvokeGenSeq>; template struct GenSeq; template using makeIndexSequence = InvokeGenSeq>; template struct GenSeq : ConcatSeq, makeIndexSequence>{}; template<> struct GenSeq<0> : IndexesList<>{}; template<> struct GenSeq<1> : IndexesList<0>{}; template struct Indexes { using Value = makeIndexSequence; }; template struct FunctionPointer { enum {ArgumentCount = -1, IsPointerToMemberFunction = false}; }; template inline void assertObjectType(QObjectPrivate *d); template inline void assertObjectType(QObject *o) { // ensure all three compile [[maybe_unused]] auto staticcast = [](QObject *obj) { return static_cast(obj); }; [[maybe_unused]] auto qobjcast = [](QObject *obj) { return Obj::staticMetaObject.cast(obj); }; #ifdef __cpp_rtti [[maybe_unused]] auto dyncast = [](QObject *obj) { return dynamic_cast(obj); }; auto cast = dyncast; #else auto cast = qobjcast; #endif Q_ASSERT_X(cast(o), Obj::staticMetaObject.className(), "Called object is not of the correct type (class destructor may have already run)"); } template struct FunctorCall; template struct FunctorCall, List, R, Function> : FunctorCallBase { static void call(Function &f, void **arg) { call_internal(arg, [&] { return f((*reinterpret_cast::Type *>(arg[II+1]))...); }); } }; template struct FunctorCall, List, R, SlotRet (Obj::*)(SlotArgs...)> : FunctorCallBase { static void call(SlotRet (Obj::*f)(SlotArgs...), Obj *o, void **arg) { assertObjectType(o); call_internal(arg, [&] { return (o->*f)((*reinterpret_cast::Type *>(arg[II+1]))...); }); } }; template struct FunctorCall, List, R, SlotRet (Obj::*)(SlotArgs...) const> : FunctorCallBase { static void call(SlotRet (Obj::*f)(SlotArgs...) const, Obj *o, void **arg) { assertObjectType(o); call_internal(arg, [&] { return (o->*f)((*reinterpret_cast::Type *>(arg[II+1]))...); }); } }; template struct FunctorCall, List, R, SlotRet (Obj::*)(SlotArgs...) noexcept> : FunctorCallBase { static void call(SlotRet (Obj::*f)(SlotArgs...) noexcept, Obj *o, void **arg) { assertObjectType(o); call_internal(arg, [&]() noexcept { return (o->*f)((*reinterpret_cast::Type *>(arg[II+1]))...); }); } }; template struct FunctorCall, List, R, SlotRet (Obj::*)(SlotArgs...) const noexcept> : FunctorCallBase { static void call(SlotRet (Obj::*f)(SlotArgs...) const noexcept, Obj *o, void **arg) { assertObjectType(o); call_internal(arg, [&]() noexcept { return (o->*f)((*reinterpret_cast::Type *>(arg[II+1]))...); }); } }; template struct FunctionPointer { typedef Obj Object; typedef List Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Args...); enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; template static void call(Function f, Obj *o, void **arg) { FunctorCall::Value, SignalArgs, R, Function>::call(f, o, arg); } }; template struct FunctionPointer { typedef Obj Object; typedef List Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Args...) const; enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; template static void call(Function f, Obj *o, void **arg) { FunctorCall::Value, SignalArgs, R, Function>::call(f, o, arg); } }; template struct FunctionPointer { typedef List Arguments; typedef Ret ReturnType; typedef Ret (*Function) (Args...); enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = false}; template static void call(Function f, void *, void **arg) { FunctorCall::Value, SignalArgs, R, Function>::call(f, arg); } }; template struct FunctionPointer { typedef Obj Object; typedef List Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Args...) noexcept; enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; template static void call(Function f, Obj *o, void **arg) { FunctorCall::Value, SignalArgs, R, Function>::call(f, o, arg); } }; template struct FunctionPointer { typedef Obj Object; typedef List Arguments; typedef Ret ReturnType; typedef Ret (Obj::*Function) (Args...) const noexcept; enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = true}; template static void call(Function f, Obj *o, void **arg) { FunctorCall::Value, SignalArgs, R, Function>::call(f, o, arg); } }; template struct FunctionPointer { typedef List Arguments; typedef Ret ReturnType; typedef Ret (*Function) (Args...) noexcept; enum {ArgumentCount = sizeof...(Args), IsPointerToMemberFunction = false}; template static void call(Function f, void *, void **arg) { FunctorCall::Value, SignalArgs, R, Function>::call(f, arg); } }; // Traits to detect if there is a conversion between two types, // and that conversion does not include a narrowing conversion. template struct NarrowingDetector { T t[1]; }; // from P0608 template struct IsConvertibleWithoutNarrowing : std::false_type {}; template struct IsConvertibleWithoutNarrowing{ {std::declval()} } ) > > : std::true_type {}; // Check for the actual arguments. If they are exactly the same, // then don't bother checking for narrowing; as a by-product, // this solves the problem of incomplete types (which must be supported, // or they would error out in the trait above). template struct AreArgumentsConvertibleWithoutNarrowingBase : std::false_type {}; template struct AreArgumentsConvertibleWithoutNarrowingBase, IsConvertibleWithoutNarrowing> > > : std::true_type {}; /* Logic that check if the arguments of the slot matches the argument of the signal. To be used like this: static_assert(CheckCompatibleArguments::Arguments, FunctionPointer::Arguments>::value) */ template struct AreArgumentsCompatible { static int test(const std::remove_reference_t&); static char test(...); enum { value = sizeof(test(std::declval>())) == sizeof(int) }; #ifdef QT_NO_NARROWING_CONVERSIONS_IN_CONNECT using AreArgumentsConvertibleWithoutNarrowing = AreArgumentsConvertibleWithoutNarrowingBase, std::decay_t>; static_assert(AreArgumentsConvertibleWithoutNarrowing::value, "Signal and slot arguments are not compatible (narrowing)"); #endif }; template struct AreArgumentsCompatible { enum { value = false }; }; template struct AreArgumentsCompatible { enum { value = true }; }; // void as a return value template struct AreArgumentsCompatible { enum { value = true }; }; template struct AreArgumentsCompatible { enum { value = true }; }; template<> struct AreArgumentsCompatible { enum { value = true }; }; template struct CheckCompatibleArguments { enum { value = false }; }; template <> struct CheckCompatibleArguments, List<>> { enum { value = true }; }; template struct CheckCompatibleArguments> { enum { value = true }; }; template struct CheckCompatibleArguments, List> { enum { value = AreArgumentsCompatible::Type, typename RemoveConstRef::Type>::value && CheckCompatibleArguments, List>::value }; }; /* Find the maximum number of arguments a functor object can take and be still compatible with the arguments from the signal. Value is the number of arguments, or -1 if nothing matches. */ template struct ComputeFunctorArgumentCount; template struct ComputeFunctorArgumentCountHelper { enum { Value = -1 }; }; template struct ComputeFunctorArgumentCountHelper, false> : ComputeFunctorArgumentCount, sizeof...(ArgList)>::Value> {}; template struct ComputeFunctorArgumentCount> { template static auto test(F f) -> decltype(((f.operator()((std::declval())...)), int())); static char test(...); enum { Ok = sizeof(test(std::declval())) == sizeof(int), Value = Ok ? int(sizeof...(ArgList)) : int(ComputeFunctorArgumentCountHelper, Ok>::Value) }; }; /* get the return type of a functor, given the signal argument list */ template struct FunctorReturnType; template struct FunctorReturnType> { typedef decltype(std::declval().operator()((std::declval())...)) Value; }; template struct FunctorCallable { using ReturnType = decltype(std::declval()(std::declval()...)); using Function = ReturnType(*)(Args...); enum {ArgumentCount = sizeof...(Args)}; using Arguments = QtPrivate::List; template static void call(Func &f, void *, void **arg) { FunctorCall::Value, SignalArgs, R, Func>::call(f, arg); } }; template struct HasCallOperatorAcceptingArgs { private: template struct Test : std::false_type { }; // We explicitly use .operator() to not return true for pointers to free/static function template struct Test().operator()(std::declval()...))>> : std::true_type { }; public: using Type = Test; static constexpr bool value = Type::value; }; template constexpr bool HasCallOperatorAcceptingArgs_v = HasCallOperatorAcceptingArgs::value; template struct CallableHelper { private: // Could've been std::conditional_t, but that requires all branches to // be valid static auto Resolve(std::true_type CallOperator) -> FunctorCallable; static auto Resolve(std::false_type CallOperator) -> FunctionPointer>; public: using Type = decltype(Resolve(typename HasCallOperatorAcceptingArgs, Args...>::Type{})); }; template struct Callable : CallableHelper::Type {}; template struct Callable> : CallableHelper::Type {}; /* Wrapper around ComputeFunctorArgumentCount and CheckCompatibleArgument, depending on whether \a Functor is a PMF or not. Returns -1 if \a Func is not compatible with the \a ExpectedArguments, otherwise returns >= 0. */ template inline constexpr std::enable_if_t, std::is_same, QMetaMethod>, std::is_convertible, std::is_same, QMetaMethod> >, int> countMatchingArguments() { using ExpectedArguments = typename QtPrivate::FunctionPointer::Arguments; using Actual = std::decay_t; if constexpr (QtPrivate::FunctionPointer::IsPointerToMemberFunction || QtPrivate::FunctionPointer::ArgumentCount >= 0) { // PMF or free function using ActualArguments = typename QtPrivate::FunctionPointer::Arguments; if constexpr (QtPrivate::CheckCompatibleArguments::value) return QtPrivate::FunctionPointer::ArgumentCount; else return -1; } else { // lambda or functor return QtPrivate::ComputeFunctorArgumentCount::Value; } } // internal base class (interface) containing functions required to call a slot managed by a pointer to function. class QSlotObjectBase { // Don't use virtual functions here; we don't want the // compiler to create tons of per-polymorphic-class stuff that // we'll never need. We just use one function pointer, and the // Operations enum below to distinguish requests #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) QAtomicInt m_ref = 1; typedef void (*ImplFn)(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret); const ImplFn m_impl; #else using ImplFn = void (*)(QSlotObjectBase* this_, QObject *receiver, void **args, int which, bool *ret); const ImplFn m_impl; QAtomicInt m_ref = 1; #endif protected: // The operations that can be requested by calls to m_impl, // see the member functions that call m_impl below for details enum Operation { Destroy, Call, Compare, NumOperations }; public: explicit QSlotObjectBase(ImplFn fn) : m_impl(fn) {} // A custom deleter compatible with std protocols (op()()) we well as // the legacy QScopedPointer protocol (cleanup()). struct Deleter { void operator()(QSlotObjectBase *p) const noexcept { if (p) p->destroyIfLastRef(); } // for the non-standard QScopedPointer protocol: static void cleanup(QSlotObjectBase *p) noexcept { Deleter{}(p); } }; bool ref() noexcept { return m_ref.ref(); } #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) inline void destroyIfLastRef() noexcept { if (!m_ref.deref()) m_impl(Destroy, this, nullptr, nullptr, nullptr); } inline bool compare(void **a) { bool ret = false; m_impl(Compare, this, nullptr, a, &ret); return ret; } inline void call(QObject *r, void **a) { m_impl(Call, this, r, a, nullptr); } #else inline void destroyIfLastRef() noexcept { if (!m_ref.deref()) m_impl(this, nullptr, nullptr, Destroy, nullptr); } inline bool compare(void **a) { bool ret = false; m_impl(this, nullptr, a, Compare, &ret); return ret; } inline void call(QObject *r, void **a) { m_impl(this, r, a, Call, nullptr); } #endif bool isImpl(ImplFn f) const { return m_impl == f; } protected: ~QSlotObjectBase() {} private: Q_DISABLE_COPY_MOVE(QSlotObjectBase) }; using SlotObjUniquePtr = std::unique_ptr; inline SlotObjUniquePtr copy(const SlotObjUniquePtr &other) noexcept { if (other) other->ref(); return SlotObjUniquePtr{other.get()}; } class SlotObjSharedPtr { SlotObjUniquePtr obj; public: Q_NODISCARD_CTOR Q_IMPLICIT SlotObjSharedPtr() noexcept = default; Q_NODISCARD_CTOR Q_IMPLICIT SlotObjSharedPtr(std::nullptr_t) noexcept : SlotObjSharedPtr() {} Q_NODISCARD_CTOR explicit SlotObjSharedPtr(SlotObjUniquePtr o) : obj(std::move(o)) { // does NOT ref() (takes unique_ptr by value) // (that's why (QSlotObjectBase*) ctor doesn't exisit: don't know whether that one _should_) } Q_NODISCARD_CTOR SlotObjSharedPtr(const SlotObjSharedPtr &other) noexcept : obj{copy(other.obj)} {} SlotObjSharedPtr &operator=(const SlotObjSharedPtr &other) noexcept { auto copy = other; swap(copy); return *this; } Q_NODISCARD_CTOR SlotObjSharedPtr(SlotObjSharedPtr &&other) noexcept = default; SlotObjSharedPtr &operator=(SlotObjSharedPtr &&other) noexcept = default; ~SlotObjSharedPtr() = default; void swap(SlotObjSharedPtr &other) noexcept { obj.swap(other.obj); } auto get() const noexcept { return obj.get(); } auto operator->() const noexcept { return get(); } explicit operator bool() const noexcept { return bool(obj); } }; // Implementation of QSlotObjectBase for which the slot is a callable (function, PMF, functor, or lambda). // Args and R are the List of arguments and the return type of the signal to which the slot is connected. template class QCallableObject : public QSlotObjectBase, private QtPrivate::CompactStorage> { using FunctorValue = std::decay_t; using Storage = QtPrivate::CompactStorage; using FuncType = Callable; #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) Q_DECL_HIDDEN static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) #else // Design note: the first three arguments match those for typical Call // and Destroy uses. We return void to enable tail call optimization // for those too. Q_DECL_HIDDEN static void impl(QSlotObjectBase *this_, QObject *r, void **a, int which, bool *ret) #endif { const auto that = static_cast(this_); switch (which) { case Destroy: delete that; break; case Call: if constexpr (std::is_member_function_pointer_v) FuncType::template call(that->object(), static_cast(r), a); else FuncType::template call(that->object(), r, a); break; case Compare: if constexpr (std::is_member_function_pointer_v) { *ret = *reinterpret_cast(a) == that->object(); break; } // not implemented otherwise Q_FALLTHROUGH(); case NumOperations: Q_UNUSED(ret); } } public: explicit QCallableObject(Func &&f) : QSlotObjectBase(&impl), Storage{std::move(f)} {} explicit QCallableObject(const Func &f) : QSlotObjectBase(&impl), Storage{f} {} }; // Helper to detect the context object type based on the functor type: // QObject for free functions and lambdas; the callee for member function // pointers. The default declaration doesn't have the ContextType typedef, // and so non-functor APIs (like old-style string-based slots) are removed // from the overload set. template struct ContextTypeForFunctor {}; template struct ContextTypeForFunctor, std::is_member_function_pointer > > > { using ContextType = QObject; }; template struct ContextTypeForFunctor>, std::is_member_function_pointer, std::is_convertible::Object *, QObject *> > > > { using ContextType = typename QtPrivate::FunctionPointer::Object; }; /* Returns a suitable QSlotObjectBase object that holds \a func, if possible. Not available (and thus produces compile-time errors) if the Functor provided is not compatible with the expected Prototype. */ template static constexpr std::enable_if_t() >= 0, QtPrivate::QSlotObjectBase *> makeCallableObject(Functor &&func) { using ExpectedSignature = QtPrivate::FunctionPointer; using ExpectedReturnType = typename ExpectedSignature::ReturnType; using ExpectedArguments = typename ExpectedSignature::Arguments; using ActualSignature = QtPrivate::FunctionPointer; constexpr int MatchingArgumentCount = QtPrivate::countMatchingArguments(); using ActualArguments = typename QtPrivate::List_Left::Value; static_assert(int(ActualSignature::ArgumentCount) <= int(ExpectedSignature::ArgumentCount), "Functor requires more arguments than what can be provided."); // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) return new QtPrivate::QCallableObject, ActualArguments, ExpectedReturnType>(std::forward(func)); } template struct AreFunctionsCompatible : std::false_type {}; template struct AreFunctionsCompatible(std::forward(std::declval()))), QtPrivate::QSlotObjectBase *>> > : std::true_type {}; template inline constexpr bool AssertCompatibleFunctions() { static_assert(AreFunctionsCompatible::value, "Functor is not compatible with expected prototype!"); return true; } } QT_END_NAMESPACE