blob: 21f6138a1569738a08c0cd7da5d53d40d373cb9c [file] [log] [blame]
// 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_