summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qobjectdefs_impl.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qobjectdefs_impl.h')
-rw-r--r--src/corelib/kernel/qobjectdefs_impl.h197
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));
}