+++ /dev/null
-// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
-// This source code is licensed under both the GPLv2 (found in the
-// COPYING file in the root directory) and Apache 2.0 License
-// (found in the LICENSE.Apache file in the root directory).
-
-#pragma once
-
-#include <cstdint>
-#include <type_traits>
-
-#include <folly/Traits.h>
-#include <folly/Utility.h>
-#include <folly/functional/Invoke.h>
-#include <folly/lang/Launder.h>
-
-namespace folly {
-namespace detail {
-
-/**
- * InlineFunctionRef is similar to folly::FunctionRef but has the additional
- * benefit of being able to store the function it was instantiated with inline
- * in a buffer of the given capacity. Inline storage is only used if the
- * function object and a pointer (for type-erasure) are small enough to fit in
- * the templated size. If there is not enough in-situ capacity for the
- * callable, this just stores a reference to the function object like
- * FunctionRef.
- *
- * This helps give a perf boost in the case where the data gets separated from
- * the point of invocation. If, for example, at the point of invocation, the
- * InlineFunctionRef object is not cached, a remote memory/cache read might be
- * required to invoke the original callable. Customizable inline storage
- * helps tune storage so we can store a type-erased callable with better
- * performance and locality. A real-life example of this might be a
- * folly::FunctionRef with a function pointer. The folly::FunctionRef would
- * point to the function pointer object in a remote location. This causes a
- * double-indirection at the point of invocation, and if that memory is dirty,
- * or not cached, it would cause additional cache misses. On the other hand
- * with InlineFunctionRef, inline storage would store the value of the
- * function pointer, avoiding the need to do a remote lookup to fetch the
- * value of the function pointer.
- *
- * To prevent misuse, InlineFunctionRef disallows construction from an lvalue
- * callable. This is to prevent usage where a user relies on the callable's
- * state after invocation through InlineFunctionRef. This has the potential
- * to copy the callable into inline storage when the callable is small, so we
- * might not use the same function when invoking, but rather a copy of it.
- *
- * Also note that InlineFunctionRef will always invoke the const qualified
- * version of the call operator for any callable that is passed. Regardless
- * of whether it has a non-const version. This is done to enforce the logical
- * constraint of function state being immutable.
- *
- * This class is always trivially-copyable (and therefore
- * trivially-destructible), making it suitable for use in a union without
- * requiring manual destruction.
- */
-template <typename FunctionType, std::size_t Size>
-class InlineFunctionRef;
-
-template <typename ReturnType, typename... Args, std::size_t Size>
-class InlineFunctionRef<ReturnType(Args...), Size> {
- using Storage =
- _t<std::aligned_storage<Size - sizeof(uintptr_t), sizeof(uintptr_t)>>;
- using Call = ReturnType (*)(const Storage&, Args&&...);
-
- struct InSituTag {};
- struct RefTag {};
-
- static_assert(
- (Size % sizeof(uintptr_t)) == 0,
- "Size has to be a multiple of sizeof(uintptr_t)");
- static_assert(Size >= 2 * sizeof(uintptr_t), "This doesn't work");
- static_assert(alignof(Call) == alignof(Storage), "Mismatching alignments");
-
- // This defines a mode tag that is used in the construction of
- // InlineFunctionRef to determine the storage and indirection method for the
- // passed callable.
- //
- // This requires that the we pass in a type that is not ref-qualified.
- template <typename Func>
- using ConstructMode = _t<std::conditional<
- folly::is_trivially_copyable<Func>{} &&
- (sizeof(Func) <= sizeof(Storage)) &&
- (alignof(Func) <= alignof(Storage)),
- InSituTag,
- RefTag>>;
-
- public:
- /**
- * InlineFunctionRef can be constructed from a nullptr, callable or another
- * InlineFunctionRef with the same size. These are the constructors that
- * don't take a callable.
- *
- * InlineFunctionRef is meant to be trivially copyable so we default the
- * constructors and assignment operators.
- */
- InlineFunctionRef(std::nullptr_t) : call_{nullptr} {}
- InlineFunctionRef() : call_{nullptr} {}
- InlineFunctionRef(const InlineFunctionRef& other) = default;
- InlineFunctionRef(InlineFunctionRef&&) = default;
- InlineFunctionRef& operator=(const InlineFunctionRef&) = default;
- InlineFunctionRef& operator=(InlineFunctionRef&&) = default;
-
- /**
- * Constructors from callables.
- *
- * If all of the following conditions are satisfied, then we store the
- * callable in the inline storage:
- *
- * 1) The function has been passed as an rvalue, meaning that there is no
- * use of the original in the user's code after it has been passed to
- * us.
- * 2) Size of the callable is less than the size of the inline storage
- * buffer.
- * 3) The callable is trivially constructible and destructible.
- *
- * If any one of the above conditions is not satisfied, we fall back to
- * reference semantics and store the function as a pointer, and add a level
- * of indirection through type erasure.
- */
- template <
- typename Func,
- _t<std::enable_if<
- !std::is_same<_t<std::decay<Func>>, InlineFunctionRef>{} &&
- !std::is_reference<Func>{} &&
- std::is_convertible<
- decltype(std::declval<Func&&>()(std::declval<Args&&>()...)),
- ReturnType>{}>>* = nullptr>
- InlineFunctionRef(Func&& func) {
- // We disallow construction from lvalues, so assert that this is not a
- // reference type. When invoked with an lvalue, Func is a lvalue
- // reference type, when invoked with an rvalue, Func is not ref-qualified.
- static_assert(
- !std::is_reference<Func>{},
- "InlineFunctionRef cannot be used with lvalues");
- static_assert(std::is_rvalue_reference<Func&&>{}, "");
- construct(ConstructMode<Func>{}, folly::as_const(func));
- }
-
- /**
- * The call operator uses the function pointer and a reference to the
- * storage to do the dispatch. The function pointer takes care of the
- * appropriate casting.
- */
- ReturnType operator()(Args... args) const {
- return call_(storage_, static_cast<Args&&>(args)...);
- }
-
- /**
- * We have a function engaged if the call function points to anything other
- * than null.
- */
- operator bool() const noexcept {
- return call_;
- }
-
- private:
- friend class InlineFunctionRefTest;
-
- /**
- * Inline storage constructor implementation.
- */
- template <typename Func>
- void construct(InSituTag, Func& func) {
- using Value = _t<std::remove_reference<Func>>;
-
- // Assert that the following two assumptions are valid
- // 1) fit in the storage space we have and match alignments, and
- // 2) be invocable in a const context, it does not make sense to copy a
- // callable into inline storage if it makes state local
- // modifications.
- static_assert(alignof(Value) <= alignof(Storage), "");
- static_assert(is_invocable<const _t<std::decay<Func>>, Args&&...>{}, "");
- static_assert(folly::is_trivially_copyable<Value>{}, "");
-
- new (&storage_) Value{func};
- call_ = &callInline<Value>;
- }
-
- /**
- * Ref storage constructor implementation. This is identical to
- * folly::FunctionRef.
- */
- template <typename Func>
- void construct(RefTag, Func& func) {
- // store a pointer to the function
- using Pointer = _t<std::add_pointer<_t<std::remove_reference<Func>>>>;
- new (&storage_) Pointer{&func};
- call_ = &callPointer<Pointer>;
- }
-
- template <typename Func>
- static ReturnType callInline(const Storage& object, Args&&... args) {
- // The only type of pointer allowed is a function pointer, no other
- // pointer types are invocable.
- static_assert(
- !std::is_pointer<Func>::value ||
- std::is_function<_t<std::remove_pointer<Func>>>::value,
- "");
- return (*folly::launder(reinterpret_cast<const Func*>(&object)))(
- static_cast<Args&&>(args)...);
- }
-
- template <typename Func>
- static ReturnType callPointer(const Storage& object, Args&&... args) {
- // When the function we were instantiated with was not trivial, the given
- // pointer points to a pointer, which pointers to the callable. So we
- // cast to a pointer and then to the pointee.
- static_assert(std::is_pointer<Func>::value, "");
- return (**folly::launder(reinterpret_cast<const Func*>(&object)))(
- static_cast<Args&&>(args)...);
- }
-
- Call call_;
- Storage storage_;
-};
-
-} // namespace detail
-} // namespace folly