Revision control

Copy as Markdown

// 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
// Qt-Security score:significant reason:default
#ifndef QRUNNABLE_H
#define QRUNNABLE_H
#include <QtCore/qcompilerdetection.h>
#include <QtCore/qfunctionaltools_impl.h>
#include <QtCore/qtclasshelpermacros.h>
#include <QtCore/qtcoreexports.h>
#include <functional>
#include <type_traits>
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<void()> functionToRun);
#endif
template <typename Callable>
using if_callable = std::enable_if_t<std::is_invocable_r_v<void, Callable>, bool>;
template <typename Callable, if_callable<Callable> = 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 <typename Callable>
class Helper : public HelperBase, private QtPrivate::CompactStorage<Callable>
{
using Storage = QtPrivate::CompactStorage<Callable>;
static void *impl(Op op, HelperBase *that, [[maybe_unused]] void *arg)
{
const auto _this = static_cast<Helper*>(that);
switch (op) {
case Op::Run: _this->object()(); break;
case Op::Destroy: delete _this; break;
}
return nullptr;
}
public:
template <typename UniCallable>
explicit Helper(UniCallable &&functionToRun) noexcept
: HelperBase(&impl),
Storage{std::forward<UniCallable>(functionToRun)}
{
}
};
HelperBase *runHelper;
public:
template <typename Callable, if_callable<Callable> = true>
explicit QGenericRunnable(Callable &&c)
: runHelper(new Helper<std::decay_t<Callable>>(std::forward<Callable>(c)))
{
}
~QGenericRunnable() override;
void run() override;
};
namespace QtPrivate {
template <typename T>
constexpr inline bool is_function_pointer_v = std::conjunction_v<
std::is_pointer<T>,
std::is_function<std::remove_pointer_t<T>>
>;
template <typename T>
constexpr inline bool is_std_function_v = false;
template <typename T>
constexpr inline bool is_std_function_v<std::function<T>> = true;
} // namespace QtPrivate
template <typename Callable, QRunnable::if_callable<Callable>>
QRunnable *QRunnable::create(Callable &&functionToRun)
{
using F = std::decay_t<Callable>;
constexpr bool is_std_function = QtPrivate::is_std_function_v<F>;
constexpr bool is_function_pointer = QtPrivate::is_function_pointer_v<F>;
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<void *>(functionToRun);
is_null = !functionPtr;
}
if (is_null)
return warnNullCallable();
}
return new QGenericRunnable(std::forward<Callable>(functionToRun));
}
QT_END_NAMESPACE
#endif