1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtConcurrent module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QTCONCURRENT_STOREDFUNCTIONCALL_H
41#define QTCONCURRENT_STOREDFUNCTIONCALL_H
42
43#include <QtConcurrent/qtconcurrent_global.h>
44
45#ifndef QT_NO_CONCURRENT
46#include <QtConcurrent/qtconcurrentrunbase.h>
47#include <QtCore/qpromise.h>
48
49#include <type_traits>
50
51QT_BEGIN_NAMESPACE
52
53#ifndef Q_QDOC
54
55namespace QtConcurrent {
56
57template<typename...>
58struct NonMemberFunctionResolver;
59
60template <class Function, class PromiseType, class... Args>
61struct NonMemberFunctionResolver<Function, PromiseType, Args...>
62{
63 using Type = std::tuple<std::decay_t<Function>, QPromise<PromiseType> &, std::decay_t<Args>...>;
64 static_assert(std::is_invocable_v<std::decay_t<Function>, QPromise<PromiseType> &, std::decay_t<Args>...>,
65 "It's not possible to invoke the function with passed arguments.");
66 static_assert(std::is_void_v<std::invoke_result_t<std::decay_t<Function>, QPromise<PromiseType> &, std::decay_t<Args>...>>,
67 "The function must return void type.");
68
69 static constexpr void invoke(std::decay_t<Function> function, QPromise<PromiseType> &promise,
70 std::decay_t<Args>... args)
71 {
72 std::invoke(function, promise, args...);
73 }
74 static Type initData(Function &&f, QPromise<PromiseType> &promise, Args &&...args)
75 {
76 return Type { std::forward<Function>(f), std::ref(promise), std::forward<Args>(args)... };
77 }
78};
79
80template<typename...>
81struct MemberFunctionResolver;
82
83template <typename Function, typename PromiseType, typename Arg, typename ... Args>
84struct MemberFunctionResolver<Function, PromiseType, Arg, Args...>
85{
86 using Type = std::tuple<std::decay_t<Function>, std::decay_t<Arg>, QPromise<PromiseType> &, std::decay_t<Args>...>;
87 static_assert(std::is_invocable_v<std::decay_t<Function>, std::decay_t<Arg>, QPromise<PromiseType> &, std::decay_t<Args>...>,
88 "It's not possible to invoke the function with passed arguments.");
89 static_assert(std::is_void_v<std::invoke_result_t<std::decay_t<Function>, std::decay_t<Arg>, QPromise<PromiseType> &, std::decay_t<Args>...>>,
90 "The function must return void type.");
91
92 static constexpr void invoke(std::decay_t<Function> function, std::decay_t<Arg> object,
93 QPromise<PromiseType> &promise, std::decay_t<Args>... args)
94 {
95 std::invoke(function, object, promise, args...);
96 }
97 static Type initData(Function &&f, QPromise<PromiseType> &promise, Arg &&fa, Args &&...args)
98 {
99 return Type { std::forward<Function>(f), std::forward<Arg>(fa), std::ref(promise), std::forward<Args>(args)... };
100 }
101};
102
103template <class IsMember, class Function, class PromiseType, class... Args>
104struct FunctionResolverHelper;
105
106template <class Function, class PromiseType, class... Args>
107struct FunctionResolverHelper<std::false_type, Function, PromiseType, Args...>
108 : public NonMemberFunctionResolver<Function, PromiseType, Args...>
109{
110};
111
112template <class Function, class PromiseType, class... Args>
113struct FunctionResolverHelper<std::true_type, Function, PromiseType, Args...>
114 : public MemberFunctionResolver<Function, PromiseType, Args...>
115{
116};
117
118template <class Function, class PromiseType, class... Args>
119struct FunctionResolver
120 : public FunctionResolverHelper<typename std::is_member_function_pointer<
121 std::decay_t<Function>>::type, Function, PromiseType, Args...>
122{
123};
124
125template <class Function, class ...Args>
126struct InvokeResult
127{
128 static_assert(std::is_invocable_v<std::decay_t<Function>, std::decay_t<Args>...>,
129 "It's not possible to invoke the function with passed arguments.");
130
131 using Type = std::invoke_result_t<std::decay_t<Function>, std::decay_t<Args>...>;
132};
133
134template <class Function, class ...Args>
135using InvokeResultType = typename InvokeResult<Function, Args...>::Type;
136
137template <class ...Types>
138using DecayedTuple = std::tuple<std::decay_t<Types>...>;
139
140template <class Function, class ...Args>
141struct StoredFunctionCall : public RunFunctionTask<InvokeResultType<Function, Args...>>
142{
143 StoredFunctionCall(DecayedTuple<Function, Args...> &&_data)
144 : data(std::move(_data))
145 {}
146
147protected:
148 void runFunctor() override
149 {
150 constexpr auto invoke = [] (std::decay_t<Function> function,
151 std::decay_t<Args>... args) -> auto {
152 return std::invoke(function, args...);
153 };
154
155 if constexpr (std::is_void_v<InvokeResultType<Function, Args...>>)
156 std::apply(invoke, std::move(data));
157 else
158 this->result = std::apply(invoke, std::move(data));
159 }
160
161private:
162 DecayedTuple<Function, Args...> data;
163};
164
165template <class Function, class PromiseType, class ...Args>
166struct StoredFunctionCallWithPromise : public RunFunctionTaskBase<PromiseType>
167{
168 using Resolver = FunctionResolver<Function, PromiseType, Args...>;
169 using DataType = typename Resolver::Type;
170 StoredFunctionCallWithPromise(Function &&f, Args &&...args)
171 : prom(this->promise),
172 data(std::move(Resolver::initData(std::forward<Function>(f), std::ref(prom),
173 std::forward<Args>(args)...)))
174 {}
175
176 StoredFunctionCallWithPromise(DecayedTuple<Function, Args...> &&_data)
177 : StoredFunctionCallWithPromise(std::move(_data),
178 std::index_sequence_for<std::decay_t<Function>, std::decay_t<Args>...>())
179 {}
180
181protected:
182 void runFunctor() override
183 {
184 std::apply(Resolver::invoke, std::move(data));
185 }
186
187private:
188 // helper to pack back the tuple into parameter pack
189 template<std::size_t... Is>
190 StoredFunctionCallWithPromise(DecayedTuple<Function, Args...> &&_data,
191 std::index_sequence<Is...>)
192 : StoredFunctionCallWithPromise(std::move(std::get<Is>(_data))...)
193 {}
194
195 QPromise<PromiseType> prom;
196 DataType data;
197};
198
199template<typename...>
200struct NonPromiseTaskResolver;
201
202template <typename Function, typename ... Args>
203struct NonPromiseTaskResolver<Function, Args...>
204{
205 using TaskWithArgs = DecayedTuple<Function, Args...>;
206 static auto run(TaskWithArgs &&args, const TaskStartParameters &startParameters) {
207 return (new StoredFunctionCall<Function, Args...>(std::move(args)))
208 ->start(startParameters);
209 }
210};
211
212template<typename...>
213struct PromiseTaskResolver;
214
215template <typename Function, typename ... Args>
216struct PromiseTaskResolver<Function, Args...>
217{
218 static_assert(QtPrivate::ArgResolver<Function>::IsPromise::value,
219 "The first argument of passed callable object isn't a QPromise<T> & type. "
220 "Did you intend to pass a callable which takes a QPromise<T> & type as a first argument? "
221 "Otherwise it's not possible to invoke the function with passed arguments.");
222 using TaskWithArgs = DecayedTuple<Function, Args...>;
223 static auto run(TaskWithArgs &&args, const TaskStartParameters &startParameters) {
224 using PromiseType = typename QtPrivate::ArgResolver<Function>::PromiseType;
225 return (new StoredFunctionCallWithPromise<Function, PromiseType, Args...>(std::move(args)))
226 ->start(startParameters);
227 }
228};
229
230template <class IsDirectlyInvocable, class Function, class... Args>
231struct TaskResolverHelper;
232
233template <class Function, class... Args>
234struct TaskResolverHelper<std::true_type, Function, Args...>
235 : public NonPromiseTaskResolver<Function, Args...>
236{
237};
238
239template <class Function, class... Args>
240struct TaskResolverHelper<std::false_type, Function, Args...>
241 : public PromiseTaskResolver<Function, Args...>
242{
243};
244
245template <class Function, class... Args>
246struct TaskResolver : public TaskResolverHelper<typename std::is_invocable<std::decay_t<Function>,
247 std::decay_t<Args>...>::type, Function, Args...>
248{
249};
250
251} //namespace QtConcurrent
252
253#endif // Q_QDOC
254
255QT_END_NAMESPACE
256
257#endif // QT_NO_CONCURRENT
258
259#endif
260