// Copyright (C) 2023 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 #ifndef QRUNNABLE_H #define QRUNNABLE_H #include #include #include #include #include #include QT_BEGIN_NAMESPACE class Q_CORE_EXPORT QRunnable { bool m_autoDelete = true; Q_DISABLE_COPY(QRunnable) public: virtual void run() = 0; constexpr QRunnable() noexcept = default; virtual ~QRunnable(); #if QT_CORE_REMOVED_SINCE(6, 6) static QRunnable *create(std::function functionToRun); #endif template using if_callable = std::enable_if_t, bool>; template = true> static QRunnable *create(Callable &&functionToRun); static QRunnable *create(std::nullptr_t) = delete; bool autoDelete() const { return m_autoDelete; } void setAutoDelete(bool autoDelete) { m_autoDelete = autoDelete; } private: static Q_DECL_COLD_FUNCTION QRunnable *warnNullCallable(); class QGenericRunnable; }; class Q_CORE_EXPORT QRunnable::QGenericRunnable : public QRunnable { // Type erasure, to only instantiate a non-virtual class per Callable: class HelperBase { protected: enum class Op { Run, Destroy, }; using OpFn = void* (*)(Op, HelperBase *, void*); OpFn fn; protected: constexpr explicit HelperBase(OpFn f) noexcept : fn(f) {} ~HelperBase() = default; public: void run() { fn(Op::Run, this, nullptr); } void destroy() { fn(Op::Destroy, this, nullptr); } }; template class Helper : public HelperBase, private QtPrivate::CompactStorage { using Storage = QtPrivate::CompactStorage; static void *impl(Op op, HelperBase *that, [[maybe_unused]] void *arg) { const auto _this = static_cast(that); switch (op) { case Op::Run: _this->object()(); break; case Op::Destroy: delete _this; break; } return nullptr; } public: template explicit Helper(UniCallable &&functionToRun) noexcept : HelperBase(&impl), Storage{std::forward(functionToRun)} { } }; HelperBase *runHelper; public: template = true> explicit QGenericRunnable(Callable &&c) : runHelper(new Helper>(std::forward(c))) { } ~QGenericRunnable() override; void run() override; }; namespace QtPrivate { template constexpr inline bool is_function_pointer_v = std::conjunction_v< std::is_pointer, std::is_function> >; template constexpr inline bool is_std_function_v = false; template constexpr inline bool is_std_function_v> = true; } // namespace QtPrivate template > QRunnable *QRunnable::create(Callable &&functionToRun) { using F = std::decay_t; constexpr bool is_std_function = QtPrivate::is_std_function_v; constexpr bool is_function_pointer = QtPrivate::is_function_pointer_v; if constexpr (is_std_function || is_function_pointer) { bool is_null; if constexpr (is_std_function) { is_null = !functionToRun; } else if constexpr (is_function_pointer) { // shut up warnings about functions always having a non-null address: const void *functionPtr = reinterpret_cast(functionToRun); is_null = !functionPtr; } if (is_null) return warnNullCallable(); } return new QGenericRunnable(std::forward(functionToRun)); } QT_END_NAMESPACE #endif