/*
* Copyright 2016 Facebook, Inc.
*
+ * @author Eric Niebler (eniebler@fb.com), Sven Over (over@fb.com)
+ *
* 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
* 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.
+ *
+ * Acknowledgements: Giuseppe Ottaviano (ott@fb.com)
*/
/**
* 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>
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 };
-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;
+union Data {
+ void* big;
+ std::aligned_storage<6 * sizeof(void*)>::type tiny;
+};
+
+template <typename Fun, typename FunT = typename std::decay<Fun>::type>
+using IsSmall = std::integral_constant<
+ bool,
+ (sizeof(FunT) <= sizeof(Data::tiny) &&
+ // Same as is_nothrow_move_constructible, but w/ no template instantiation.
+ noexcept(FunT(std::declval<FunT&&>()))
+ )>;
+using SmallTag = std::true_type;
+using HeapTag = std::false_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<ReturnType(Args...)>*>(this);
+ return fn.call_(fn.data_, static_cast<Args&&>(args)...);
+ }
+
+ struct SharedFunctionImpl {
+ std::shared_ptr<Function<ReturnType(Args...)>> sp_;
+ 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)...));
+ }
- /**
- * Default constructor. Constructs an empty Function.
- */
- Function() noexcept {
- initializeEmptyExecutor();
+ template <typename Fun>
+ static ReturnType callBig(Data& p, Args&&... args) {
+ return static_cast<ReturnType>(
+ (*static_cast<const Fun*>(p.big))(static_cast<Args&&>(args)...));
}
- ~Function() {
- destroyExecutor();
+ static ReturnType uninitCall(Data&, Args&&...) {
+ throw std::bad_function_call();
+ }
- static_assert(
- kStorageSize == sizeof(*this),
- "There is something wrong with the size of Function");
+ ReturnType operator()(Args... args) const {
+ auto& fn = *static_cast<const Function<ReturnType(Args...) const>*>(this);
+ return fn.call_(fn.data_, static_cast<Args&&>(args)...);
}
- // construct/assign from Function
- /**
- * Move constructor
- */
- Function(Function&& other) noexcept(hasNoExceptMoveCtor());
- /**
- * Move assignment operator
- */
- Function& operator=(Function&& rhs) noexcept(hasNoExceptMoveCtor());
+ struct SharedFunctionImpl {
+ std::shared_ptr<Function<ReturnType(Args...) const>> sp_;
+ ReturnType operator()(Args&&... args) const {
+ return (*sp_)(static_cast<Args&&>(args)...);
+ }
+ };
+};
- /**
- * Constructs a `Function` by moving from one with different template
- * parameters with regards to const-ness, no-except-movability and internal
- * storage size.
- */
- template <
- typename OtherFunctionType,
- FunctionMoveCtor OtherNTM,
- size_t OtherEmbedFunctorSize>
- Function(Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>&& other)
- noexcept(
- OtherNTM == FunctionMoveCtor::NO_THROW &&
- EmbedFunctorSize >= OtherEmbedFunctorSize);
+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
+
+FOLLY_PUSH_WARNING
+FOLLY_MSVC_DISABLE_WARNING(4521) // Multiple copy constructors
+FOLLY_MSVC_DISABLE_WARNING(4522) // Multiple assignment operators
+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>;
+
+ using OtherSignature = typename Traits::OtherSignature;
+
+ // 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<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>;
+ }
+
+ Function(Function<OtherSignature>&& that, CoerceTag) noexcept {
+ that.exec_(Op::MOVE, &that.data_, &data_);
+ std::swap(call_, that.call_);
+ std::swap(exec_, that.exec_);
+ }
+
+ public:
/**
- * Moves a `Function` with different template parameters with regards
- * to const-ness, no-except-movability and internal storage size into this
- * one.
+ * Default constructor. Constructs an empty Function.
*/
- template <
- typename RhsFunctionType,
- FunctionMoveCtor RhsNTM,
- size_t RhsEmbedFunctorSize>
- Function& operator=(Function<RhsFunctionType, RhsNTM, RhsEmbedFunctorSize>&&
- rhs) noexcept(RhsNTM == FunctionMoveCtor::NO_THROW);
+ Function() = default;
+
+ // not copyable
+ // NOTE: Deleting the non-const copy constructor is unusual but necessary to
+ // prevent copies from non-const `Function` object from selecting the
+ // perfect forwarding implicit converting constructor below
+ // (i.e., `template <typename Fun> Function(Fun&&)`).
+ Function(Function&) = delete;
+ Function(const Function&) = delete;
/**
- * Constructs an empty `Function`.
+ * Move constructor
*/
- /* implicit */ Function(std::nullptr_t) noexcept : Function() {}
+ Function(Function&& that) noexcept {
+ that.exec_(Op::MOVE, &that.data_, &data_);
+ std::swap(call_, that.call_);
+ std::swap(exec_, that.exec_);
+ }
/**
- * Clears this `Function`.
+ * Constructs an empty `Function`.
*/
- Function& operator=(std::nullptr_t) noexcept {
- destroyExecutor();
- initializeEmptyExecutor();
- return *this;
- }
+ /* implicit */ Function(std::nullptr_t) noexcept {}
/**
* Constructs a new `Function` from any callable object. This
* 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
+ * `operator() const` otherwise.
+ *
+ * \note `typename = ResultOf<Fun>` prevents this overload from being
+ * selected by overload resolution when `fun` is not a compatible function.
+ */
+ template <class Fun, typename = typename Traits::template ResultOf<Fun>>
+ /* implicit */ Function(Fun&& fun) noexcept(IsSmall<Fun>::value)
+ : Function(static_cast<Fun&&>(fun), IsSmall<Fun>{}) {}
+
+ /**
+ * For moving a `Function<X(Ys..) const>` into a `Function<X(Ys...)>`.
+ */
+ template <
+ bool Const = Traits::IsConst::value,
+ typename std::enable_if<!Const, int>::type = 0>
+ Function(Function<OtherSignature>&& that) noexcept
+ : Function(std::move(that), CoerceTag{}) {}
+
+ /**
+ * If `ptr` is null, constructs an empty `Function`. Otherwise,
+ * this constructor is equivalent to `Function(std::mem_fn(ptr))`.
*/
- 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));
+ 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);
+ }
}
+ ~Function() {
+ exec_(Op::NUKE, &data_, nullptr);
+ }
+
+ Function& operator=(Function&) = delete;
+ Function& operator=(const Function&) = delete;
+
/**
- * Assigns a callable object to this `Function`.
+ * Move assignment operator
*/
- 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));
+ Function& operator=(Function&& that) noexcept {
+ if (&that != this) {
+ // Q: Why is is 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.
+ this->~Function();
+ ::new (this) Function(std::move(that));
+ }
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.
+ * 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 <FunctionMoveCtor OtherNTM, size_t OtherEmbedFunctorSize>
- void
- swap(Function<FunctionType, OtherNTM, OtherEmbedFunctorSize>& o) noexcept(
- hasNoExceptMoveCtor() && OtherNTM == FunctionMoveCtor::NO_THROW);
+ template <class Fun, typename = typename Traits::template ResultOf<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;
+ }
/**
- * 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;
-
- template <typename T>
- T* access();
-
- void initializeEmptyExecutor() noexcept;
-
- template <typename F>
- void createExecutor(F&& f) noexcept(
- noexcept(typename std::decay<F>::type(std::forward<F>(f))));
-
- void destroyExecutor() noexcept;
-
- struct MinStorageSize;
+ bool hasAllocatedMemory() const noexcept {
+ return exec_(Op::HEAP, nullptr, nullptr);
+ }
- typename std::aligned_storage<MinStorageSize::value>::type data_;
- static constexpr size_t kStorageSize = sizeof(data_);
+ /**
+ * 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() && {
+ using Impl = typename Traits::SharedFunctionImpl;
+ return Impl{std::make_shared<Function>(std::move(*this))};
+ }
};
+FOLLY_POP_WARNING
-// 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>
+void swap(Function<FunctionType>& lhs, Function<FunctionType>& rhs) noexcept {
+ lhs.swap(rhs);
+}
+
+template <typename FunctionType>
+bool operator==(const Function<FunctionType>& fn, std::nullptr_t) {
+ return !fn;
}
-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 !fn;
}
-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);
}
/**
- * 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!
+ * 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 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();
+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{}};
}
-} // folly
-
-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);
+template <typename ReturnType, typename... Args>
+Function<ReturnType(Args...) const> constCastFunction(
+ Function<ReturnType(Args...) const>&& that) noexcept {
+ return std::move(that);
}
-} // std
-
-#include "Function-inl.h"
+} // namespace folly