1 | // Copyright (C) 2023 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QRUNNABLE_H |
5 | #define QRUNNABLE_H |
6 | |
7 | #include <QtCore/qcompilerdetection.h> |
8 | #include <QtCore/qfunctionaltools_impl.h> |
9 | #include <QtCore/qtclasshelpermacros.h> |
10 | #include <QtCore/qtcoreexports.h> |
11 | |
12 | #include <functional> |
13 | #include <type_traits> |
14 | |
15 | QT_BEGIN_NAMESPACE |
16 | |
17 | class Q_CORE_EXPORT QRunnable |
18 | { |
19 | bool m_autoDelete = true; |
20 | |
21 | Q_DISABLE_COPY(QRunnable) |
22 | public: |
23 | virtual void run() = 0; |
24 | |
25 | constexpr QRunnable() noexcept = default; |
26 | virtual ~QRunnable(); |
27 | #if QT_CORE_REMOVED_SINCE(6, 6) |
28 | static QRunnable *create(std::function<void()> functionToRun); |
29 | #endif |
30 | template <typename Callable> |
31 | using if_callable = std::enable_if_t<std::is_invocable_r_v<void, Callable>, bool>; |
32 | |
33 | template <typename Callable, if_callable<Callable> = true> |
34 | static QRunnable *create(Callable &&functionToRun); |
35 | static QRunnable *create(std::nullptr_t) = delete; |
36 | |
37 | bool autoDelete() const { return m_autoDelete; } |
38 | void setAutoDelete(bool autoDelete) { m_autoDelete = autoDelete; } |
39 | |
40 | private: |
41 | static Q_DECL_COLD_FUNCTION QRunnable *warnNullCallable(); |
42 | class QGenericRunnable; |
43 | }; |
44 | |
45 | class Q_CORE_EXPORT QRunnable::QGenericRunnable : public QRunnable |
46 | { |
47 | // Type erasure, to only instantiate a non-virtual class per Callable: |
48 | class HelperBase |
49 | { |
50 | protected: |
51 | enum class Op { |
52 | Run, |
53 | Destroy, |
54 | }; |
55 | using OpFn = void* (*)(Op, HelperBase *, void*); |
56 | OpFn fn; |
57 | protected: |
58 | constexpr explicit HelperBase(OpFn f) noexcept : fn(f) {} |
59 | ~HelperBase() = default; |
60 | public: |
61 | void run() { fn(Op::Run, this, nullptr); } |
62 | void destroy() { fn(Op::Destroy, this, nullptr); } |
63 | }; |
64 | |
65 | template <typename Callable> |
66 | class Helper : public HelperBase, private QtPrivate::CompactStorage<Callable> |
67 | { |
68 | using Storage = QtPrivate::CompactStorage<Callable>; |
69 | static void *impl(Op op, HelperBase *that, [[maybe_unused]] void *arg) |
70 | { |
71 | const auto _this = static_cast<Helper*>(that); |
72 | switch (op) { |
73 | case Op::Run: _this->object()(); break; |
74 | case Op::Destroy: delete _this; break; |
75 | } |
76 | return nullptr; |
77 | } |
78 | public: |
79 | template <typename UniCallable> |
80 | explicit Helper(UniCallable &&functionToRun) noexcept |
81 | : HelperBase(&impl), |
82 | Storage{std::forward<UniCallable>(functionToRun)} |
83 | { |
84 | } |
85 | }; |
86 | |
87 | HelperBase *runHelper; |
88 | public: |
89 | template <typename Callable, if_callable<Callable> = true> |
90 | explicit QGenericRunnable(Callable &&c) |
91 | : runHelper(new Helper<std::decay_t<Callable>>(std::forward<Callable>(c))) |
92 | { |
93 | } |
94 | ~QGenericRunnable() override; |
95 | |
96 | void run() override; |
97 | }; |
98 | |
99 | namespace QtPrivate { |
100 | |
101 | template <typename T> |
102 | constexpr inline bool is_function_pointer_v = std::conjunction_v< |
103 | std::is_pointer<T>, |
104 | std::is_function<std::remove_pointer_t<T>> |
105 | >; |
106 | template <typename T> |
107 | constexpr inline bool is_std_function_v = false; |
108 | template <typename T> |
109 | constexpr inline bool is_std_function_v<std::function<T>> = true; |
110 | |
111 | } // namespace QtPrivate |
112 | |
113 | template <typename Callable, QRunnable::if_callable<Callable>> |
114 | QRunnable *QRunnable::create(Callable &&functionToRun) |
115 | { |
116 | using F = std::decay_t<Callable>; |
117 | constexpr bool is_std_function = QtPrivate::is_std_function_v<F>; |
118 | constexpr bool is_function_pointer = QtPrivate::is_function_pointer_v<F>; |
119 | if constexpr (is_std_function || is_function_pointer) { |
120 | bool is_null; |
121 | if constexpr (is_std_function) { |
122 | is_null = !functionToRun; |
123 | } else if constexpr (is_function_pointer) { |
124 | // shut up warnings about functions always having a non-null address: |
125 | const void *functionPtr = reinterpret_cast<void *>(functionToRun); |
126 | is_null = !functionPtr; |
127 | } |
128 | if (is_null) |
129 | return warnNullCallable(); |
130 | } |
131 | |
132 | return new QGenericRunnable(std::forward<Callable>(functionToRun)); |
133 | } |
134 | |
135 | QT_END_NAMESPACE |
136 | |
137 | #endif |
138 | |