diff options
Diffstat (limited to 'src/corelib/kernel/qobjectdefs_impl.h')
-rw-r--r-- | src/corelib/kernel/qobjectdefs_impl.h | 197 |
1 files changed, 149 insertions, 48 deletions
diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h index 6f2dac783b..1e953f29b6 100644 --- a/src/corelib/kernel/qobjectdefs_impl.h +++ b/src/corelib/kernel/qobjectdefs_impl.h @@ -14,6 +14,8 @@ #include <QtCore/qfunctionaltools_impl.h> +#include <memory> + QT_BEGIN_NAMESPACE class QObject; class QObjectPrivate; @@ -49,25 +51,27 @@ namespace QtPrivate { template <typename L> struct List_Left<L, 0> { typedef List<> Value; }; /* - Trick to set the return value of a slot that works even if the signal or the slot returns void - to be used like - function(), ApplyReturnValue<ReturnType>(&return_value) - if function() returns a value, the operator,(T, ApplyReturnValue<ReturnType>) is called, but if it - returns void, the built-in one is used without an error. - */ - template <typename T> - struct ApplyReturnValue { - void *data; - explicit ApplyReturnValue(void *data_) : data(data_) {} + 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 <typename R, typename Lambda> + static void call_internal(void **args, Lambda &&fn) noexcept(noexcept(fn())) + { + using SlotRet = decltype(fn()); + if constexpr (std::is_void_v<R> || std::is_void_v<SlotRet>) { + Q_UNUSED(args); + } else { + if (args[0]) { + *reinterpret_cast<R *>(args[0]) = fn(); + return; + } + } + fn(); + } }; - template<typename T, typename U> - void operator,(T &&value, const ApplyReturnValue<U> &container) { - if (container.data) - *reinterpret_cast<U *>(container.data) = std::forward<T>(value); - } - template<typename T> - void operator,(T, const ApplyReturnValue<void> &) {} - /* The FunctionPointer<Func> struct is a type trait for function pointer. @@ -130,41 +134,57 @@ namespace QtPrivate { template <typename, typename, typename, typename> struct FunctorCall; template <int... II, typename... SignalArgs, typename R, typename Function> - struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, Function> { - static void call(Function &f, void **arg) { - f((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); + struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, Function> : FunctorCallBase + { + static void call(Function &f, void **arg) + { + call_internal<R>(arg, [&] { + return f((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...); + }); } }; template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj> - struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...)> { + struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...)> : FunctorCallBase + { static void call(SlotRet (Obj::*f)(SlotArgs...), Obj *o, void **arg) { assertObjectType<Obj>(o); - (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); + call_internal<R>(arg, [&] { + return (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...); + }); } }; template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj> - struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) const> { + struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) const> : FunctorCallBase + { static void call(SlotRet (Obj::*f)(SlotArgs...) const, Obj *o, void **arg) { assertObjectType<Obj>(o); - (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); + call_internal<R>(arg, [&] { + return (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...); + }); } }; template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj> - struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) noexcept> { + struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) noexcept> : FunctorCallBase + { static void call(SlotRet (Obj::*f)(SlotArgs...) noexcept, Obj *o, void **arg) { assertObjectType<Obj>(o); - (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); + call_internal<R>(arg, [&]() noexcept { + return (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...); + }); } }; template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj> - struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) const noexcept> { + struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) const noexcept> : FunctorCallBase + { static void call(SlotRet (Obj::*f)(SlotArgs...) const noexcept, Obj *o, void **arg) { assertObjectType<Obj>(o); - (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]); + call_internal<R>(arg, [&]() noexcept { + return (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...); + }); } }; @@ -330,28 +350,64 @@ namespace QtPrivate { typedef decltype(std::declval<Functor>().operator()((std::declval<ArgList>())...)) Value; }; - template<typename Function, int N> struct Functor + template<typename Func, typename... Args> + struct FunctorCallable { + using ReturnType = decltype(std::declval<Func>()(std::declval<Args>()...)); + using Function = ReturnType(*)(Args...); + enum {ArgumentCount = sizeof...(Args)}; + using Arguments = QtPrivate::List<Args...>; + template <typename SignalArgs, typename R> - static void call(Function &f, void *, void **arg) { - FunctorCall<typename Indexes<N>::Value, SignalArgs, R, Function>::call(f, arg); + static void call(Func &f, void *, void **arg) { + FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Func>::call(f, arg); } }; - template<typename Func> - struct ZeroArgFunctor : Functor<Func, 0> + template <typename Functor, typename... Args> + struct HasCallOperatorAcceptingArgs { - using ReturnType = decltype(std::declval<Func>()()); - using Function = ReturnType(*)(); - enum {ArgumentCount = 0}; - using Arguments = QtPrivate::List<>; + private: + template <typename F, typename = void> + struct Test : std::false_type + { + }; + // We explicitly use .operator() to not return true for pointers to free/static function + template <typename F> + struct Test<F, std::void_t<decltype(std::declval<F>().operator()(std::declval<Args>()...))>> + : std::true_type + { + }; + + public: + using Type = Test<Functor>; + static constexpr bool value = Type::value; + }; + + template <typename Functor, typename... Args> + constexpr bool + HasCallOperatorAcceptingArgs_v = HasCallOperatorAcceptingArgs<Functor, Args...>::value; + + template <typename Func, typename... Args> + 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<Func, Args...>; + static auto Resolve(std::false_type CallOperator) -> FunctionPointer<std::decay_t<Func>>; + + public: + using Type = decltype(Resolve(typename HasCallOperatorAcceptingArgs<std::decay_t<Func>, + Args...>::Type{})); }; - template<typename Func> - using Callable = std::conditional_t<FunctionPointer<std::decay_t<Func>>::ArgumentCount == -1, - ZeroArgFunctor<std::decay_t<Func>>, - FunctionPointer<std::decay_t<Func>> - >; + template<typename Func, typename... Args> + struct Callable : CallableHelper<Func, Args...>::Type + {}; + template<typename Func, typename... Args> + struct Callable<Func, List<Args...>> : CallableHelper<Func, Args...>::Type + {}; /* Wrapper around ComputeFunctorArgumentCount and CheckCompatibleArgument, @@ -413,7 +469,16 @@ namespace QtPrivate { public: explicit QSlotObjectBase(ImplFn fn) : m_impl(fn) {} - inline int ref() noexcept { return m_ref.ref(); } + // 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); } @@ -439,6 +504,44 @@ namespace QtPrivate { Q_DISABLE_COPY_MOVE(QSlotObjectBase) }; + using SlotObjUniquePtr = std::unique_ptr<QSlotObjectBase, + QSlotObjectBase::Deleter>; + 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 <typename Func, typename Args, typename R> @@ -447,10 +550,7 @@ namespace QtPrivate { { using FunctorValue = std::decay_t<Func>; using Storage = QtPrivate::CompactStorage<FunctorValue>; - using FuncType = std::conditional_t<std::is_member_function_pointer_v<FunctorValue>, - QtPrivate::FunctionPointer<FunctorValue>, - QtPrivate::Functor<FunctorValue, Args::size> - >; + using FuncType = Callable<Func, Args>; #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) @@ -540,6 +640,7 @@ namespace QtPrivate { 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<std::decay_t<Functor>, ActualArguments, ExpectedReturnType>(std::forward<Functor>(func)); } |