1 // Copyright 2021 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // Copyright 2017 The Fuchsia Authors. All rights reserved.
16 // Use of this source code is governed by a BSD-style license that can be
17 // found in the LICENSE file.
18
19 #pragma once
20
21 #include "FunctionInternal.h"
22 #include "UtilityInternal.h"
23
24 namespace gfxstream::guest {
25 namespace fit {
26
27 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
28 class FunctionImpl;
29
30 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
31 class CallbackImpl;
32
33 // The default size allowance for storing a target inline within a function
34 // object, in bytes. This default allows for inline storage of targets
35 // as big as two pointers, such as an object pointer and a pointer to a member
36 // function.
37 constexpr size_t kDefaultInlineTargetSize = sizeof(void*) * 2;
38
39 // A |fit::Function| is a move-only polymorphic function wrapper.
40 //
41 // If you need a class with similar characteristics that also ensures
42 // "run-once" semantics (such as callbacks shared with timeouts, or for
43 // service requests with redundant, failover, or fallback service providers),
44 // see |fit::Callback|.
45 //
46 // |fit::Function<T>| behaves like |std::function<T>| except that it is
47 // move-only instead of copyable, so it can hold targets that cannot be copied,
48 // such as mutable lambdas, and immutable lambdas that capture move-only
49 // objects.
50 //
51 // Targets of up to |inlineTargetSize| bytes in size (rounded up for memory
52 // alignment) are stored inline within the function object without incurring
53 // any heap allocation. Larger callable objects will be moved to the heap as
54 // required.
55 //
56 // See also |fit::InlineFunction<T, size>| for more control over allocation
57 // behavior.
58 //
59 // SYNOPSIS
60 //
61 // |T| is the function's signature. e.g. void(int, std::string).
62 //
63 // |inlineTargetSize| is the minimum size of target that is guaranteed to
64 // fit within a function without requiring heap allocation.
65 // Defaults to |kDefaultInlineTargetSize|.
66 //
67 // Class members are documented in |fit::FunctionImpl|, below.
68 //
69 // EXAMPLES
70 //
71 // -
72 // https://fuchsia.googlesource.com/fuchsia/+/HEAD/sdk/lib/fit/test/examples/function_example1.cc
73 // -
74 // https://fuchsia.googlesource.com/fuchsia/+/HEAD/sdk/lib/fit/test/examples/function_example2.cc
75 //
76 template <typename T, size_t inlineTargetSize = kDefaultInlineTargetSize>
77 using function = FunctionImpl<inlineTargetSize, /*requireInline=*/false, T>;
78
79 // A move-only callable object wrapper that forces callables to be stored inline
80 // and never performs heap allocation.
81 //
82 // Behaves just like |fit::Function<T, inlineTargetSize>| except that
83 // attempting to store a target larger than |inlineTargetSize| will fail to
84 // compile.
85 template <typename T, size_t inlineTargetSize = kDefaultInlineTargetSize>
86 using InlineFunction = FunctionImpl<inlineTargetSize,
87 /*requireInline=*/true,
88 T>;
89
90 // Synonym for a function which takes no arguments and produces no result.
91 using closure = function<void()>;
92
93 // A |fit::Callback| is a move-only polymorphic function wrapper that also
94 // ensures "run-once" semantics (such as callbacks shared with timeouts, or for
95 // service requests with redundant, failover, or fallback service providers).
96 // A |fit::Callback| releases it's resources after the first call, and can be
97 // inspected before calling, so a potential caller can know if it should call
98 // the function, or skip the call because the target was already called.
99 //
100 // If you need a move-only function class with typical function characteristics,
101 // that permits multiple invocations of the same function, see |fit::Function|.
102 //
103 // |fit::Callback<T>| behaves like |std::function<T>| except:
104 //
105 // 1. It is move-only instead of copyable, so it can hold targets that cannot
106 // be copied, such as mutable lambdas, and immutable lambdas that capture
107 // move-only objects.
108 // 2. On the first call to invoke a |fit::Callback|, the target function held
109 // by the |fit::Callback| cannot be called again.
110 //
111 // When a |fit::Callback| is invoked for the first time, the target function is
112 // released and destructed, along with any resources owned by that function
113 // (typically the objects captured by a lambda).
114 //
115 // A |fit::Callback| in the "already called" state has the same state as a
116 // |fit::Callback| that has been assigned to |nullptr|. It can be compared to
117 // |nullptr| (via "==" or "!=", and its "operator bool()" returns false, which
118 // provides a convenient way to gate whether or not the |fit::Callback| should
119 // be called. (Note that invoking an empty |fit::Callback| or |fit::Function|
120 // will cause a program abort!)
121 //
122 // As an example, sharing |fit::Callback| between both a service and a timeout
123 // might look something like this:
124 //
125 // void service_with_timeout(fit::Callback<void(bool)> cb, uint timeout_ms) {
126 // service_request([cb = cb.share()]() mutable { if (cb) cb(false); });
127 // timeout(timeout_ms, [cb = std::move(cb)]() mutable { if (cb) cb(true); });
128 // }
129 //
130 // Since |fit::Callback| objects are move-only, and not copyable, duplicate
131 // references to the same |fit::Callback| can be obtained via share(), as shown
132 // in the example above. This method converts the |fit::Callback| into a
133 // reference-counted version of the |fit::Callback| and returns a copy of the
134 // reference as another |fit::Callback| with the same target function.
135 //
136 // What is notable about |fit::Callback<T>.share()| is that invoking any shared
137 // copy will "nullify" all shared copies, as shown in the example.
138 //
139 // Note that |fit::Callback| is NOT thread-safe by default. If multi-threaded
140 // support is required, you would need to implement your own mutex, or similar
141 // guard, before checking and calling a |fit::Callback|.
142 //
143 // Targets of up to |inlineTargetSize| bytes in size (rounded up for memory
144 // alignment) are stored inline within the callback object without incurring
145 // any heap allocation. Larger callable objects will be moved to the heap as
146 // required.
147 //
148 // See also |fit::inline_callback<T, size>| for more control over allocation
149 // behavior.
150 //
151 // SYNOPSIS
152 //
153 // |T| is the callback's signature. e.g. void(int, std::string).
154 //
155 // |inlineTargetSize| is the minimum size of target that is guaranteed to
156 // fit within a callback without requiring heap allocation.
157 // Defaults to |kDefaultInlineTargetSize|.
158 //
159 // Class members are documented in |fit::CallbackImpl|, below.
160 //
161 template <typename T, size_t inlineTargetSize = kDefaultInlineTargetSize>
162 using Callback = CallbackImpl<inlineTargetSize, /*requireInline=*/false, T>;
163
164 // A move-only, run-once, callable object wrapper that forces callables to be
165 // stored inline and never performs heap allocation.
166 //
167 // Behaves just like |fit::Callback<T, inlineTargetSize>| except that
168 // attempting to store a target larger than |inlineTargetSize| will fail to
169 // compile.
170 template <typename T, size_t inlineTargetSize = kDefaultInlineTargetSize>
171 using InlineCallback = CallbackImpl<inlineTargetSize,
172 /*requireInline=*/true,
173 T>;
174
175 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
176 class FunctionImpl<inlineTargetSize, requireInline, Result(Args...)> final
177 : private gfxstream::guest::fit::internal::
178 function_base<inlineTargetSize, requireInline, Result(Args...)> {
179 using Base = gfxstream::guest::fit::internal::
180 function_base<inlineTargetSize, requireInline, Result(Args...)>;
181
182 // function_base requires private access during share()
183 friend class gfxstream::guest::fit::internal::
184 function_base<inlineTargetSize, requireInline, Result(Args...)>;
185
186 // supports target() for shared functions
187 friend const void* gfxstream::guest::fit::internal::get_target_type_id<>(
188 const FunctionImpl<inlineTargetSize, requireInline, Result(Args...)>&);
189
190 template <typename U>
191 using NotSelfType = gfxstream::guest::fit::internal::NotSameType<FunctionImpl, U>;
192
193 template <typename... Conditions>
194 using RequiresConditions = gfxstream::guest::fit::internal::RequiresConditions<Conditions...>;
195
196 template <typename... Conditions>
197 using AssignmentRequiresConditions =
198 gfxstream::guest::fit::internal::AssignmentRequiresConditions<FunctionImpl&, Conditions...>;
199
200 public:
201 // The function's result type.
202 using typename Base::result_type;
203
204 // Initializes an empty (null) function. Attempting to call an empty
205 // function will abort the program.
206 FunctionImpl() = default;
207
208 // Creates a function with an empty target (same outcome as the default
209 // constructor).
FunctionImpl(decltype (nullptr))210 FunctionImpl(decltype(nullptr)) : Base(nullptr) {}
211
212 // Creates a function bound to the specified function pointer.
213 // If target == nullptr, assigns an empty target.
FunctionImpl(Result (* target)(Args...))214 FunctionImpl(Result (*target)(Args...)) : Base(target) {}
215
216 // Creates a function bound to the specified callable object.
217 // If target == nullptr, assigns an empty target.
218 //
219 // For functors, we need to capture the raw type but also restrict on the
220 // existence of an appropriate operator () to resolve overloads and implicit
221 // casts properly.
222 //
223 // Note that specializations of this template method that take fit::Callback
224 // objects as the target Callable are deleted (see below).
225 template <typename Callable,
226 RequiresConditions<
227 std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)),
228 result_type>,
229 NotSelfType<Callable>> = true>
FunctionImpl(Callable && target)230 FunctionImpl(Callable&& target) : Base(std::forward<Callable>(target)) {}
231
232 // Deletes the specializations of FunctionImpl(Callable) that would allow
233 // a |fit::Function| to be constructed from a |fit::Callback|. This prevents
234 // unexpected behavior of a |fit::Function| that would otherwise fail after
235 // one call. To explicitly allow this, simply wrap the |fit::Callback| in a
236 // pass-through lambda before passing it to the |fit::Function|.
237 template <size_t otherInlineTargetSize, bool otherRequireInline>
238 FunctionImpl(gfxstream::guest::fit::CallbackImpl<otherInlineTargetSize,
239 otherRequireInline,
240 Result(Args...)>) = delete;
241
242 // Creates a function with a target moved from another function,
243 // leaving the other function with an empty target.
FunctionImpl(FunctionImpl && other)244 FunctionImpl(FunctionImpl&& other) : Base(static_cast<Base&&>(other)) {}
245
246 // Destroys the function, releasing its target.
247 ~FunctionImpl() = default;
248
249 // Assigns the function to an empty target. Attempting to invoke the
250 // function will abort the program.
decltype(nullptr)251 FunctionImpl& operator=(decltype(nullptr)) {
252 Base::assign(nullptr);
253 return *this;
254 }
255
256 // Assigns the function to the specified callable object. If target ==
257 // nullptr, assigns an empty target.
258 //
259 // For functors, we need to capture the raw type but also restrict on the
260 // existence of an appropriate operator () to resolve overloads and implicit
261 // casts properly.
262 //
263 // Note that specializations of this template method that take fit::Callback
264 // objects as the target Callable are deleted (see below).
265 template <typename Callable>
266 AssignmentRequiresConditions<
267 std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)),
268 result_type>,
269 NotSelfType<Callable>>
270 operator=(Callable&& target) {
271 Base::assign(std::forward<Callable>(target));
272 return *this;
273 }
274
275 // Deletes the specializations of operator=(Callable) that would allow
276 // a |fit::Function| to be assigned from a |fit::Callback|. This
277 // prevents unexpected behavior of a |fit::Function| that would otherwise
278 // fail after one call. To explicitly allow this, simply wrap the
279 // |fit::Callback| in a pass-through lambda before assigning it to the
280 // |fit::Function|.
281 template <size_t otherInlineTargetSize, bool otherRequireInline>
282 FunctionImpl& operator=(gfxstream::guest::fit::CallbackImpl<otherInlineTargetSize,
283 otherRequireInline,
284 Result(Args...)>) = delete;
285
286 // Move assignment
287 FunctionImpl& operator=(FunctionImpl&& other) {
288 if (&other == this)
289 return *this;
290 Base::assign(static_cast<Base&&>(other));
291 return *this;
292 }
293
294 // Swaps the functions' targets.
swap(FunctionImpl & other)295 void swap(FunctionImpl& other) { Base::swap(other); }
296
297 // Returns a pointer to the function's target.
298 using Base::target;
299
300 // Returns true if the function has a non-empty target.
301 using Base::operator bool;
302
303 // Invokes the function's target.
304 // Aborts if the function's target is empty.
operator()305 Result operator()(Args... args) const { return Base::invoke(std::forward<Args>(args)...); }
306
307 // Returns a new function object that invokes the same target.
308 // The target itself is not copied; it is moved to the heap and its
309 // lifetime is extended until all references have been released.
310 //
311 // Note: This method is not supported on |fit::InlineFunction<>|
312 // because it may incur a heap allocation which is contrary to
313 // the stated purpose of |fit::InlineFunction<>|.
share()314 FunctionImpl share() {
315 FunctionImpl copy;
316 Base::template share_with<FunctionImpl>(copy);
317 return copy;
318 }
319 };
320
321 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
swap(FunctionImpl<inlineTargetSize,requireInline,Result,Args...> & a,FunctionImpl<inlineTargetSize,requireInline,Result,Args...> & b)322 void swap(FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& a,
323 FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& b) {
324 a.swap(b);
325 }
326
327 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
328 bool operator==(const FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& f,
329 decltype(nullptr)) {
330 return !f;
331 }
332 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
333 bool operator==(decltype(nullptr),
334 const FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& f) {
335 return !f;
336 }
337 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
338 bool operator!=(const FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& f,
339 decltype(nullptr)) {
340 return !!f;
341 }
342 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
343 bool operator!=(decltype(nullptr),
344 const FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& f) {
345 return !!f;
346 }
347
348 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
349 class CallbackImpl<inlineTargetSize, requireInline, Result(Args...)> final
350 : private gfxstream::guest::fit::internal::
351 function_base<inlineTargetSize, requireInline, Result(Args...)> {
352 using Base = gfxstream::guest::fit::internal::
353 function_base<inlineTargetSize, requireInline, Result(Args...)>;
354
355 // function_base requires private access during share()
356 friend class gfxstream::guest::fit::internal::
357 function_base<inlineTargetSize, requireInline, Result(Args...)>;
358
359 // supports target() for shared functions
360 friend const void* gfxstream::guest::fit::internal::get_target_type_id<>(
361 const CallbackImpl<inlineTargetSize, requireInline, Result(Args...)>&);
362
363 template <typename U>
364 using NotSelfType = gfxstream::guest::fit::internal::NotSameType<CallbackImpl, U>;
365
366 template <typename... Conditions>
367 using RequiresConditions = gfxstream::guest::fit::internal::RequiresConditions<Conditions...>;
368
369 template <typename... Conditions>
370 using AssignmentRequiresConditions =
371 gfxstream::guest::fit::internal::AssignmentRequiresConditions<CallbackImpl&, Conditions...>;
372
373 public:
374 // The callback function's result type.
375 using typename Base::result_type;
376
377 // Initializes an empty (null) callback. Attempting to call an empty
378 // callback will abort the program.
379 CallbackImpl() = default;
380
381 // Creates a callback with an empty target (same outcome as the default
382 // constructor).
CallbackImpl(decltype (nullptr))383 CallbackImpl(decltype(nullptr)) : Base(nullptr) {}
384
385 // Creates a callback bound to the specified function pointer.
386 // If target == nullptr, assigns an empty target.
CallbackImpl(Result (* target)(Args...))387 CallbackImpl(Result (*target)(Args...)) : Base(target) {}
388
389 // Creates a callback bound to the specified callable object.
390 // If target == nullptr, assigns an empty target.
391 //
392 // For functors, we need to capture the raw type but also restrict on the
393 // existence of an appropriate operator () to resolve overloads and implicit
394 // casts properly.
395 template <typename Callable,
396 RequiresConditions<
397 std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)),
398 result_type>,
399 NotSelfType<Callable>> = true>
CallbackImpl(Callable && target)400 CallbackImpl(Callable&& target) : Base(std::forward<Callable>(target)) {}
401
402 // Creates a callback with a target moved from another callback,
403 // leaving the other callback with an empty target.
CallbackImpl(CallbackImpl && other)404 CallbackImpl(CallbackImpl&& other) : Base(static_cast<Base&&>(other)) {}
405
406 // Destroys the callback, releasing its target.
407 ~CallbackImpl() = default;
408
409 // Assigns the callback to an empty target. Attempting to invoke the
410 // callback will abort the program.
decltype(nullptr)411 CallbackImpl& operator=(decltype(nullptr)) {
412 Base::assign(nullptr);
413 return *this;
414 }
415
416 // Assigns the callback to the specified callable object. If target ==
417 // nullptr, assigns an empty target.
418 //
419 // For functors, we need to capture the raw type but also restrict on the
420 // existence of an appropriate operator () to resolve overloads and implicit
421 // casts properly.
422 template <typename Callable>
423 AssignmentRequiresConditions<
424 std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)),
425 result_type>,
426 NotSelfType<Callable>>
427 operator=(Callable&& target) {
428 Base::assign(std::forward<Callable>(target));
429 return *this;
430 }
431
432 // Move assignment
433 CallbackImpl& operator=(CallbackImpl&& other) {
434 if (&other == this)
435 return *this;
436 Base::assign(static_cast<Base&&>(other));
437 return *this;
438 }
439
440 // Swaps the callbacks' targets.
swap(CallbackImpl & other)441 void swap(CallbackImpl& other) { Base::swap(other); }
442
443 // Returns a pointer to the callback's target.
444 using Base::target;
445
446 // Returns true if the callback has a non-empty target.
447 using Base::operator bool;
448
449 // Invokes the callback's target.
450 // Aborts if the callback's target is empty.
451 // |fit::Callback| must be non-const to invoke. Before the target function
452 // is actually called, the fit::Callback will be set to the default empty
453 // state (== nullptr, and operator bool() will subsequently return |false|).
454 // The target function will then be released after the function is called.
455 // If the callback was shared, any remaining copies will also be cleared.
operator()456 Result operator()(Args... args) {
457 auto temp = std::move(*this);
458 return temp.invoke(std::forward<Args>(args)...);
459 }
460
461 // Returns a new callback object that invokes the same target.
462 // The target itself is not copied; it is moved to the heap and its
463 // lifetime is extended until all references have been released.
464 // For |fit::Callback| (unlike fit::Function), the first invocation of the
465 // callback will release all references to the target. All callbacks
466 // derived from the same original callback (via share()) will be cleared,
467 // as if set to |nullptr|, and "operator bool()" will return false.
468 //
469 // Note: This method is not supported on |fit::InlineFunction<>|
470 // because it may incur a heap allocation which is contrary to
471 // the stated purpose of |fit::InlineFunction<>|.
share()472 CallbackImpl share() {
473 CallbackImpl copy;
474 Base::template share_with<CallbackImpl>(copy);
475 return copy;
476 }
477 };
478
479 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
swap(CallbackImpl<inlineTargetSize,requireInline,Result,Args...> & a,CallbackImpl<inlineTargetSize,requireInline,Result,Args...> & b)480 void swap(CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& a,
481 CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& b) {
482 a.swap(b);
483 }
484
485 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
486 bool operator==(const CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& f,
487 decltype(nullptr)) {
488 return !f;
489 }
490 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
491 bool operator==(decltype(nullptr),
492 const CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& f) {
493 return !f;
494 }
495 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
496 bool operator!=(const CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& f,
497 decltype(nullptr)) {
498 return !!f;
499 }
500 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
501 bool operator!=(decltype(nullptr),
502 const CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& f) {
503 return !!f;
504 }
505
506 // Returns a Callable object that invokes a member function of an object.
507 template <typename R, typename T, typename... Args>
bindMember(T * instance,R (T::* fn)(Args...))508 auto bindMember(T* instance, R (T::*fn)(Args...)) {
509 return [instance, fn](Args... args) { return (instance->*fn)(std::forward<Args>(args)...); };
510 }
511
512 } // namespace fit
513 } // namespace gfxstream::guest
514