/*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+/*
+ * @author Eric Niebler (eniebler@fb.com), Sven Over (over@fb.com)
+ * Acknowledgements: Giuseppe Ottaviano (ott@fb.com)
+ */
/**
* @class Function
* type of the embedded callable. E.g. a `folly::Function<int(int)>`
* can wrap callables that return an `int` when passed an `int`. This can be a
* function pointer or any class object implementing one or both of
+ *
* int operator(int);
* int operator(int) const;
+ *
* If both are defined, the non-const one takes precedence.
*
* Unlike `std::function`, a `folly::Function` can wrap objects that are not
* const-reference.
*
* For example:
+ *
* class Foo {
* public:
* void operator()() {
* foo_();
* }
* };
+ *
* Even though `mutateFoo` is a const-method, so it can only reference `foo_`
* as const, it is able to call the non-const `operator()` of the Foo
* object that is embedded in the foo_ function.
* wrapped function in a const context, you can wrap any functor that
* implements either or both of const and non-const `operator()`.
*
- * The first (and usually only specified) template parameter of
- * `folly::Function`, the `FunctionType`, can be const-qualified. Be aware
- * that the const is part of the function signature. It does not mean that
- * the function type is a const type.
+ * The template parameter of `folly::Function`, the `FunctionType`, can be
+ * const-qualified. Be aware that the const is part of the function signature.
+ * It does not mean that the function type is a const type.
*
* using FunctionType = R(Args...);
* using ConstFunctionType = R(Args...) const;
* Casting from a non-const to a const signature is potentially dangerous,
* as it means that a function that may change its inner state when invoked
* is made possible to call from a const context. Therefore this cast does
- * not happen implicitly. The function `folly::constCastfolly::Function` can
+ * not happen implicitly. The function `folly::constCastFunction` can
* be used to perform the cast.
*
* // Mutable lambda: can only be stored in a non-const folly::Function:
*
* // const-cast to a const folly::Function:
* folly::Function<void() const> print_number_const =
- * constCastfolly::Function(std::move(print_number));
+ * constCastFunction(std::move(print_number));
*
* When to use const function types?
* Generally, only when you need them. When you use a `folly::Function` as a
*
* std::function<void(void)> stdfunc = someCallable;
*
- * folly::Function<void(void) const> uniqfunc = constCastfolly::Function(
+ * folly::Function<void(void) const> uniqfunc = constCastFunction(
* folly::Function<void(void)>(someCallable)
* );
*
* You need to wrap the callable first in a non-const `folly::Function` to
* select a non-const invoke operator (or the const one if no non-const one is
* present), and then move it into a const `folly::Function` using
- * `constCastfolly::Function`.
- * The name of `constCastfolly::Function` should warn you that something
+ * `constCastFunction`.
+ * The name of `constCastFunction` should warn you that something
* potentially dangerous is happening. As a matter of fact, using
* `std::function` always involves this potentially dangerous aspect, which
* is why it is not considered fully const-safe or even const-correct.
* However, in most of the cases you will not need the dangerous aspect at all.
* Either you do not require invokation of the function from a const context,
- * in which case you do not need to use `constCastfolly::Function` and just
+ * in which case you do not need to use `constCastFunction` and just
* use the inner `folly::Function` in the example above, i.e. just use a
* non-const `folly::Function`. Or, you may need invokation from const, but
* the callable you are wrapping does not mutate its state (e.g. it is a class
* object and implements `operator() const`, or it is a normal,
* non-mutable lambda), in which case you can wrap the callable in a const
- * `folly::Function` directly, without using `constCastfolly::Function`.
+ * `folly::Function` directly, without using `constCastFunction`.
* Only if you require invokation from a const context of a callable that
* may mutate itself when invoked you have to go through the above procedure.
* However, in that case what you do is potentially dangerous and requires
* the equivalent of a `const_cast`, hence you need to call
- * `constCastfolly::Function`.
- *
- * `folly::Function` also has two additional template paremeters:
- * * `NTM`: if set to `folly::FunctionMoveCtor::NO_THROW`, the
- * `folly::Function` object is guaranteed to be nothrow move constructible.
- * The downside is that any function object that itself is
- * not nothrow move constructible cannot be stored in-place in the
- * `folly::Function` object and will be stored on the heap instead.
- * * `EmbedFunctorSize`: a number of bytes that will be reserved in the
- * `folly::Function` object to store callable objects in-place. If you
- * wrap a callable object bigger than this in a `folly::Function` object,
- * it will be stored on the heap and the `folly::Function` object will keep
- * a `std::unique_ptr` to it.
+ * `constCastFunction`.
*/
#pragma once
#include <functional>
+#include <memory>
+#include <new>
#include <type_traits>
-#include <typeinfo>
#include <utility>
-#include <folly/ScopeGuard.h>
-#include <folly/portability/Constexpr.h>
+#include <folly/CppAttributes.h>
+#include <folly/Portability.h>
+#include <folly/Traits.h>
+#include <folly/functional/Invoke.h>
namespace folly {
-enum class FunctionMoveCtor { NO_THROW, MAY_THROW };
-
-template <
- typename FunctionType,
- FunctionMoveCtor NTM = FunctionMoveCtor::NO_THROW,
- size_t EmbedFunctorSize = (NTM == FunctionMoveCtor::NO_THROW)
- ? sizeof(void (*)(void))
- : sizeof(std::function<void(void)>)>
+template <typename FunctionType>
class Function;
-} // folly
+template <typename ReturnType, typename... Args>
+Function<ReturnType(Args...) const> constCastFunction(
+ Function<ReturnType(Args...)>&&) noexcept;
-// boring predeclarations and details
-#include "Function-pre.h"
+namespace detail {
+namespace function {
-namespace folly {
+enum class Op { MOVE, NUKE, FULL, HEAP };
+
+union Data {
+ void* big;
+ std::aligned_storage<6 * sizeof(void*)>::type tiny;
+};
-template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
-class Function final
- : public detail::function::FunctionTypeTraits<FunctionType>::
- template InvokeOperator<
- Function<FunctionType, NTM, EmbedFunctorSize>>,
- public detail::function::MaybeUnaryOrBinaryFunction<FunctionType> {
- private:
- using Traits = detail::function::FunctionTypeTraits<FunctionType>;
- static_assert(
- Traits::SuitableForFunction::value,
- "Function<FunctionType>: FunctionType must be of the "
- "form 'R(Args...)' or 'R(Args...) const'");
-
- using ThisType = Function<FunctionType, NTM, EmbedFunctorSize>;
- using InvokeOperator = typename Traits::template InvokeOperator<ThisType>;
-
- static constexpr bool hasNoExceptMoveCtor() noexcept {
- return NTM == FunctionMoveCtor::NO_THROW;
+template <typename Fun, typename = Fun*>
+using IsSmall = Conjunction<
+ std::integral_constant<bool, (sizeof(Fun) <= sizeof(Data::tiny))>,
+ std::is_nothrow_move_constructible<Fun>>;
+using SmallTag = std::true_type;
+using HeapTag = std::false_type;
+
+template <typename T>
+struct NotFunction : std::true_type {};
+template <typename T>
+struct NotFunction<Function<T>> : std::false_type {};
+
+template <typename T>
+using EnableIfNotFunction =
+ typename std::enable_if<NotFunction<T>::value>::type;
+
+struct CoerceTag {};
+
+template <typename T>
+bool isNullPtrFn(T* p) {
+ return p == nullptr;
+}
+template <typename T>
+std::false_type isNullPtrFn(T&&) {
+ return {};
+}
+
+inline bool uninitNoop(Op, Data*, Data*) {
+ return false;
+}
+
+template <typename FunctionType>
+struct FunctionTraits;
+
+template <typename ReturnType, typename... Args>
+struct FunctionTraits<ReturnType(Args...)> {
+ using Call = ReturnType (*)(Data&, Args&&...);
+ using IsConst = std::false_type;
+ using ConstSignature = ReturnType(Args...) const;
+ using NonConstSignature = ReturnType(Args...);
+ using OtherSignature = ConstSignature;
+
+ template <typename F, typename G = typename std::decay<F>::type>
+ using ResultOf = decltype(
+ static_cast<ReturnType>(std::declval<G&>()(std::declval<Args>()...)));
+
+ template <typename Fun>
+ static ReturnType callSmall(Data& p, Args&&... args) {
+ return static_cast<ReturnType>((*static_cast<Fun*>(
+ static_cast<void*>(&p.tiny)))(static_cast<Args&&>(args)...));
+ }
+
+ template <typename Fun>
+ static ReturnType callBig(Data& p, Args&&... args) {
+ return static_cast<ReturnType>(
+ (*static_cast<Fun*>(p.big))(static_cast<Args&&>(args)...));
+ }
+
+ static ReturnType uninitCall(Data&, Args&&...) {
+ throw std::bad_function_call();
+ }
+
+ ReturnType operator()(Args... args) {
+ auto& fn = *static_cast<Function<NonConstSignature>*>(this);
+ return fn.call_(fn.data_, static_cast<Args&&>(args)...);
+ }
+
+ class SharedProxy {
+ std::shared_ptr<Function<NonConstSignature>> sp_;
+
+ public:
+ explicit SharedProxy(Function<NonConstSignature>&& func)
+ : sp_(std::make_shared<Function<NonConstSignature>>(std::move(func))) {}
+ ReturnType operator()(Args&&... args) const {
+ return (*sp_)(static_cast<Args&&>(args)...);
+ }
};
+};
- public:
- // not copyable
- Function(Function const&) = delete;
- Function& operator=(Function const&) = delete;
+template <typename ReturnType, typename... Args>
+struct FunctionTraits<ReturnType(Args...) const> {
+ using Call = ReturnType (*)(Data&, Args&&...);
+ using IsConst = std::true_type;
+ using ConstSignature = ReturnType(Args...) const;
+ using NonConstSignature = ReturnType(Args...);
+ using OtherSignature = NonConstSignature;
+
+ template <typename F, typename G = typename std::decay<F>::type>
+ using ResultOf = decltype(static_cast<ReturnType>(
+ std::declval<const G&>()(std::declval<Args>()...)));
+
+ template <typename Fun>
+ static ReturnType callSmall(Data& p, Args&&... args) {
+ return static_cast<ReturnType>((*static_cast<const Fun*>(
+ static_cast<void*>(&p.tiny)))(static_cast<Args&&>(args)...));
+ }
+
+ template <typename Fun>
+ static ReturnType callBig(Data& p, Args&&... args) {
+ return static_cast<ReturnType>(
+ (*static_cast<const Fun*>(p.big))(static_cast<Args&&>(args)...));
+ }
+
+ static ReturnType uninitCall(Data&, Args&&...) {
+ throw std::bad_function_call();
+ }
+
+ ReturnType operator()(Args... args) const {
+ auto& fn = *static_cast<const Function<ConstSignature>*>(this);
+ return fn.call_(fn.data_, static_cast<Args&&>(args)...);
+ }
+
+ class SharedProxy {
+ std::shared_ptr<Function<ConstSignature>> sp_;
+ public:
+ explicit SharedProxy(Function<ConstSignature>&& func)
+ : sp_(std::make_shared<Function<ConstSignature>>(std::move(func))) {}
+ ReturnType operator()(Args&&... args) const {
+ return (*sp_)(static_cast<Args&&>(args)...);
+ }
+ };
+};
+
+template <typename Fun>
+bool execSmall(Op o, Data* src, Data* dst) {
+ switch (o) {
+ case Op::MOVE:
+ ::new (static_cast<void*>(&dst->tiny))
+ Fun(std::move(*static_cast<Fun*>(static_cast<void*>(&src->tiny))));
+ FOLLY_FALLTHROUGH;
+ case Op::NUKE:
+ static_cast<Fun*>(static_cast<void*>(&src->tiny))->~Fun();
+ break;
+ case Op::FULL:
+ return true;
+ case Op::HEAP:
+ break;
+ }
+ return false;
+}
+
+template <typename Fun>
+bool execBig(Op o, Data* src, Data* dst) {
+ switch (o) {
+ case Op::MOVE:
+ dst->big = src->big;
+ src->big = nullptr;
+ break;
+ case Op::NUKE:
+ delete static_cast<Fun*>(src->big);
+ break;
+ case Op::FULL:
+ case Op::HEAP:
+ break;
+ }
+ return true;
+}
+
+} // namespace function
+} // namespace detail
+
+template <typename FunctionType>
+class Function final : private detail::function::FunctionTraits<FunctionType> {
+ // These utility types are defined outside of the template to reduce
+ // the number of instantiations, and then imported in the class
+ // namespace for convenience.
+ using Data = detail::function::Data;
+ using Op = detail::function::Op;
+ using SmallTag = detail::function::SmallTag;
+ using HeapTag = detail::function::HeapTag;
+ using CoerceTag = detail::function::CoerceTag;
+
+ using Traits = detail::function::FunctionTraits<FunctionType>;
+ using Call = typename Traits::Call;
+ using Exec = bool (*)(Op, Data*, Data*);
+
+ template <typename Fun>
+ using IsSmall = detail::function::IsSmall<Fun>;
+
+ // The `data_` member is mutable to allow `constCastFunction` to work without
+ // invoking undefined behavior. Const-correctness is only violated when
+ // `FunctionType` is a const function type (e.g., `int() const`) and `*this`
+ // is the result of calling `constCastFunction`.
+ mutable Data data_;
+ Call call_{&Traits::uninitCall};
+ Exec exec_{&detail::function::uninitNoop};
+
+ friend Traits;
+ friend Function<typename Traits::ConstSignature> folly::constCastFunction<>(
+ Function<typename Traits::NonConstSignature>&&) noexcept;
+ friend class Function<typename Traits::OtherSignature>;
+
+ template <typename Fun>
+ Function(Fun&& fun, SmallTag) noexcept {
+ using FunT = typename std::decay<Fun>::type;
+ if (!detail::function::isNullPtrFn(fun)) {
+ ::new (static_cast<void*>(&data_.tiny)) FunT(static_cast<Fun&&>(fun));
+ call_ = &Traits::template callSmall<FunT>;
+ exec_ = &detail::function::execSmall<FunT>;
+ }
+ }
+
+ template <typename Fun>
+ Function(Fun&& fun, HeapTag) {
+ using FunT = typename std::decay<Fun>::type;
+ data_.big = new FunT(static_cast<Fun&&>(fun));
+ call_ = &Traits::template callBig<FunT>;
+ exec_ = &detail::function::execBig<FunT>;
+ }
+
+ template <typename Signature>
+ Function(Function<Signature>&& that, CoerceTag)
+ : Function(static_cast<Function<Signature>&&>(that), HeapTag{}) {}
+
+ Function(
+ Function<typename Traits::OtherSignature>&& that,
+ CoerceTag) noexcept {
+ that.exec_(Op::MOVE, &that.data_, &data_);
+ std::swap(call_, that.call_);
+ std::swap(exec_, that.exec_);
+ }
+
+ public:
/**
* Default constructor. Constructs an empty Function.
*/
- Function() noexcept {
- initializeEmptyExecutor();
- }
+ Function() = default;
- ~Function() {
- destroyExecutor();
+ // not copyable
+ Function(const Function&) = delete;
- static_assert(
- kStorageSize == sizeof(*this),
- "There is something wrong with the size of Function");
- }
+#if __OBJC__
+ // Make sure Objective C blocks are copied
+ template <class ReturnType, class... Args>
+ /*implicit*/ Function(ReturnType (^objCBlock)(Args... args))
+ : Function([blockCopy = (ReturnType (^)(Args...))[objCBlock copy]](
+ Args... args) { return blockCopy(args...); }){};
+#endif
- // construct/assign from Function
/**
* Move constructor
*/
- Function(Function&& other) noexcept(hasNoExceptMoveCtor());
+ Function(Function&& that) noexcept {
+ that.exec_(Op::MOVE, &that.data_, &data_);
+ std::swap(call_, that.call_);
+ std::swap(exec_, that.exec_);
+ }
+
/**
- * Move assignment operator
+ * Constructs an empty `Function`.
*/
- Function& operator=(Function&& rhs) noexcept(hasNoExceptMoveCtor());
+ /* implicit */ Function(std::nullptr_t) noexcept {}
/**
- * Constructs a `Function` by moving from one with different template
- * parameters with regards to const-ness, no-except-movability and internal
- * storage size.
+ * Constructs a new `Function` from any callable object that is _not_ a
+ * `folly::Function`. This handles function pointers, pointers to static
+ * member functions, `std::reference_wrapper` objects, `std::function`
+ * objects, and arbitrary objects that implement `operator()` if the parameter
+ * signature matches (i.e. it returns an object convertible to `R` when called
+ * with `Args...`).
+ *
+ * \note `typename = ResultOf<Fun>` prevents this overload from being
+ * selected by overload resolution when `fun` is not a compatible function.
+ *
+ * \note The noexcept requires some explanation. IsSmall is true when the
+ * decayed type fits within the internal buffer and is noexcept-movable. But
+ * this ctor might copy, not move. What we need here, if this ctor does a
+ * copy, is that this ctor be noexcept when the copy is noexcept. That is not
+ * checked in IsSmall, and shouldn't be, because once the Function is
+ * constructed, the contained object is never copied. This check is for this
+ * ctor only, in the case that this ctor does a copy.
*/
template <
- typename OtherFunctionType,
- FunctionMoveCtor OtherNTM,
- size_t OtherEmbedFunctorSize>
- Function(
- Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>&& other,
- typename std::enable_if<std::is_same<
- typename Traits::NonConstFunctionType,
- typename detail::function::FunctionTypeTraits<
- OtherFunctionType>::NonConstFunctionType>::value>::type* =
- 0) noexcept(OtherNTM == FunctionMoveCtor::NO_THROW &&
- EmbedFunctorSize >= OtherEmbedFunctorSize);
+ typename Fun,
+ typename = detail::function::EnableIfNotFunction<Fun>,
+ typename = typename Traits::template ResultOf<Fun>>
+ /* implicit */ Function(Fun fun) noexcept(
+ IsSmall<Fun>::value && noexcept(Fun(std::declval<Fun>())))
+ : Function(static_cast<Fun&&>(fun), IsSmall<Fun>{}) {}
/**
- * Moves a `Function` with different template parameters with regards
- * to const-ness, no-except-movability and internal storage size into this
- * one.
+ * For move-constructing from a `folly::Function<X(Ys...) [const?]>`.
+ * For a `Function` with a `const` function type, the object must be
+ * callable from a `const`-reference, i.e. implement `operator() const`.
+ * For a `Function` with a non-`const` function type, the object will
+ * be called from a non-const reference, which means that it will execute
+ * a non-const `operator()` if it is defined, and falls back to
+ * `operator() const` otherwise.
*/
template <
- typename RhsFunctionType,
- FunctionMoveCtor RhsNTM,
- size_t RhsEmbedFunctorSize>
- Function& operator=(Function<RhsFunctionType, RhsNTM, RhsEmbedFunctorSize>&&
- rhs) noexcept(RhsNTM == FunctionMoveCtor::NO_THROW);
+ typename Signature,
+ typename = typename Traits::template ResultOf<Function<Signature>>>
+ Function(Function<Signature>&& that) noexcept(
+ noexcept(Function(std::move(that), CoerceTag{})))
+ : Function(std::move(that), CoerceTag{}) {}
/**
- * Constructs an empty `Function`.
+ * If `ptr` is null, constructs an empty `Function`. Otherwise,
+ * this constructor is equivalent to `Function(std::mem_fn(ptr))`.
*/
- /* implicit */ Function(std::nullptr_t) noexcept : Function() {}
+ template <
+ typename Member,
+ typename Class,
+ // Prevent this overload from being selected when `ptr` is not a
+ // compatible member function pointer.
+ typename = decltype(Function(std::mem_fn((Member Class::*)0)))>
+ /* implicit */ Function(Member Class::*ptr) noexcept {
+ if (ptr) {
+ *this = std::mem_fn(ptr);
+ }
+ }
- /**
- * Clears this `Function`.
- */
- Function& operator=(std::nullptr_t) noexcept {
- destroyExecutor();
- initializeEmptyExecutor();
+ ~Function() {
+ exec_(Op::NUKE, &data_, nullptr);
+ }
+
+ Function& operator=(const Function&) = delete;
+
+#if __OBJC__
+ // Make sure Objective C blocks are copied
+ template <class ReturnType, class... Args>
+ /* implicit */ Function &operator=(ReturnType (^objCBlock)(Args... args)) {
+ (*this) = [blockCopy = (ReturnType (^)(Args...))[objCBlock copy]](
+ Args... args) { return blockCopy(args...); };
return *this;
}
+#endif
/**
- * Constructs a new `Function` from any callable object. This
- * handles function pointers, pointers to static member functions,
- * `std::reference_wrapper` objects, `std::function` objects, and arbitrary
- * objects that implement `operator()` if the parameter signature
- * matches (i.e. it returns R when called with Args...).
- * For a `Function` with a const function type, the object must be
- * callable from a const-reference, i.e. implement `operator() const`.
- * For a `Function` with a non-const function type, the object will
- * be called from a non-const reference, which means that it will execute
- * a non-const `operator()` if it is defined, and falls back to
- * `operator() const` otherwise
+ * Move assignment operator
+ *
+ * \note Leaves `that` in a valid but unspecified state. If `&that == this`
+ * then `*this` is left in a valid but unspecified state.
*/
- template <typename F>
- /* implicit */ Function(
- F&& f,
- typename std::enable_if<
- detail::function::IsCallable<F, FunctionType>::value>::type* =
- 0) noexcept(noexcept(typename std::decay<F>::
- type(std::forward<F>(f)))) {
- createExecutor(std::forward<F>(f));
+ Function& operator=(Function&& that) noexcept {
+ // Q: Why is it safe to destroy and reconstruct this object in place?
+ // A: Two reasons: First, `Function` is a final class, so in doing this
+ // we aren't slicing off any derived parts. And second, the move
+ // operation is guaranteed not to throw so we always leave the object
+ // in a valid state.
+ // In the case of self-move (this == &that), this leaves the object in
+ // a default-constructed state. First the object is destroyed, then we
+ // pass the destroyed object to the move constructor. The first thing the
+ // move constructor does is default-construct the object. That object is
+ // "moved" into itself, which is a no-op for a default-constructed Function.
+ this->~Function();
+ ::new (this) Function(std::move(that));
+ return *this;
}
/**
- * Assigns a callable object to this `Function`.
+ * Assigns a callable object to this `Function`. If the operation fails,
+ * `*this` is left unmodified.
+ *
+ * \note `typename = ResultOf<Fun>` prevents this overload from being
+ * selected by overload resolution when `fun` is not a compatible function.
*/
- template <typename F>
- typename std::enable_if<
- detail::function::IsCallable<F, FunctionType>::value,
- Function&>::type
- operator=(F&& f) noexcept(
- noexcept(typename std::decay<F>::type(std::forward<F>(f)))) {
- destroyExecutor();
- SCOPE_FAIL {
- initializeEmptyExecutor();
- };
- createExecutor(std::forward<F>(f));
+ template <typename Fun, typename = decltype(Function(std::declval<Fun>()))>
+ Function& operator=(Fun fun) noexcept(
+ noexcept(/* implicit */ Function(std::declval<Fun>()))) {
+ // Doing this in place is more efficient when we can do so safely.
+ if (noexcept(/* implicit */ Function(std::declval<Fun>()))) {
+ // Q: Why is is safe to destroy and reconstruct this object in place?
+ // A: See the explanation in the move assignment operator.
+ this->~Function();
+ ::new (this) Function(static_cast<Fun&&>(fun));
+ } else {
+ // Construct a temporary and (nothrow) swap.
+ Function(static_cast<Fun&&>(fun)).swap(*this);
+ }
return *this;
}
/**
- * Exchanges the callable objects of `*this` and `other`. `other` can be
- * a Function with different settings with regard to
- * no-except-movability and internal storage size, but must match
- * `*this` with regards to return type and argument types.
+ * For assigning from a `Function<X(Ys..) [const?]>`.
*/
- template <FunctionMoveCtor OtherNTM, size_t OtherEmbedFunctorSize>
- void
- swap(Function<FunctionType, OtherNTM, OtherEmbedFunctorSize>& o) noexcept(
- hasNoExceptMoveCtor() && OtherNTM == FunctionMoveCtor::NO_THROW);
+ template <
+ typename Signature,
+ typename = typename Traits::template ResultOf<Function<Signature>>>
+ Function& operator=(Function<Signature>&& that) noexcept(
+ noexcept(Function(std::move(that)))) {
+ return (*this = Function(std::move(that)));
+ }
/**
- * Returns `true` if this `Function` contains a callable, i.e. is
- * non-empty.
+ * Clears this `Function`.
*/
- explicit operator bool() const noexcept;
+ Function& operator=(std::nullptr_t) noexcept {
+ return (*this = Function());
+ }
/**
- * Returns `true` if this `Function` stores the callable on the
- * heap. If `false` is returned, there has been no additional memory
- * allocation and the callable is stored inside the `Function`
- * object itself.
+ * If `ptr` is null, clears this `Function`. Otherwise, this assignment
+ * operator is equivalent to `*this = std::mem_fn(ptr)`.
*/
- bool hasAllocatedMemory() const noexcept;
+ template <typename Member, typename Class>
+ auto operator=(Member Class::*ptr) noexcept
+ // Prevent this overload from being selected when `ptr` is not a
+ // compatible member function pointer.
+ -> decltype(operator=(std::mem_fn(ptr))) {
+ return ptr ? (*this = std::mem_fn(ptr)) : (*this = Function());
+ }
/**
- * Returns the `type_info` (as returned by `typeid`) of the callable stored
- * in this `Function`. Returns `typeid(void)` if empty.
+ * Call the wrapped callable object with the specified arguments.
*/
- std::type_info const& target_type() const noexcept;
+ using Traits::operator();
/**
- * Returns a pointer to the stored callable if its type matches `T`, and
- * `nullptr` otherwise.
+ * Exchanges the callable objects of `*this` and `that`.
*/
- template <typename T>
- T* target() noexcept;
+ void swap(Function& that) noexcept {
+ std::swap(*this, that);
+ }
/**
- * Returns a const-pointer to the stored callable if its type matches `T`,
- * and `nullptr` otherwise.
+ * Returns `true` if this `Function` contains a callable, i.e. is
+ * non-empty.
*/
- template <typename T>
- const T* target() const noexcept;
+ explicit operator bool() const noexcept {
+ return exec_(Op::FULL, nullptr, nullptr);
+ }
/**
- * Move out this `Function` into one with a const function type.
- *
- * This is a potentially dangerous operation, equivalent to a `const_cast`.
- * This converts a `Function` with a non-const function type, i.e.
- * one that can only be called when in the form of a non-const reference,
- * into one that can be called in a const context. Use at your own risk!
+ * Returns `true` if this `Function` stores the callable on the
+ * heap. If `false` is returned, there has been no additional memory
+ * allocation and the callable is stored inside the `Function`
+ * object itself.
*/
- Function<typename Traits::ConstFunctionType, NTM, EmbedFunctorSize>
- castToConstFunction() && noexcept(hasNoExceptMoveCtor());
-
- using SignatureType = FunctionType;
- using ResultType = typename Traits::ResultType;
- using ArgsTuple = typename Traits::ArgsTuple;
-
- private:
- template <class, FunctionMoveCtor, size_t>
- friend class Function;
-
- friend struct detail::function::FunctionTypeTraits<FunctionType>;
-
- using ExecutorIf =
- typename detail::function::Executors<FunctionType>::ExecutorIf;
- using EmptyExecutor =
- typename detail::function::Executors<FunctionType>::EmptyExecutor;
- template <typename F, typename SelectFunctionTag>
- using FunctorPtrExecutor = typename detail::function::Executors<
- FunctionType>::template FunctorPtrExecutor<F, SelectFunctionTag>;
- template <typename F, typename SelectFunctionTag>
- using FunctorExecutor = typename detail::function::Executors<
- FunctionType>::template FunctorExecutor<F, SelectFunctionTag>;
-
- template <typename T>
- T const* access() const;
+ bool hasAllocatedMemory() const noexcept {
+ return exec_(Op::HEAP, nullptr, nullptr);
+ }
- template <typename T>
- T* access();
+ using typename Traits::SharedProxy;
- void initializeEmptyExecutor() noexcept;
+ /**
+ * Move this `Function` into a copyable callable object, of which all copies
+ * share the state.
+ */
+ SharedProxy asSharedProxy() && {
+ return SharedProxy{std::move(*this)};
+ }
- template <typename F>
- void createExecutor(F&& f) noexcept(
- noexcept(typename std::decay<F>::type(std::forward<F>(f))));
+ /**
+ * Construct a `std::function` by moving in the contents of this `Function`.
+ * Note that the returned `std::function` will share its state (i.e. captured
+ * data) across all copies you make of it, so be very careful when copying.
+ */
+ std::function<typename Traits::NonConstSignature> asStdFunction() && {
+ return std::move(*this).asSharedProxy();
+ }
+};
- void destroyExecutor() noexcept;
+template <typename FunctionType>
+void swap(Function<FunctionType>& lhs, Function<FunctionType>& rhs) noexcept {
+ lhs.swap(rhs);
+}
- struct MinStorageSize;
+template <typename FunctionType>
+bool operator==(const Function<FunctionType>& fn, std::nullptr_t) {
+ return !fn;
+}
- typename std::aligned_storage<MinStorageSize::value>::type data_;
- static constexpr size_t kStorageSize = sizeof(data_);
-};
+template <typename FunctionType>
+bool operator==(std::nullptr_t, const Function<FunctionType>& fn) {
+ return !fn;
+}
-// operator==
-template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
-inline bool operator==(
- Function<FunctionType, NTM, EmbedFunctorSize> const& f,
- std::nullptr_t) noexcept {
- return !f;
+template <typename FunctionType>
+bool operator!=(const Function<FunctionType>& fn, std::nullptr_t) {
+ return !(fn == nullptr);
}
-template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
-inline bool operator==(
- std::nullptr_t,
- Function<FunctionType, NTM, EmbedFunctorSize> const& f) noexcept {
- return !f;
+template <typename FunctionType>
+bool operator!=(std::nullptr_t, const Function<FunctionType>& fn) {
+ return !(nullptr == fn);
}
-template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
-inline bool operator!=(
- Function<FunctionType, NTM, EmbedFunctorSize> const& f,
- std::nullptr_t) noexcept {
- return !!f;
+/**
+ * NOTE: See detailed note about `constCastFunction` at the top of the file.
+ * This is potentially dangerous and requires the equivalent of a `const_cast`.
+ */
+template <typename ReturnType, typename... Args>
+Function<ReturnType(Args...) const> constCastFunction(
+ Function<ReturnType(Args...)>&& that) noexcept {
+ return Function<ReturnType(Args...) const>{std::move(that),
+ detail::function::CoerceTag{}};
}
-template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
-inline bool operator!=(
- std::nullptr_t,
- Function<FunctionType, NTM, EmbedFunctorSize> const& f) noexcept {
- return !!f;
+template <typename ReturnType, typename... Args>
+Function<ReturnType(Args...) const> constCastFunction(
+ Function<ReturnType(Args...) const>&& that) noexcept {
+ return std::move(that);
}
+namespace detail {
+namespace function {
+template <typename Fun, typename FunctionType, typename = void>
+struct IsCallableAsImpl : std::false_type {};
+
+template <typename Fun, typename ReturnType, typename... Args>
+struct IsCallableAsImpl<
+ Fun,
+ ReturnType(Args...),
+ void_t<typename std::result_of<Fun && (Args && ...)>::type>>
+ : std::is_convertible<
+ typename std::result_of<Fun && (Args && ...)>::type,
+ ReturnType> {};
+
+template <typename Fun, typename FunctionType>
+struct IsCallableAs : IsCallableAsImpl<Fun, FunctionType> {};
+} // namespace function
+} // namespace detail
+
/**
- * Cast a `Function` into one with a const function type.
- *
- * This is a potentially dangerous operation, equivalent to a `const_cast`.
- * This converts a `Function` with a non-const function type, i.e.
- * one that can only be called when in the form of a non-const reference,
- * into one that can be called in a const context. Use at your own risk!
+ * @class FunctionRef
+ *
+ * @brief A reference wrapper for callable objects
+ *
+ * FunctionRef is similar to std::reference_wrapper, but the template parameter
+ * is the function signature type rather than the type of the referenced object.
+ * A folly::FunctionRef is cheap to construct as it contains only a pointer to
+ * the referenced callable and a pointer to a function which invokes the
+ * callable.
+ *
+ * The user of FunctionRef must be aware of the reference semantics: storing a
+ * copy of a FunctionRef is potentially dangerous and should be avoided unless
+ * the referenced object definitely outlives the FunctionRef object. Thus any
+ * function that accepts a FunctionRef parameter should only use it to invoke
+ * the referenced function and not store a copy of it. Knowing that FunctionRef
+ * itself has reference semantics, it is generally okay to use it to reference
+ * lambdas that capture by reference.
*/
-template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
-Function<
- typename detail::function::FunctionTypeTraits<
- FunctionType>::ConstFunctionType,
- NTM,
- EmbedFunctorSize>
-constCastFunction(Function<FunctionType, NTM, EmbedFunctorSize>&&
- from) noexcept(NTM == FunctionMoveCtor::NO_THROW) {
- return std::move(from).castToConstFunction();
-}
-} // folly
+template <typename FunctionType>
+class FunctionRef;
-namespace std {
-template <typename FunctionType, bool NOM1, bool NOM2, size_t S1, size_t S2>
-void swap(
- ::folly::Function<FunctionType, NOM1, S1>& lhs,
- ::folly::Function<FunctionType, NOM2, S2>& rhs) {
- lhs.swap(rhs);
-}
-} // std
+template <typename ReturnType, typename... Args>
+class FunctionRef<ReturnType(Args...)> final {
+ using Call = ReturnType (*)(void*, Args&&...);
+
+ static ReturnType uninitCall(void*, Args&&...) {
+ throw std::bad_function_call();
+ }
+
+ template <typename Fun>
+ static ReturnType call(void* object, Args&&... args) {
+ using Pointer = _t<std::add_pointer<Fun>>;
+ return static_cast<ReturnType>(invoke(
+ static_cast<Fun&&>(*static_cast<Pointer>(object)),
+ static_cast<Args&&>(args)...));
+ }
+
+ void* object_{nullptr};
+ Call call_{&FunctionRef::uninitCall};
+
+ public:
+ /**
+ * Default constructor. Constructs an empty FunctionRef.
+ *
+ * Invoking it will throw std::bad_function_call.
+ */
+ FunctionRef() = default;
+
+ /**
+ * Construct a FunctionRef from a reference to a callable object.
+ */
+ template <
+ typename Fun,
+ typename std::enable_if<
+ Conjunction<
+ Negation<std::is_same<FunctionRef, _t<std::decay<Fun>>>>,
+ detail::function::IsCallableAs<Fun, ReturnType(Args...)>>::value,
+ int>::type = 0>
+ constexpr /* implicit */ FunctionRef(Fun&& fun) noexcept
+ // `Fun` may be a const type, in which case we have to do a const_cast
+ // to store the address in a `void*`. This is safe because the `void*`
+ // will be cast back to `Fun*` (which is a const pointer whenever `Fun`
+ // is a const type) inside `FunctionRef::call`
+ : object_(
+ const_cast<void*>(static_cast<void const*>(std::addressof(fun)))),
+ call_(&FunctionRef::call<Fun>) {}
+
+ ReturnType operator()(Args... args) const {
+ return call_(object_, static_cast<Args&&>(args)...);
+ }
+
+ constexpr explicit operator bool() const {
+ return object_;
+ }
+};
-#include "Function-inl.h"
+} // namespace folly