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