xref: /aosp_15_r20/external/pigweed/third_party/fuchsia/repo/sdk/lib/fit/include/lib/fit/function.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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