| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef LIB_FIT_FUNCTION_H_ |
| #define LIB_FIT_FUNCTION_H_ |
| |
| #include "function_internal.h" |
| |
| namespace fit { |
| |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| class function_impl; |
| |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| class callback_impl; |
| |
| // The default size allowance for storing a target inline within a function |
| // object, in bytes. This default allows for inline storage of targets |
| // as big as two pointers, such as an object pointer and a pointer to a member |
| // function. |
| constexpr size_t default_inline_target_size = sizeof(void*) * 2; |
| |
| // A |fit::function| is a move-only polymorphic function wrapper. |
| // |
| // If you need a class with similar characteristics that also ensures |
| // "run-once" semantics (such as callbacks shared with timeouts, or for |
| // service requests with redundant, failover, or fallback service providers), |
| // see |fit::callback|. |
| // |
| // |fit::function<T>| behaves like |std::function<T>| except that it is |
| // move-only instead of copyable, so it can hold targets that cannot be copied, |
| // such as mutable lambdas, and immutable lambdas that capture move-only |
| // objects. |
| // |
| // Targets of up to |inline_target_size| bytes in size (rounded up for memory |
| // alignment) are stored inline within the function object without incurring |
| // any heap allocation. Larger callable objects will be moved to the heap as |
| // required. |
| // |
| // See also |fit::inline_function<T, size>| for more control over allocation |
| // behavior. |
| // |
| // SYNOPSIS |
| // |
| // |T| is the function's signature. e.g. void(int, std::string). |
| // |
| // |inline_target_size| is the minimum size of target that is guaranteed to |
| // fit within a function without requiring heap allocation. |
| // Defaults to |default_inline_target_size|. |
| // |
| // Class members are documented in |fit::function_impl|, below. |
| // |
| // EXAMPLES |
| // |
| // - |
| // https://fuchsia.googlesource.com/fuchsia/+/master/zircon/system/ulib/fit/test/examples/function_example1.cc |
| // - |
| // https://fuchsia.googlesource.com/fuchsia/+/master/zircon/system/ulib/fit/test/examples/function_example2.cc |
| // |
| template <typename T, size_t inline_target_size = default_inline_target_size> |
| using function = function_impl<inline_target_size, |
| /*require_inline=*/false, T>; |
| |
| // A move-only callable object wrapper that forces callables to be stored inline |
| // and never performs heap allocation. |
| // |
| // Behaves just like |fit::function<T, inline_target_size>| except that |
| // attempting to store a target larger than |inline_target_size| will fail to |
| // compile. |
| template <typename T, size_t inline_target_size = default_inline_target_size> |
| using inline_function = function_impl<inline_target_size, |
| /*require_inline=*/true, T>; |
| |
| // Synonym for a function which takes no arguments and produces no result. |
| using closure = function<void()>; |
| |
| // A |fit::callback| is a move-only polymorphic function wrapper that also |
| // ensures "run-once" semantics (such as callbacks shared with timeouts, or for |
| // service requests with redundant, failover, or fallback service providers). |
| // A |fit::callback| releases it's resources after the first call, and can be |
| // inspected before calling, so a potential caller can know if it should call |
| // the function, or skip the call because the target was already called. |
| // |
| // If you need a move-only function class with typical function characteristics, |
| // that permits multiple invocations of the same function, see |fit::function|. |
| // |
| // |fit::callback<T>| behaves like |std::function<T>| except: |
| // |
| // 1. It is move-only instead of copyable, so it can hold targets that cannot |
| // be copied, such as mutable lambdas, and immutable lambdas that capture |
| // move-only objects. |
| // 2. On the first call to invoke a |fit::callback|, the target function held |
| // by the |fit::callback| cannot be called again. |
| // |
| // When a |fit::callback| is invoked for the first time, the target function is |
| // released and destructed, along with any resources owned by that function |
| // (typically the objects captured by a lambda). |
| // |
| // A |fit::callback| in the "already called" state has the same state as a |
| // |fit::callback| that has been assigned to |nullptr|. It can be compared to |
| // |nullptr| (via "==" or "!=", and its "operator bool()" returns false, which |
| // provides a convenient way to gate whether or not the |fit::callback| should |
| // be called. (Note that invoking an empty |fit::callback| or |fit::function| |
| // will cause a program abort!) |
| // |
| // As an example, sharing |fit::callback| between both a service and a timeout |
| // might look something like this: |
| // |
| // void service_with_timeout(fit::callback<void(bool)> cb, uint timeout_ms) { |
| // service_request([cb = cb.share()]() mutable { if (cb) cb(false); }); |
| // timeout(timeout_ms, [cb = std::move(cb)]() mutable { if (cb) cb(true); }); |
| // } |
| // |
| // Since |fit::callback| objects are move-only, and not copyable, duplicate |
| // references to the same |fit::callback| can be obtained via share(), as shown |
| // in the example above. This method converts the |fit::callback| into a |
| // reference-counted version of the |fit::callback| and returns a copy of the |
| // reference as another |fit::callback| with the same target function. |
| // |
| // What is notable about |fit::callback<T>.share()| is that invoking any shared |
| // copy will "nullify" all shared copies, as shown in the example. |
| // |
| // Note that |fit::callback| is NOT thread-safe by default. If multi-threaded |
| // support is required, you would need to implement your own mutex, or similar |
| // guard, before checking and calling a |fit::callback|. |
| // |
| // Targets of up to |inline_target_size| bytes in size (rounded up for memory |
| // alignment) are stored inline within the callback object without incurring |
| // any heap allocation. Larger callable objects will be moved to the heap as |
| // required. |
| // |
| // See also |fit::inline_callback<T, size>| for more control over allocation |
| // behavior. |
| // |
| // SYNOPSIS |
| // |
| // |T| is the callback's signature. e.g. void(int, std::string). |
| // |
| // |inline_target_size| is the minimum size of target that is guaranteed to |
| // fit within a callback without requiring heap allocation. |
| // Defaults to |default_inline_target_size|. |
| // |
| // Class members are documented in |fit::callback_impl|, below. |
| // |
| template <typename T, size_t inline_target_size = default_inline_target_size> |
| using callback = callback_impl<inline_target_size, /*require_inline=*/false, T>; |
| |
| // A move-only, run-once, callable object wrapper that forces callables to be |
| // stored inline and never performs heap allocation. |
| // |
| // Behaves just like |fit::callback<T, inline_target_size>| except that |
| // attempting to store a target larger than |inline_target_size| will fail to |
| // compile. |
| template <typename T, size_t inline_target_size = default_inline_target_size> |
| using inline_callback = callback_impl<inline_target_size, |
| /*require_inline=*/true, T>; |
| |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| class function_impl<inline_target_size, require_inline, Result(Args...)> final |
| : private ::fit::internal::function_base<inline_target_size, require_inline, Result(Args...)> { |
| using base = ::fit::internal::function_base<inline_target_size, require_inline, Result(Args...)>; |
| |
| // function_base requires private access during share() |
| friend class ::fit::internal::function_base<inline_target_size, require_inline, Result(Args...)>; |
| |
| // supports target() for shared functions |
| friend const void* ::fit::internal::get_target_type_id<>( |
| const function_impl<inline_target_size, require_inline, Result(Args...)>&); |
| |
| public: |
| // The function's result type. |
| using typename base::result_type; |
| |
| // Initializes an empty (null) function. Attempting to call an empty |
| // function will abort the program. |
| function_impl() = default; |
| |
| // Creates a function with an empty target (same outcome as the default |
| // constructor). |
| function_impl(decltype(nullptr)) : base(nullptr) {} |
| |
| // Creates a function bound to the specified function pointer. |
| // If target == nullptr, assigns an empty target. |
| function_impl(Result (*target)(Args...)) : base(target) {} |
| |
| // Creates a function bound to the specified callable object. |
| // If target == nullptr, assigns an empty target. |
| // |
| // For functors, we need to capture the raw type but also restrict on the |
| // existence of an appropriate operator () to resolve overloads and implicit |
| // casts properly. |
| // |
| // Note that specializations of this template method that take fit::callback |
| // objects as the target Callable are deleted (see below). |
| template <typename Callable, |
| typename = std::enable_if_t<std::is_convertible< |
| decltype(std::declval<Callable&>()(std::declval<Args>()...)), result_type>::value>> |
| function_impl(Callable target) : base(std::move(target)) {} |
| |
| // Deletes the specializations of function_impl(Callable) that would allow |
| // a |fit::function| to be constructed from a |fit::callback|. This prevents |
| // unexpected behavior of a |fit::function| that would otherwise fail after |
| // one call. To explicitly allow this, simply wrap the |fit::callback| in a |
| // pass-through lambda before passing it to the |fit::function|. |
| template <size_t other_inline_target_size, bool other_require_inline> |
| function_impl( |
| ::fit::callback_impl<other_inline_target_size, other_require_inline, Result(Args...)>) = |
| delete; |
| |
| // Creates a function with a target moved from another function, |
| // leaving the other function with an empty target. |
| function_impl(function_impl&& other) : base(static_cast<base&&>(other)) {} |
| |
| // Destroys the function, releasing its target. |
| ~function_impl() = default; |
| |
| // Assigns the function to an empty target. Attempting to invoke the |
| // function will abort the program. |
| function_impl& operator=(decltype(nullptr)) { |
| base::assign(nullptr); |
| return *this; |
| } |
| |
| // Assigns the function to the specified callable object. If target == |
| // nullptr, assigns an empty target. |
| // |
| // For functors, we need to capture the raw type but also restrict on the |
| // existence of an appropriate operator () to resolve overloads and implicit |
| // casts properly. |
| // |
| // Note that specializations of this template method that take fit::callback |
| // objects as the target Callable are deleted (see below). |
| template <typename Callable, |
| typename = std::enable_if_t<std::is_convertible< |
| decltype(std::declval<Callable&>()(std::declval<Args>()...)), result_type>::value>> |
| function_impl& operator=(Callable target) { |
| base::assign(std::move(target)); |
| return *this; |
| } |
| |
| // Deletes the specializations of operator=(Callable) that would allow |
| // a |fit::function| to be assigned from a |fit::callback|. This |
| // prevents unexpected behavior of a |fit::function| that would otherwise |
| // fail after one call. To explicitly allow this, simply wrap the |
| // |fit::callback| in a pass-through lambda before assigning it to the |
| // |fit::function|. |
| template <size_t other_inline_target_size, bool other_require_inline> |
| function_impl& operator=( |
| ::fit::callback_impl<other_inline_target_size, other_require_inline, Result(Args...)>) = |
| delete; |
| |
| // Move assignment |
| function_impl& operator=(function_impl&& other) { |
| if (&other == this) |
| return *this; |
| base::assign(static_cast<base&&>(other)); |
| return *this; |
| } |
| |
| // Swaps the functions' targets. |
| void swap(function_impl& other) { base::swap(other); } |
| |
| // Returns a pointer to the function's target. |
| using base::target; |
| |
| // Returns true if the function has a non-empty target. |
| using base::operator bool; |
| |
| // Invokes the function's target. |
| // Aborts if the function's target is empty. |
| Result operator()(Args... args) const { return base::invoke(std::forward<Args>(args)...); } |
| |
| // Returns a new function object that invokes the same target. |
| // The target itself is not copied; it is moved to the heap and its |
| // lifetime is extended until all references have been released. |
| // |
| // Note: This method is not supported on |fit::inline_function<>| |
| // because it may incur a heap allocation which is contrary to |
| // the stated purpose of |fit::inline_function<>|. |
| function_impl share() { |
| function_impl copy; |
| base::template share_with<function_impl>(copy); |
| return copy; |
| } |
| }; |
| |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| void swap(function_impl<inline_target_size, require_inline, Result, Args...>& a, |
| function_impl<inline_target_size, require_inline, Result, Args...>& b) { |
| a.swap(b); |
| } |
| |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| bool operator==(const function_impl<inline_target_size, require_inline, Result, Args...>& f, |
| decltype(nullptr)) { |
| return !f; |
| } |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| bool operator==(decltype(nullptr), |
| const function_impl<inline_target_size, require_inline, Result, Args...>& f) { |
| return !f; |
| } |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| bool operator!=(const function_impl<inline_target_size, require_inline, Result, Args...>& f, |
| decltype(nullptr)) { |
| return !!f; |
| } |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| bool operator!=(decltype(nullptr), |
| const function_impl<inline_target_size, require_inline, Result, Args...>& f) { |
| return !!f; |
| } |
| |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| class callback_impl<inline_target_size, require_inline, Result(Args...)> final |
| : private ::fit::internal::function_base<inline_target_size, require_inline, Result(Args...)> { |
| using base = ::fit::internal::function_base<inline_target_size, require_inline, Result(Args...)>; |
| |
| // function_base requires private access during share() |
| friend class ::fit::internal::function_base<inline_target_size, require_inline, Result(Args...)>; |
| |
| // supports target() for shared functions |
| friend const void* ::fit::internal::get_target_type_id<>( |
| const callback_impl<inline_target_size, require_inline, Result(Args...)>&); |
| |
| public: |
| // The callback function's result type. |
| using typename base::result_type; |
| |
| // Initializes an empty (null) callback. Attempting to call an empty |
| // callback will abort the program. |
| callback_impl() = default; |
| |
| // Creates a callback with an empty target (same outcome as the default |
| // constructor). |
| callback_impl(decltype(nullptr)) : base(nullptr) {} |
| |
| // Creates a callback bound to the specified function pointer. |
| // If target == nullptr, assigns an empty target. |
| callback_impl(Result (*target)(Args...)) : base(target) {} |
| |
| // Creates a callback bound to the specified callable object. |
| // If target == nullptr, assigns an empty target. |
| // |
| // For functors, we need to capture the raw type but also restrict on the |
| // existence of an appropriate operator () to resolve overloads and implicit |
| // casts properly. |
| template <typename Callable, |
| typename = std::enable_if_t<std::is_convertible< |
| decltype(std::declval<Callable&>()(std::declval<Args>()...)), result_type>::value>> |
| callback_impl(Callable target) : base(std::move(target)) {} |
| |
| // Creates a callback with a target moved from another callback, |
| // leaving the other callback with an empty target. |
| callback_impl(callback_impl&& other) : base(static_cast<base&&>(other)) {} |
| |
| // Destroys the callback, releasing its target. |
| ~callback_impl() = default; |
| |
| // Assigns the callback to an empty target. Attempting to invoke the |
| // callback will abort the program. |
| callback_impl& operator=(decltype(nullptr)) { |
| base::assign(nullptr); |
| return *this; |
| } |
| |
| // Assigns the callback to the specified callable object. If target == |
| // nullptr, assigns an empty target. |
| // |
| // For functors, we need to capture the raw type but also restrict on the |
| // existence of an appropriate operator () to resolve overloads and implicit |
| // casts properly. |
| template <typename Callable, |
| typename = std::enable_if_t<std::is_convertible< |
| decltype(std::declval<Callable&>()(std::declval<Args>()...)), result_type>::value>> |
| callback_impl& operator=(Callable target) { |
| base::assign(std::move(target)); |
| return *this; |
| } |
| |
| // Move assignment |
| callback_impl& operator=(callback_impl&& other) { |
| if (&other == this) |
| return *this; |
| base::assign(static_cast<base&&>(other)); |
| return *this; |
| } |
| |
| // Swaps the callbacks' targets. |
| void swap(callback_impl& other) { base::swap(other); } |
| |
| // Returns a pointer to the callback's target. |
| using base::target; |
| |
| // Returns true if the callback has a non-empty target. |
| using base::operator bool; |
| |
| // Invokes the callback's target. |
| // Aborts if the callback's target is empty. |
| // |fit::callback| must be non-const to invoke. Before the target function |
| // is actually called, the fit::callback will be set to the default empty |
| // state (== nullptr, and operator bool() will subsequently return |false|). |
| // The target function will then be released after the function is called. |
| // If the callback was shared, any remaining copies will also be cleared. |
| Result operator()(Args... args) { |
| auto temp = std::move(*this); |
| return temp.invoke(std::forward<Args>(args)...); |
| } |
| |
| // Returns a new callback object that invokes the same target. |
| // The target itself is not copied; it is moved to the heap and its |
| // lifetime is extended until all references have been released. |
| // For |fit::callback| (unlike fit::function), the first invocation of the |
| // callback will release all references to the target. All callbacks |
| // derived from the same original callback (via share()) will be cleared, |
| // as if set to |nullptr|, and "operator bool()" will return false. |
| // |
| // Note: This method is not supported on |fit::inline_function<>| |
| // because it may incur a heap allocation which is contrary to |
| // the stated purpose of |fit::inline_function<>|. |
| callback_impl share() { |
| callback_impl copy; |
| base::template share_with<callback_impl>(copy); |
| return copy; |
| } |
| }; |
| |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| void swap(callback_impl<inline_target_size, require_inline, Result, Args...>& a, |
| callback_impl<inline_target_size, require_inline, Result, Args...>& b) { |
| a.swap(b); |
| } |
| |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| bool operator==(const callback_impl<inline_target_size, require_inline, Result, Args...>& f, |
| decltype(nullptr)) { |
| return !f; |
| } |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| bool operator==(decltype(nullptr), |
| const callback_impl<inline_target_size, require_inline, Result, Args...>& f) { |
| return !f; |
| } |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| bool operator!=(const callback_impl<inline_target_size, require_inline, Result, Args...>& f, |
| decltype(nullptr)) { |
| return !!f; |
| } |
| template <size_t inline_target_size, bool require_inline, typename Result, typename... Args> |
| bool operator!=(decltype(nullptr), |
| const callback_impl<inline_target_size, require_inline, Result, Args...>& f) { |
| return !!f; |
| } |
| |
| // Returns a Callable object that invokes a member function of an object. |
| template <typename R, typename T, typename... Args> |
| auto bind_member(T* instance, R (T::*fn)(Args...)) { |
| return [instance, fn](Args... args) { return (instance->*fn)(std::forward<Args>(args)...); }; |
| } |
| |
| } // namespace fit |
| |
| #endif // LIB_FIT_FUNCTION_H_ |