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