blob: 4f4b17da25b9afe78964655c8b47b172a4e72eed [file] [log] [blame]
// Copyright 2021 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 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.
#pragma once
#include "FunctionInternal.h"
#include "UtilityInternal.h"
namespace android::base {
namespace fit {
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
class FunctionImpl;
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
class CallbackImpl;
// 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 kDefaultInlineTargetSize = 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 |inlineTargetSize| 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::InlineFunction<T, size>| for more control over allocation
// behavior.
//
// SYNOPSIS
//
// |T| is the function's signature. e.g. void(int, std::string).
//
// |inlineTargetSize| is the minimum size of target that is guaranteed to
// fit within a function without requiring heap allocation.
// Defaults to |kDefaultInlineTargetSize|.
//
// Class members are documented in |fit::FunctionImpl|, below.
//
// EXAMPLES
//
// -
// https://fuchsia.googlesource.com/fuchsia/+/HEAD/sdk/lib/fit/test/examples/function_example1.cc
// -
// https://fuchsia.googlesource.com/fuchsia/+/HEAD/sdk/lib/fit/test/examples/function_example2.cc
//
template <typename T, size_t inlineTargetSize = kDefaultInlineTargetSize>
using function = FunctionImpl<inlineTargetSize, /*requireInline=*/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, inlineTargetSize>| except that
// attempting to store a target larger than |inlineTargetSize| will fail to
// compile.
template <typename T, size_t inlineTargetSize = kDefaultInlineTargetSize>
using InlineFunction = FunctionImpl<inlineTargetSize,
/*requireInline=*/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 |inlineTargetSize| 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).
//
// |inlineTargetSize| is the minimum size of target that is guaranteed to
// fit within a callback without requiring heap allocation.
// Defaults to |kDefaultInlineTargetSize|.
//
// Class members are documented in |fit::CallbackImpl|, below.
//
template <typename T, size_t inlineTargetSize = kDefaultInlineTargetSize>
using Callback = CallbackImpl<inlineTargetSize, /*requireInline=*/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, inlineTargetSize>| except that
// attempting to store a target larger than |inlineTargetSize| will fail to
// compile.
template <typename T, size_t inlineTargetSize = kDefaultInlineTargetSize>
using InlineCallback = CallbackImpl<inlineTargetSize,
/*requireInline=*/true,
T>;
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
class FunctionImpl<inlineTargetSize, requireInline, Result(Args...)> final
: private ::android::base::fit::internal::
function_base<inlineTargetSize, requireInline, Result(Args...)> {
using Base = ::android::base::fit::internal::
function_base<inlineTargetSize, requireInline, Result(Args...)>;
// function_base requires private access during share()
friend class ::android::base::fit::internal::
function_base<inlineTargetSize, requireInline, Result(Args...)>;
// supports target() for shared functions
friend const void* ::android::base::fit::internal::get_target_type_id<>(
const FunctionImpl<inlineTargetSize, requireInline, Result(Args...)>&);
template <typename U>
using NotSelfType = ::android::base::fit::internal::NotSameType<FunctionImpl, U>;
template <typename... Conditions>
using RequiresConditions = ::android::base::fit::internal::RequiresConditions<Conditions...>;
template <typename... Conditions>
using AssignmentRequiresConditions =
::android::base::fit::internal::AssignmentRequiresConditions<FunctionImpl&, Conditions...>;
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.
FunctionImpl() = default;
// Creates a function with an empty target (same outcome as the default
// constructor).
FunctionImpl(decltype(nullptr)) : Base(nullptr) {}
// Creates a function bound to the specified function pointer.
// If target == nullptr, assigns an empty target.
FunctionImpl(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,
RequiresConditions<
std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)),
result_type>,
NotSelfType<Callable>> = true>
FunctionImpl(Callable&& target) : Base(std::forward<Callable>(target)) {}
// Deletes the specializations of FunctionImpl(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 otherInlineTargetSize, bool otherRequireInline>
FunctionImpl(::android::base::fit::CallbackImpl<otherInlineTargetSize,
otherRequireInline,
Result(Args...)>) = delete;
// Creates a function with a target moved from another function,
// leaving the other function with an empty target.
FunctionImpl(FunctionImpl&& other) : Base(static_cast<Base&&>(other)) {}
// Destroys the function, releasing its target.
~FunctionImpl() = default;
// Assigns the function to an empty target. Attempting to invoke the
// function will abort the program.
FunctionImpl& 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>
AssignmentRequiresConditions<
std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)),
result_type>,
NotSelfType<Callable>>
operator=(Callable&& target) {
Base::assign(std::forward<Callable>(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 otherInlineTargetSize, bool otherRequireInline>
FunctionImpl& operator=(::android::base::fit::CallbackImpl<otherInlineTargetSize,
otherRequireInline,
Result(Args...)>) = delete;
// Move assignment
FunctionImpl& operator=(FunctionImpl&& other) {
if (&other == this)
return *this;
Base::assign(static_cast<Base&&>(other));
return *this;
}
// Swaps the functions' targets.
void swap(FunctionImpl& 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::InlineFunction<>|
// because it may incur a heap allocation which is contrary to
// the stated purpose of |fit::InlineFunction<>|.
FunctionImpl share() {
FunctionImpl copy;
Base::template share_with<FunctionImpl>(copy);
return copy;
}
};
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
void swap(FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& a,
FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& b) {
a.swap(b);
}
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
bool operator==(const FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& f,
decltype(nullptr)) {
return !f;
}
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
bool operator==(decltype(nullptr),
const FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& f) {
return !f;
}
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
bool operator!=(const FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& f,
decltype(nullptr)) {
return !!f;
}
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
bool operator!=(decltype(nullptr),
const FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& f) {
return !!f;
}
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
class CallbackImpl<inlineTargetSize, requireInline, Result(Args...)> final
: private ::android::base::fit::internal::
function_base<inlineTargetSize, requireInline, Result(Args...)> {
using Base = ::android::base::fit::internal::
function_base<inlineTargetSize, requireInline, Result(Args...)>;
// function_base requires private access during share()
friend class ::android::base::fit::internal::
function_base<inlineTargetSize, requireInline, Result(Args...)>;
// supports target() for shared functions
friend const void* ::android::base::fit::internal::get_target_type_id<>(
const CallbackImpl<inlineTargetSize, requireInline, Result(Args...)>&);
template <typename U>
using NotSelfType = ::android::base::fit::internal::NotSameType<CallbackImpl, U>;
template <typename... Conditions>
using RequiresConditions = ::android::base::fit::internal::RequiresConditions<Conditions...>;
template <typename... Conditions>
using AssignmentRequiresConditions =
::android::base::fit::internal::AssignmentRequiresConditions<CallbackImpl&, Conditions...>;
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.
CallbackImpl() = default;
// Creates a callback with an empty target (same outcome as the default
// constructor).
CallbackImpl(decltype(nullptr)) : Base(nullptr) {}
// Creates a callback bound to the specified function pointer.
// If target == nullptr, assigns an empty target.
CallbackImpl(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,
RequiresConditions<
std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)),
result_type>,
NotSelfType<Callable>> = true>
CallbackImpl(Callable&& target) : Base(std::forward<Callable>(target)) {}
// Creates a callback with a target moved from another callback,
// leaving the other callback with an empty target.
CallbackImpl(CallbackImpl&& other) : Base(static_cast<Base&&>(other)) {}
// Destroys the callback, releasing its target.
~CallbackImpl() = default;
// Assigns the callback to an empty target. Attempting to invoke the
// callback will abort the program.
CallbackImpl& 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>
AssignmentRequiresConditions<
std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)),
result_type>,
NotSelfType<Callable>>
operator=(Callable&& target) {
Base::assign(std::forward<Callable>(target));
return *this;
}
// Move assignment
CallbackImpl& operator=(CallbackImpl&& other) {
if (&other == this)
return *this;
Base::assign(static_cast<Base&&>(other));
return *this;
}
// Swaps the callbacks' targets.
void swap(CallbackImpl& 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::InlineFunction<>|
// because it may incur a heap allocation which is contrary to
// the stated purpose of |fit::InlineFunction<>|.
CallbackImpl share() {
CallbackImpl copy;
Base::template share_with<CallbackImpl>(copy);
return copy;
}
};
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
void swap(CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& a,
CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& b) {
a.swap(b);
}
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
bool operator==(const CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& f,
decltype(nullptr)) {
return !f;
}
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
bool operator==(decltype(nullptr),
const CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& f) {
return !f;
}
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
bool operator!=(const CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& f,
decltype(nullptr)) {
return !!f;
}
template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
bool operator!=(decltype(nullptr),
const CallbackImpl<inlineTargetSize, requireInline, 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 bindMember(T* instance, R (T::*fn)(Args...)) {
return [instance, fn](Args... args) { return (instance->*fn)(std::forward<Args>(args)...); };
}
} // namespace fit
} // namespace android::base