514 lines
22 KiB
C++
514 lines
22 KiB
C++
// 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
|