X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FFunction.h;h=c3c3699a1459a11c5abd386fa588927d450ef113;hp=d33ec9ebb4f1e102c234908658adafc4529588f8;hb=61cdb3dc27700545a65062e174d1b7f1ac15f088;hpb=08a67734a964c129f3675c159b40af8a9b26fc12 diff --git a/folly/Function.h b/folly/Function.h index d33ec9eb..c3c3699a 100644 --- a/folly/Function.h +++ b/folly/Function.h @@ -1,5 +1,5 @@ /* - * Copyright 2016 Facebook, Inc. + * Copyright 2016-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. @@ -13,6 +13,10 @@ * 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 @@ -26,8 +30,10 @@ * type of the embedded callable. E.g. a `folly::Function` * 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 @@ -42,6 +48,7 @@ * const-reference. * * For example: + * * class Foo { * public: * void operator()() { @@ -57,6 +64,7 @@ * 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. @@ -69,10 +77,9 @@ * 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; @@ -151,7 +158,7 @@ * 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: @@ -161,7 +168,7 @@ * * // const-cast to a const folly::Function: * folly::Function 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 @@ -180,359 +187,784 @@ * * std::function stdfunc = someCallable; * - * folly::Function uniqfunc = constCastfolly::Function( + * folly::Function uniqfunc = constCastFunction( * folly::Function(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 +#include +#include #include -#include #include -#include -#include +#include +#include +#include +#include namespace folly { -enum class FunctionMoveCtor { NO_THROW, MAY_THROW }; +template +class Function; + +template +Function constCastFunction( + Function&&) noexcept; + +#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE +template +Function constCastFunction( + Function&&) noexcept; +#endif + +namespace detail { +namespace function { + +enum class Op { MOVE, NUKE, FULL, HEAP }; + +union Data { + void* big; + std::aligned_storage<6 * sizeof(void*)>::type tiny; +}; + +template +using IsSmall = Conjunction< + std::integral_constant, + std::is_nothrow_move_constructible>; +using SmallTag = std::true_type; +using HeapTag = std::false_type; + +template +struct NotFunction : std::true_type {}; +template +struct NotFunction> : std::false_type {}; + +template +using EnableIfNotFunction = + typename std::enable_if::value>::type; + +struct CoerceTag {}; + +template +bool isNullPtrFn(T* p) { + return p == nullptr; +} +template +std::false_type isNullPtrFn(T&&) { + return {}; +} + +inline bool uninitNoop(Op, Data*, Data*) { + return false; +} + +template +using CallableResult = decltype(std::declval()(std::declval()...)); template < - typename FunctionType, - FunctionMoveCtor NTM = FunctionMoveCtor::NO_THROW, - size_t EmbedFunctorSize = (NTM == FunctionMoveCtor::NO_THROW) - ? sizeof(void (*)(void)) - : sizeof(std::function)> -class Function; + typename From, + typename To, + typename = typename std::enable_if< + !std::is_reference::value || std::is_reference::value>::type> +using SafeResultOf = decltype(static_cast(std::declval())); + +template +struct FunctionTraits; + +template +struct FunctionTraits { + using Call = ReturnType (*)(Data&, Args&&...); + using IsConst = std::false_type; + using ConstSignature = ReturnType(Args...) const; + using NonConstSignature = ReturnType(Args...); + using OtherSignature = ConstSignature; -} // folly + template + using ResultOf = + SafeResultOf>&, Args...>, ReturnType>; -// boring predeclarations and details -#include "Function-pre.h" + template + static ReturnType callSmall(Data& p, Args&&... args) { + return static_cast((*static_cast( + static_cast(&p.tiny)))(static_cast(args)...)); + } -namespace folly { + template + static ReturnType callBig(Data& p, Args&&... args) { + return static_cast( + (*static_cast(p.big))(static_cast(args)...)); + } + + static ReturnType uninitCall(Data&, Args&&...) { + throw std::bad_function_call(); + } -template -class Function final - : public detail::function::FunctionTypeTraits:: - template InvokeOperator< - Function>, - public detail::function::MaybeUnaryOrBinaryFunction { - private: - using Traits = detail::function::FunctionTypeTraits; - static_assert( - Traits::SuitableForFunction::value, - "Function: FunctionType must be of the " - "form 'R(Args...)' or 'R(Args...) const'"); - - using ThisType = Function; - using InvokeOperator = typename Traits::template InvokeOperator; - - static constexpr bool hasNoExceptMoveCtor() noexcept { - return NTM == FunctionMoveCtor::NO_THROW; + ReturnType operator()(Args... args) { + auto& fn = *static_cast*>(this); + return fn.call_(fn.data_, static_cast(args)...); + } + + class SharedProxy { + std::shared_ptr> sp_; + + public: + explicit SharedProxy(Function&& func) + : sp_(std::make_shared>(std::move(func))) {} + ReturnType operator()(Args&&... args) const { + return (*sp_)(static_cast(args)...); + } }; +}; - public: - // not copyable - Function(Function const&) = delete; - Function& operator=(Function const&) = delete; +template +struct FunctionTraits { + using Call = ReturnType (*)(Data&, Args&&...); + using IsConst = std::true_type; + using ConstSignature = ReturnType(Args...) const; + using NonConstSignature = ReturnType(Args...); + using OtherSignature = NonConstSignature; + + template + using ResultOf = SafeResultOf< + CallableResult>&, Args...>, + ReturnType>; + + template + static ReturnType callSmall(Data& p, Args&&... args) { + return static_cast((*static_cast( + static_cast(&p.tiny)))(static_cast(args)...)); + } + + template + static ReturnType callBig(Data& p, Args&&... args) { + return static_cast( + (*static_cast(p.big))(static_cast(args)...)); + } + + static ReturnType uninitCall(Data&, Args&&...) { + throw std::bad_function_call(); + } + + ReturnType operator()(Args... args) const { + auto& fn = *static_cast*>(this); + return fn.call_(fn.data_, static_cast(args)...); + } + + class SharedProxy { + std::shared_ptr> sp_; + + public: + explicit SharedProxy(Function&& func) + : sp_(std::make_shared>(std::move(func))) {} + ReturnType operator()(Args&&... args) const { + return (*sp_)(static_cast(args)...); + } + }; +}; + +#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE +template +struct FunctionTraits { + using Call = ReturnType (*)(Data&, Args&&...) noexcept; + using IsConst = std::false_type; + using ConstSignature = ReturnType(Args...) const noexcept; + using NonConstSignature = ReturnType(Args...) noexcept; + using OtherSignature = ConstSignature; + + template + using ResultOf = + SafeResultOf>&, Args...>, ReturnType>; + + template + static ReturnType callSmall(Data& p, Args&&... args) noexcept { + return static_cast((*static_cast( + static_cast(&p.tiny)))(static_cast(args)...)); + } + + template + static ReturnType callBig(Data& p, Args&&... args) noexcept { + return static_cast( + (*static_cast(p.big))(static_cast(args)...)); + } + + static ReturnType uninitCall(Data&, Args&&...) noexcept { + throw std::bad_function_call(); + } + + ReturnType operator()(Args... args) noexcept { + auto& fn = *static_cast*>(this); + return fn.call_(fn.data_, static_cast(args)...); + } + + class SharedProxy { + std::shared_ptr> sp_; + + public: + explicit SharedProxy(Function&& func) + : sp_(std::make_shared>(std::move(func))) {} + ReturnType operator()(Args&&... args) const { + return (*sp_)(static_cast(args)...); + } + }; +}; + +template +struct FunctionTraits { + using Call = ReturnType (*)(Data&, Args&&...) noexcept; + using IsConst = std::true_type; + using ConstSignature = ReturnType(Args...) const noexcept; + using NonConstSignature = ReturnType(Args...) noexcept; + using OtherSignature = NonConstSignature; + template + using ResultOf = SafeResultOf< + CallableResult>&, Args...>, + ReturnType>; + + template + static ReturnType callSmall(Data& p, Args&&... args) noexcept { + return static_cast((*static_cast( + static_cast(&p.tiny)))(static_cast(args)...)); + } + + template + static ReturnType callBig(Data& p, Args&&... args) noexcept { + return static_cast( + (*static_cast(p.big))(static_cast(args)...)); + } + + static ReturnType uninitCall(Data&, Args&&...) noexcept { + throw std::bad_function_call(); + } + + ReturnType operator()(Args... args) const noexcept { + auto& fn = *static_cast*>(this); + return fn.call_(fn.data_, static_cast(args)...); + } + + class SharedProxy { + std::shared_ptr> sp_; + + public: + explicit SharedProxy(Function&& func) + : sp_(std::make_shared>(std::move(func))) {} + ReturnType operator()(Args&&... args) const { + return (*sp_)(static_cast(args)...); + } + }; +}; +#endif + +template +bool execSmall(Op o, Data* src, Data* dst) { + switch (o) { + case Op::MOVE: + ::new (static_cast(&dst->tiny)) + Fun(std::move(*static_cast(static_cast(&src->tiny)))); + FOLLY_FALLTHROUGH; + case Op::NUKE: + static_cast(static_cast(&src->tiny))->~Fun(); + break; + case Op::FULL: + return true; + case Op::HEAP: + break; + } + return false; +} + +template +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(src->big); + break; + case Op::FULL: + case Op::HEAP: + break; + } + return true; +} + +} // namespace function +} // namespace detail + +template +class Function final : private detail::function::FunctionTraits { + // 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; + using Call = typename Traits::Call; + using Exec = bool (*)(Op, Data*, Data*); + + template + using IsSmall = detail::function::IsSmall; + + // 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 folly::constCastFunction<>( + Function&&) noexcept; + friend class Function; + + template + Function(Fun&& fun, SmallTag) noexcept { + using FunT = typename std::decay::type; + if (!detail::function::isNullPtrFn(fun)) { + ::new (static_cast(&data_.tiny)) FunT(static_cast(fun)); + call_ = &Traits::template callSmall; + exec_ = &detail::function::execSmall; + } + } + + template + Function(Fun&& fun, HeapTag) { + using FunT = typename std::decay::type; + data_.big = new FunT(static_cast(fun)); + call_ = &Traits::template callBig; + exec_ = &detail::function::execBig; + } + + template + Function(Function&& that, CoerceTag) + : Function(static_cast&&>(that), HeapTag{}) {} + + Function( + Function&& 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 + /*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 Traits::template ResultOf` 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&& other) - noexcept( - OtherNTM == FunctionMoveCtor::NO_THROW && - EmbedFunctorSize >= OtherEmbedFunctorSize); + typename Fun, + typename = detail::function::EnableIfNotFunction, + typename = typename Traits::template ResultOf> + /* implicit */ Function(Fun fun) noexcept( + IsSmall::value && noexcept(Fun(std::declval()))) + : Function(std::move(fun), IsSmall{}) {} /** - * 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`. + * 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&& - rhs) noexcept(RhsNTM == FunctionMoveCtor::NO_THROW); + typename Signature, + typename = typename Traits::template ResultOf>> + Function(Function&& 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 + /* 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 - /* implicit */ Function( - F&& f, - typename std::enable_if< - detail::function::IsCallable::value>::type* = - 0) noexcept(noexcept(typename std::decay:: - type(std::forward(f)))) { - createExecutor(std::forward(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 = decltype(Function(std::declval()))` prevents this + * overload from being selected by overload resolution when `fun` is not a + * compatible function. */ - template - typename std::enable_if< - detail::function::IsCallable::value, - Function&>::type - operator=(F&& f) noexcept( - noexcept(typename std::decay::type(std::forward(f)))) { - destroyExecutor(); - SCOPE_FAIL { - initializeEmptyExecutor(); - }; - createExecutor(std::forward(f)); + template ()))> + Function& operator=(Fun fun) noexcept( + noexcept(/* implicit */ Function(std::declval()))) { + // Doing this in place is more efficient when we can do so safely. + if (noexcept(/* implicit */ Function(std::declval()))) { + // 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(std::move(fun)); + } else { + // Construct a temporary and (nothrow) swap. + Function(std::move(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`. */ - template - void - swap(Function& o) noexcept( - hasNoExceptMoveCtor() && OtherNTM == FunctionMoveCtor::NO_THROW); + template < + typename Signature, + typename = typename Traits::template ResultOf>> + Function& operator=(Function&& 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 + 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 - 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 - 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 - castToConstFunction() && noexcept(hasNoExceptMoveCtor()); - - using SignatureType = FunctionType; - using ResultType = typename Traits::ResultType; - using ArgsTuple = typename Traits::ArgsTuple; - - private: - template - friend class Function; - - friend struct detail::function::FunctionTypeTraits; + bool hasAllocatedMemory() const noexcept { + return exec_(Op::HEAP, nullptr, nullptr); + } - using ExecutorIf = - typename detail::function::Executors::ExecutorIf; - using EmptyExecutor = - typename detail::function::Executors::EmptyExecutor; - template - using FunctorPtrExecutor = typename detail::function::Executors< - FunctionType>::template FunctorPtrExecutor; - template - using FunctorExecutor = typename detail::function::Executors< - FunctionType>::template FunctorExecutor; + using typename Traits::SharedProxy; - template - T const* access() const; + /** + * Move this `Function` into a copyable callable object, of which all copies + * share the state. + */ + SharedProxy asSharedProxy() && { + return SharedProxy{std::move(*this)}; + } - template - T* access(); + /** + * 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 asStdFunction() && { + return std::move(*this).asSharedProxy(); + } +}; - void initializeEmptyExecutor() noexcept; +template +void swap(Function& lhs, Function& rhs) noexcept { + lhs.swap(rhs); +} - template - void createExecutor(F&& f) noexcept( - noexcept(typename std::decay::type(std::forward(f)))); +template +bool operator==(const Function& fn, std::nullptr_t) { + return !fn; +} - void destroyExecutor() noexcept; +template +bool operator==(std::nullptr_t, const Function& fn) { + return !fn; +} - struct MinStorageSize; +template +bool operator!=(const Function& fn, std::nullptr_t) { + return !(fn == nullptr); +} - typename std::aligned_storage::type data_; - static constexpr size_t kStorageSize = sizeof(data_); -}; +template +bool operator!=(std::nullptr_t, const Function& fn) { + return !(nullptr == fn); +} -// operator== -template -inline bool operator==( - Function 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 +Function constCastFunction( + Function&& that) noexcept { + return Function{std::move(that), + detail::function::CoerceTag{}}; } -template -inline bool operator==( - std::nullptr_t, - Function const& f) noexcept { - return !f; +template +Function constCastFunction( + Function&& that) noexcept { + return std::move(that); } -template -inline bool operator!=( - Function const& f, - std::nullptr_t) noexcept { - return !!f; +#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE +template +Function constCastFunction( + Function&& that) noexcept { + return Function{ + std::move(that), detail::function::CoerceTag{}}; } -template -inline bool operator!=( - std::nullptr_t, - Function const& f) noexcept { - return !!f; +template +Function constCastFunction( + Function&& that) noexcept { + return std::move(that); } +#endif + +namespace detail { +namespace function { +template +struct IsCallableAsImpl : std::false_type {}; + +template +struct IsCallableAsImpl< + Fun, + ReturnType(Args...), + void_t::type>> + : std::is_convertible< + typename std::result_of::type, + ReturnType> {}; + +template +struct IsCallableAs : IsCallableAsImpl {}; +} // 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 -Function< - typename detail::function::FunctionTypeTraits< - FunctionType>::ConstFunctionType, - NTM, - EmbedFunctorSize> -constCastFunction(Function&& - from) noexcept(NTM == FunctionMoveCtor::NO_THROW) { - return std::move(from).castToConstFunction(); -} -} // folly +template +class FunctionRef; -namespace std { -template -void swap( - ::folly::Function& lhs, - ::folly::Function& rhs) { - lhs.swap(rhs); -} -} // std +template +class FunctionRef final { + using Call = ReturnType (*)(void*, Args&&...); + + static ReturnType uninitCall(void*, Args&&...) { + throw std::bad_function_call(); + } + + template + static ReturnType call(void* object, Args&&... args) { + using Pointer = _t>; + return static_cast(invoke( + static_cast(*static_cast(object)), + static_cast(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>>>, + detail::function::IsCallableAs>::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(static_cast(std::addressof(fun)))), + call_(&FunctionRef::call) {} + + ReturnType operator()(Args... args) const { + return call_(object_, static_cast(args)...); + } + + constexpr explicit operator bool() const { + return object_; + } +}; -#include "Function-inl.h" +} // namespace folly