--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace folly {
+namespace detail {
+namespace function {
+
+// ---------------------------------------------------------------------------
+// HELPER TYPES
+
+enum class AllocationStatus { EMPTY, EMBEDDED, ALLOCATED };
+
+// ---------------------------------------------------------------------------
+// EXECUTOR CLASSES
+
+// function::ExecutorIf
+template <typename FunctionType>
+class Executors<FunctionType>::ExecutorIf
+ : public Executors<FunctionType>::Traits::ExecutorMixin {
+ protected:
+ ExecutorIf(InvokeFunctionPtr invoke_ptr)
+ : Traits::ExecutorMixin(invoke_ptr){};
+
+ public:
+ // executors are neither copyable nor movable
+ ExecutorIf(ExecutorIf const&) = delete;
+ ExecutorIf& operator=(ExecutorIf const&) = delete;
+ ExecutorIf(ExecutorIf&&) = delete;
+ ExecutorIf& operator=(ExecutorIf&&) = delete;
+
+ virtual ~ExecutorIf() {}
+ virtual detail::function::AllocationStatus getAllocationStatus() const
+ noexcept = 0;
+ virtual std::pair<std::type_info const&, void*> target() const noexcept = 0;
+
+ // moveTo: move this executor to a different place
+ // preconditions:
+ // * *this is a valid executor object (derived from ExecutorIf)
+ // * the memory at [dest; dest+size) may be overwritten
+ // postconditions:
+ // * *this is an EmptyExecutor
+ // * *dest is a valid executor object (derived from ExecutorIf)
+ // You can move this executor into one for a non-const or const
+ // function.
+ virtual void moveTo(
+ typename NonConstFunctionExecutors::ExecutorIf* dest,
+ size_t size,
+ FunctionMoveCtor throws) = 0;
+ virtual void moveTo(
+ typename ConstFunctionExecutors::ExecutorIf* dest,
+ size_t size,
+ FunctionMoveCtor throws) = 0;
+};
+
+// function::EmptyExecutor
+template <typename FunctionType>
+class Executors<FunctionType>::EmptyExecutor final
+ : public Executors<FunctionType>::ExecutorIf {
+ public:
+ EmptyExecutor() noexcept : ExecutorIf(&EmptyExecutor::invokeEmpty) {}
+ ~EmptyExecutor() {}
+ detail::function::AllocationStatus getAllocationStatus() const noexcept {
+ return detail::function::AllocationStatus::EMPTY;
+ }
+
+ std::pair<std::type_info const&, void*> target() const noexcept {
+ return {typeid(void), nullptr};
+ }
+
+ template <typename DestinationExecutors>
+ void moveToImpl(typename DestinationExecutors::ExecutorIf* dest) noexcept {
+ new (dest) typename DestinationExecutors::EmptyExecutor();
+ }
+
+ void moveTo(
+ typename NonConstFunctionExecutors::ExecutorIf* dest,
+ size_t /*size*/,
+ FunctionMoveCtor /*throws*/) noexcept {
+ moveToImpl<Executors<typename Traits::NonConstFunctionType>>(dest);
+ }
+ void moveTo(
+ typename ConstFunctionExecutors::ExecutorIf* dest,
+ size_t /*size*/,
+ FunctionMoveCtor /*throws*/) noexcept {
+ moveToImpl<Executors<typename Traits::ConstFunctionType>>(dest);
+ }
+};
+
+// function::FunctorPtrExecutor
+template <typename FunctionType>
+template <typename F, typename SelectFunctionTag>
+class Executors<FunctionType>::FunctorPtrExecutor final
+ : public Executors<FunctionType>::ExecutorIf {
+ public:
+ FunctorPtrExecutor(F&& f)
+ : ExecutorIf(
+ &FunctorPtrExecutor::template invokeFunctor<FunctorPtrExecutor>),
+ functorPtr_(new F(std::move(f))) {}
+ FunctorPtrExecutor(F const& f)
+ : ExecutorIf(
+ &FunctorPtrExecutor::template invokeFunctor<FunctorPtrExecutor>),
+ functorPtr_(new F(f)) {}
+ FunctorPtrExecutor(std::unique_ptr<F> f)
+ : ExecutorIf(
+ &FunctorPtrExecutor::template invokeFunctor<FunctorPtrExecutor>),
+ functorPtr_(std::move(f)) {}
+ ~FunctorPtrExecutor() {}
+ detail::function::AllocationStatus getAllocationStatus() const noexcept {
+ return detail::function::AllocationStatus::ALLOCATED;
+ }
+
+ static auto getFunctor(
+ typename Traits::template QualifiedPointer<ExecutorIf> self) ->
+ typename SelectFunctionTag::template QualifiedPointer<F> {
+ return FunctorPtrExecutor::selectFunctionHelper(
+ static_cast<
+ typename Traits::template QualifiedPointer<FunctorPtrExecutor>>(
+ self)
+ ->functorPtr_.get(),
+ SelectFunctionTag());
+ }
+
+ std::pair<std::type_info const&, void*> target() const noexcept {
+ return {typeid(F), const_cast<F*>(functorPtr_.get())};
+ }
+
+ template <typename DestinationExecutors>
+ void moveToImpl(typename DestinationExecutors::ExecutorIf* dest) noexcept {
+ new (dest) typename DestinationExecutors::
+ template FunctorPtrExecutor<F, SelectFunctionTag>(
+ std::move(functorPtr_));
+ this->~FunctorPtrExecutor();
+ new (this) EmptyExecutor();
+ }
+
+ void moveTo(
+ typename NonConstFunctionExecutors::ExecutorIf* dest,
+ size_t /*size*/,
+ FunctionMoveCtor /*throws*/) noexcept {
+ moveToImpl<Executors<typename Traits::NonConstFunctionType>>(dest);
+ }
+ void moveTo(
+ typename ConstFunctionExecutors::ExecutorIf* dest,
+ size_t /*size*/,
+ FunctionMoveCtor /*throws*/) noexcept {
+ moveToImpl<Executors<typename Traits::ConstFunctionType>>(dest);
+ }
+
+ private:
+ std::unique_ptr<F> functorPtr_;
+};
+
+// function::FunctorExecutor
+template <typename FunctionType>
+template <typename F, typename SelectFunctionTag>
+class Executors<FunctionType>::FunctorExecutor final
+ : public Executors<FunctionType>::ExecutorIf {
+ public:
+ static constexpr bool kFunctorIsNTM =
+ std::is_nothrow_move_constructible<F>::value;
+
+ FunctorExecutor(F&& f)
+ : ExecutorIf(&FunctorExecutor::template invokeFunctor<FunctorExecutor>),
+ functor_(std::move(f)) {}
+ FunctorExecutor(F const& f)
+ : ExecutorIf(&FunctorExecutor::template invokeFunctor<FunctorExecutor>),
+ functor_(f) {}
+ ~FunctorExecutor() {}
+ detail::function::AllocationStatus getAllocationStatus() const noexcept {
+ return detail::function::AllocationStatus::EMBEDDED;
+ }
+
+ static auto getFunctor(
+ typename Traits::template QualifiedPointer<ExecutorIf> self) ->
+ typename SelectFunctionTag::template QualifiedPointer<F> {
+ return FunctorExecutor::selectFunctionHelper(
+ &static_cast<
+ typename Traits::template QualifiedPointer<FunctorExecutor>>(self)
+ ->functor_,
+ SelectFunctionTag());
+ }
+
+ std::pair<std::type_info const&, void*> target() const noexcept {
+ return {typeid(F), const_cast<F*>(&functor_)};
+ }
+
+ template <typename DestinationExecutors>
+ void moveToImpl(
+ typename DestinationExecutors::ExecutorIf* dest,
+ size_t size,
+ FunctionMoveCtor throws) noexcept(kFunctorIsNTM) {
+ if ((kFunctorIsNTM || throws == FunctionMoveCtor::MAY_THROW) &&
+ size >= sizeof(*this)) {
+ // Either functor_ is no-except-movable or no-except-movability is
+ // not requested *and* functor_ fits into destination
+ // => functor_ will be moved into a FunctorExecutor at dest
+ new (dest) typename DestinationExecutors::
+ template FunctorExecutor<F, SelectFunctionTag>(std::move(functor_));
+ } else {
+ // Either functor_ may throw when moved and no-except-movabilty is
+ // requested *or* the functor is too big to fit into destination
+ // => functor_ will be moved into a FunctorPtrExecutor. This will
+ // move functor_ onto the heap. The FunctorPtrExecutor object
+ // contains a unique_ptr.
+ new (dest) typename DestinationExecutors::
+ template FunctorPtrExecutor<F, SelectFunctionTag>(
+ std::move(functor_));
+ }
+ this->~FunctorExecutor();
+ new (this) EmptyExecutor();
+ }
+ void moveTo(
+ typename NonConstFunctionExecutors::ExecutorIf* dest,
+ size_t size,
+ FunctionMoveCtor throws) noexcept(kFunctorIsNTM) {
+ moveToImpl<Executors<typename Traits::NonConstFunctionType>>(
+ dest, size, throws);
+ }
+ void moveTo(
+ typename ConstFunctionExecutors::ExecutorIf* dest,
+ size_t size,
+ FunctionMoveCtor throws) noexcept(kFunctorIsNTM) {
+ moveToImpl<Executors<typename Traits::ConstFunctionType>>(
+ dest, size, throws);
+ }
+
+ private:
+ F functor_;
+};
+} // namespace function
+} // namespace detail
+
+// ---------------------------------------------------------------------------
+// MOVE CONSTRUCTORS & MOVE ASSIGNMENT OPERATORS
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+Function<FunctionType, NTM, EmbedFunctorSize>::Function(
+ Function&& other) noexcept(hasNoExceptMoveCtor()) {
+ other.access<ExecutorIf>()->moveTo(access<ExecutorIf>(), kStorageSize, NTM);
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+Function<FunctionType, NTM, EmbedFunctorSize>&
+Function<FunctionType, NTM, EmbedFunctorSize>::operator=(
+ Function&& rhs) noexcept(hasNoExceptMoveCtor()) {
+ destroyExecutor();
+ SCOPE_FAIL {
+ initializeEmptyExecutor();
+ };
+ rhs.access<ExecutorIf>()->moveTo(access<ExecutorIf>(), kStorageSize, NTM);
+ return *this;
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+template <
+ typename OtherFunctionType,
+ FunctionMoveCtor OtherNTM,
+ size_t OtherEmbedFunctorSize>
+Function<FunctionType, NTM, EmbedFunctorSize>::
+ Function(
+ Function<OtherFunctionType,
+ OtherNTM,
+ OtherEmbedFunctorSize>&& other) noexcept(
+ OtherNTM == FunctionMoveCtor::NO_THROW &&
+ EmbedFunctorSize >= OtherEmbedFunctorSize) {
+ using OtherFunction =
+ Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>;
+
+ static_assert(
+ std::is_same<
+ typename Traits::NonConstFunctionType,
+ typename OtherFunction::Traits::NonConstFunctionType>::value,
+ "Function: cannot move into a Function with different "
+ "parameter signature");
+ static_assert(
+ !Traits::IsConst::value || OtherFunction::Traits::IsConst::value,
+ "Function: cannot move Function<R(Args...)> into "
+ "Function<R(Args...) const>; "
+ "use folly::constCastFunction!");
+
+ other.template access<typename OtherFunction::ExecutorIf>()->moveTo(
+ access<ExecutorIf>(), kStorageSize, NTM);
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+template <
+ typename OtherFunctionType,
+ FunctionMoveCtor OtherNTM,
+ size_t OtherEmbedFunctorSize>
+Function<FunctionType, NTM, EmbedFunctorSize>&
+Function<FunctionType, NTM, EmbedFunctorSize>::operator=(
+ Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>&&
+ rhs) noexcept(OtherNTM == FunctionMoveCtor::NO_THROW) {
+ using OtherFunction =
+ Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>;
+
+ static_assert(
+ std::is_same<
+ typename Traits::NonConstFunctionType,
+ typename OtherFunction::Traits::NonConstFunctionType>::value,
+ "Function: cannot move into a Function with different "
+ "parameter signature");
+ static_assert(
+ !Traits::IsConst::value || OtherFunction::Traits::IsConst::value,
+ "Function: cannot move Function<R(Args...)> into "
+ "Function<R(Args...) const>; "
+ "use folly::constCastFunction!");
+
+ destroyExecutor();
+ SCOPE_FAIL {
+ initializeEmptyExecutor();
+ };
+ rhs.template access<typename OtherFunction::ExecutorIf>()->moveTo(
+ access<ExecutorIf>(), kStorageSize, NTM);
+ return *this;
+}
+
+// ---------------------------------------------------------------------------
+// PUBLIC METHODS
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+template <FunctionMoveCtor OtherNTM, size_t OtherEmbedFunctorSize>
+inline void Function<FunctionType, NTM, EmbedFunctorSize>::
+ swap(Function<FunctionType, OtherNTM, OtherEmbedFunctorSize>& o) noexcept(
+ hasNoExceptMoveCtor() && OtherNTM == FunctionMoveCtor::NO_THROW) {
+ Function<FunctionType, NTM, EmbedFunctorSize> tmp(std::move(*this));
+ *this = std::move(o);
+ o = std::move(tmp);
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+Function<FunctionType, NTM, EmbedFunctorSize>::operator bool() const noexcept {
+ return access<ExecutorIf>()->getAllocationStatus() !=
+ detail::function::AllocationStatus::EMPTY;
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+inline bool Function<FunctionType, NTM, EmbedFunctorSize>::hasAllocatedMemory()
+ const noexcept {
+ return access<ExecutorIf>()->getAllocationStatus() ==
+ detail::function::AllocationStatus::ALLOCATED;
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+inline std::type_info const&
+Function<FunctionType, NTM, EmbedFunctorSize>::target_type() const noexcept {
+ return access<ExecutorIf>()->target().first;
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+template <typename T>
+T* Function<FunctionType, NTM, EmbedFunctorSize>::target() noexcept {
+ auto type_target_pair = access<ExecutorIf>()->target();
+ if (type_target_pair.first == typeid(T)) {
+ return static_cast<T*>(type_target_pair.second);
+ }
+ return nullptr;
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+template <typename T>
+T const* Function<FunctionType, NTM, EmbedFunctorSize>::target() const
+ noexcept {
+ auto type_target_pair = access<ExecutorIf>()->target();
+ if (type_target_pair.first == typeid(T)) {
+ return static_cast<T const*>(type_target_pair.second);
+ }
+ return nullptr;
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+ Function<
+ typename detail::function::FunctionTypeTraits<
+ FunctionType>::ConstFunctionType,
+ NTM,
+ EmbedFunctorSize>
+ Function<FunctionType, NTM, EmbedFunctorSize>::castToConstFunction() &&
+ noexcept(hasNoExceptMoveCtor()) {
+ using ReturnType =
+ Function<typename Traits::ConstFunctionType, NTM, EmbedFunctorSize>;
+
+ ReturnType result;
+ result.destroyExecutor();
+ SCOPE_FAIL {
+ result.initializeEmptyExecutor();
+ };
+ access<ExecutorIf>()->moveTo(
+ result.template access<typename ReturnType::ExecutorIf>(),
+ kStorageSize,
+ NTM);
+ return result;
+}
+
+// ---------------------------------------------------------------------------
+// PRIVATE METHODS
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+template <typename T>
+T* Function<FunctionType, NTM, EmbedFunctorSize>::access() {
+ static_assert(
+ std::is_base_of<ExecutorIf, T>::value,
+ "Function::access<T>: ExecutorIf must be base class of T "
+ "(this is a bug in the Function implementation)");
+ static_assert(
+ sizeof(T) <= kStorageSize,
+ "Requested access to object not fitting into ExecutorStore "
+ "(this is a bug in the Function implementation)");
+
+ return reinterpret_cast<T*>(&data_);
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+template <typename T>
+T const* Function<FunctionType, NTM, EmbedFunctorSize>::access() const {
+ static_assert(
+ std::is_base_of<ExecutorIf, T>::value,
+ "Function::access<T>: ExecutorIf must be base class of T "
+ "(this is a bug in the Function implementation)");
+ static_assert(
+ sizeof(T) <= kStorageSize,
+ "Requested access to object not fitting into ExecutorStore "
+ "(this is a bug in the Function implementation)");
+
+ return reinterpret_cast<T const*>(&data_);
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+void Function<FunctionType, NTM, EmbedFunctorSize>::
+ initializeEmptyExecutor() noexcept {
+ new (access<EmptyExecutor>()) EmptyExecutor;
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+template <typename F>
+void Function<FunctionType, NTM, EmbedFunctorSize>::
+ createExecutor(F&& f) noexcept(
+ noexcept(typename std::decay<F>::type(std::forward<F>(f)))) {
+ using ValueType = typename std::decay<F>::type;
+ static constexpr bool kFunctorIsNTM =
+ std::is_nothrow_move_constructible<ValueType>::value;
+ using ExecutorType = typename std::conditional<
+ (sizeof(FunctorExecutor<
+ ValueType,
+ typename Traits::DefaultSelectFunctionTag>) > kStorageSize ||
+ (hasNoExceptMoveCtor() && !kFunctorIsNTM)),
+ FunctorPtrExecutor<ValueType, typename Traits::DefaultSelectFunctionTag>,
+ FunctorExecutor<ValueType, typename Traits::DefaultSelectFunctionTag>>::
+ type;
+ new (access<ExecutorType>()) ExecutorType(std::forward<F>(f));
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+void Function<FunctionType, NTM, EmbedFunctorSize>::destroyExecutor() noexcept {
+ access<ExecutorIf>()->~ExecutorIf();
+}
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+struct Function<FunctionType, NTM, EmbedFunctorSize>::MinStorageSize {
+ using NotEmbeddedFunctor =
+ FunctorPtrExecutor<void(void), detail::function::SelectConstFunctionTag>;
+
+ using EmbeddedFunctor = FunctorExecutor<
+ typename std::aligned_storage<
+ constexpr_max(EmbedFunctorSize, sizeof(void (*)(void)))>::type,
+ detail::function::SelectConstFunctionTag>;
+
+ static constexpr size_t value =
+ constexpr_max(sizeof(NotEmbeddedFunctor), sizeof(EmbeddedFunctor));
+
+ static_assert(
+ sizeof(EmptyExecutor) <= value,
+ "Internal error in Function: EmptyExecutor does not fit "
+ "in storage");
+};
+
+} // namespace folly
--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// included by Function.h, do not include directly.
+
+#include <memory>
+
+namespace folly {
+
+namespace detail {
+namespace function {
+
+struct SelectConstFunctionTag {
+ template <typename T>
+ using QualifiedPointer = T const*;
+};
+struct SelectNonConstFunctionTag {
+ template <typename T>
+ using QualifiedPointer = T*;
+};
+
+// Helper class to extract properties from a function type
+template <typename T>
+struct FunctionTypeTraits;
+
+// FunctionTypeTraits default implementation - this only exists to suppress
+// very long compiler errors when Function is tried to be instantiated
+// with an unsuitable type
+template <typename T>
+struct FunctionTypeTraits {
+ using SuitableForFunction = std::false_type;
+
+ // The following definitions are here only to suppress long and misleading
+ // compiler errors.
+ using ResultType = void;
+ using ArgsTuple = int;
+ using ArgsRefTuple = int;
+ using NonConstFunctionType = void;
+ using ConstFunctionType = int;
+
+ template <typename X>
+ class InvokeOperator {};
+ class ExecutorMixin {};
+};
+
+// FunctionTypeTraits for non-const function types
+template <typename R, typename... Args>
+struct FunctionTypeTraits<R(Args...)> {
+ using SuitableForFunction = std::true_type;
+ using ResultType = R;
+ using ArgsTuple = std::tuple<Args...>;
+ using ArgsRefTuple = std::tuple<Args&&...>;
+ using NonConstFunctionType = R(Args...);
+ using ConstFunctionType = R(Args...) const;
+ using IsConst = std::false_type;
+ using DefaultSelectFunctionTag = SelectNonConstFunctionTag;
+ template <typename F>
+ using IsCallable =
+ std::is_convertible<typename std::result_of<F&(Args...)>::type, R>;
+ template <typename T>
+ using QualifiedPointer = T*;
+ template <typename Obj>
+ using InvokeFunctionPtr = R (*)(Obj*, Args&&...);
+
+ // Function inherits from InvokeOperator<Function>. This is
+ // where Function's operator() is defined.
+ template <typename FunctionType>
+ class InvokeOperator {
+ public:
+ /**
+ * Invokes the stored callable via the invokePtr stored in the Executor.
+ *
+ * Throws std::bad_function_call if @c *this is empty.
+ */
+ ResultType operator()(Args... args) {
+ auto executor =
+ static_cast<FunctionType*>(this)
+ ->template access<typename FunctionType::ExecutorIf>();
+ return executor->invokePtr(executor, std::forward<Args>(args)...);
+ }
+ };
+
+ class ExecutorMixin;
+};
+
+// FunctionTypeTraits for const function types
+template <typename R, typename... Args>
+struct FunctionTypeTraits<R(Args...) const> {
+ using SuitableForFunction = std::true_type;
+ using ResultType = R;
+ using ArgsTuple = std::tuple<Args...>;
+ using ArgsRefTuple = std::tuple<Args&&...>;
+ using NonConstFunctionType = R(Args...);
+ using ConstFunctionType = R(Args...) const;
+ using IsConst = std::true_type;
+ using DefaultSelectFunctionTag = SelectConstFunctionTag;
+ template <typename F>
+ using IsCallable =
+ std::is_convertible<typename std::result_of<F const&(Args...)>::type, R>;
+ template <typename T>
+ using QualifiedPointer = T const*;
+ template <typename Obj>
+ using InvokeFunctionPtr = R (*)(Obj const*, Args&&...);
+
+ // Function inherits from InvokeOperator<Function>. This is
+ // where Function's operator() is defined.
+ template <typename FunctionType>
+ class InvokeOperator {
+ public:
+ /**
+ * Invokes the stored callable via the invokePtr stored in the Executor.
+ *
+ * Throws std::bad_function_call if @c *this is empty.
+ */
+ ResultType operator()(Args... args) const {
+ auto executor =
+ static_cast<FunctionType const*>(this)
+ ->template access<typename FunctionType::ExecutorIf>();
+ return executor->invokePtr(executor, std::forward<Args>(args)...);
+ }
+ };
+
+ class ExecutorMixin;
+};
+
+// Helper template for checking if a type is a Function
+template <typename T>
+struct IsFunction : public std::false_type {};
+
+template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+struct IsFunction<::folly::Function<FunctionType, NTM, EmbedFunctorSize>>
+ : public std::true_type {};
+
+// Helper template to check if a functor can be called with arguments of type
+// Args..., if it returns a type convertible to R, and also is not a
+// Function.
+// Function objects can constructed or assigned from types for which
+// IsCallableHelper is true_type.
+template <typename FunctionType>
+struct IsCallableHelper {
+ using Traits = FunctionTypeTraits<FunctionType>;
+
+ template <typename F>
+ static std::integral_constant<bool, Traits::template IsCallable<F>::value>
+ test(int);
+ template <typename F>
+ static std::false_type test(...);
+};
+
+template <typename F, typename FunctionType>
+struct IsCallable : public std::integral_constant<
+ bool,
+ (!IsFunction<typename std::decay<F>::type>::value &&
+ decltype(IsCallableHelper<FunctionType>::template test<
+ typename std::decay<F>::type>(0))::value)> {};
+
+// MaybeUnaryOrBinaryFunction: helper template class for deriving
+// Function from std::unary_function or std::binary_function
+template <typename R, typename ArgsTuple>
+struct MaybeUnaryOrBinaryFunctionImpl {
+ using result_type = R;
+};
+
+template <typename R, typename Arg>
+struct MaybeUnaryOrBinaryFunctionImpl<R, std::tuple<Arg>>
+ : public std::unary_function<Arg, R> {};
+
+template <typename R, typename Arg1, typename Arg2>
+struct MaybeUnaryOrBinaryFunctionImpl<R, std::tuple<Arg1, Arg2>>
+ : public std::binary_function<Arg1, Arg2, R> {};
+
+template <typename FunctionType>
+using MaybeUnaryOrBinaryFunction = MaybeUnaryOrBinaryFunctionImpl<
+ typename FunctionTypeTraits<FunctionType>::ResultType,
+ typename FunctionTypeTraits<FunctionType>::ArgsTuple>;
+
+// Invoke helper
+template <typename F, typename... Args>
+inline auto invoke(F&& f, Args&&... args)
+ -> decltype(std::forward<F>(f)(std::forward<Args>(args)...)) {
+ return std::forward<F>(f)(std::forward<Args>(args)...);
+}
+
+template <typename M, typename C, typename... Args>
+inline auto invoke(M(C::*d), Args&&... args)
+ -> decltype(std::mem_fn(d)(std::forward<Args>(args)...)) {
+ return std::mem_fn(d)(std::forward<Args>(args)...);
+}
+
+// Executors helper class
+template <typename FunctionType>
+struct Executors {
+ class ExecutorIf;
+ class EmptyExecutor;
+ template <class F, class SelectFunctionTag>
+ class FunctorPtrExecutor;
+ template <class F, class SelectFunctionTag>
+ class FunctorExecutor;
+
+ using Traits = FunctionTypeTraits<FunctionType>;
+ using NonConstFunctionExecutors =
+ Executors<typename Traits::NonConstFunctionType>;
+ using ConstFunctionExecutors = Executors<typename Traits::ConstFunctionType>;
+ using InvokeFunctionPtr = typename Traits::template InvokeFunctionPtr<
+ Executors<FunctionType>::ExecutorIf>;
+};
+
+template <typename R, typename... Args>
+class FunctionTypeTraits<R(Args...)>::ExecutorMixin {
+ public:
+ using ExecutorIf = typename Executors<R(Args...)>::ExecutorIf;
+ using InvokeFunctionPtr = typename Executors<R(Args...)>::InvokeFunctionPtr;
+
+ ExecutorMixin(InvokeFunctionPtr invoke_ptr) : invokePtr(invoke_ptr) {}
+ virtual ~ExecutorMixin() {}
+
+ template <typename F>
+ static F* selectFunctionHelper(F* f, SelectNonConstFunctionTag) {
+ return f;
+ }
+
+ template <typename F>
+ static F const* selectFunctionHelper(F* f, SelectConstFunctionTag) {
+ return f;
+ }
+
+ static R invokeEmpty(ExecutorIf*, Args&&...) {
+ throw std::bad_function_call();
+ }
+
+ template <typename Ex>
+ static R invokeFunctor(ExecutorIf* executor, Args&&... args) {
+ return folly::detail::function::invoke(
+ *Ex::getFunctor(executor), std::forward<Args>(args)...);
+ }
+
+ // invokePtr is of type
+ // ReturnType (*)(ExecutorIf*, Args&&...)
+ // and it will be set to the address of one of the static functions above
+ // (invokeEmpty or invokeFunctor), which will invoke the stored callable
+ InvokeFunctionPtr const invokePtr;
+};
+
+template <typename R, typename... Args>
+class FunctionTypeTraits<R(Args...) const>::ExecutorMixin {
+ public:
+ using ExecutorIf = typename Executors<R(Args...) const>::ExecutorIf;
+ using InvokeFunctionPtr =
+ typename Executors<R(Args...) const>::InvokeFunctionPtr;
+
+ ExecutorMixin(InvokeFunctionPtr invoke_ptr) : invokePtr(invoke_ptr) {}
+ virtual ~ExecutorMixin() {}
+
+ template <typename F>
+ static F* selectFunctionHelper(F const* f, SelectNonConstFunctionTag) {
+ return const_cast<F*>(f);
+ }
+
+ template <typename F>
+ static F const* selectFunctionHelper(F const* f, SelectConstFunctionTag) {
+ return f;
+ }
+
+ static R invokeEmpty(ExecutorIf const*, Args&&...) {
+ throw std::bad_function_call();
+ }
+
+ template <typename Ex>
+ static R invokeFunctor(ExecutorIf const* executor, Args&&... args) {
+ return folly::detail::function::invoke(
+ *Ex::getFunctor(executor), std::forward<Args>(args)...);
+ }
+
+ // invokePtr is of type
+ // ReturnType (*)(ExecutorIf*, Args&&...)
+ // and it will be set to the address of one of the static functions above
+ // (invokeEmpty or invokeFunctor), which will invoke the stored callable
+ InvokeFunctionPtr const invokePtr;
+};
+
+} // namespace function
+} // namespace detail
+} // namespace folly
--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @class Function
+ *
+ * @brief A polymorphic function wrapper that is not copyable and does not
+ * require the wrapped function to be copy constructible.
+ *
+ * `folly::Function` is a polymorphic function wrapper, similar to
+ * `std::function`. The template parameters of the `folly::Function` define
+ * the parameter signature of the wrapped callable, but not the specific
+ * 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
+ * copy constructible. As a consequence of this, `folly::Function` itself
+ * is not copyable, either.
+ *
+ * Another difference is that, unlike `std::function`, `folly::Function` treats
+ * const-ness of methods correctly. While a `std::function` allows to wrap
+ * an object that only implements a non-const `operator()` and invoke
+ * a const-reference of the `std::function`, `folly::Function` requires you to
+ * declare a function type as const in order to be able to execute it on a
+ * const-reference.
+ *
+ * For example:
+ * class Foo {
+ * public:
+ * void operator()() {
+ * // mutates the Foo object
+ * }
+ * };
+ *
+ * class Bar {
+ * std::function<void(void)> foo_; // wraps a Foo object
+ * public:
+ * void mutateFoo() const
+ * {
+ * 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.
+ *
+ * `folly::Function` will not allow you to do that. You will have to decide
+ * whether you need to invoke your wrapped callable from a const reference
+ * (like in the example above), in which case it will only wrap a
+ * `operator() const`. If your functor does not implement that,
+ * compilation will fail. If you do not require to be able to invoke the
+ * 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.
+ *
+ * using FunctionType = R(Args...);
+ * using ConstFunctionType = R(Args...) const;
+ *
+ * In this example, `FunctionType` and `ConstFunctionType` are different
+ * types. `ConstFunctionType` is not the same as `const FunctionType`.
+ * As a matter of fact, trying to use the latter should emit a compiler
+ * warning or error, because it has no defined meaning.
+ *
+ * // This will not compile:
+ * folly::Function<void(void) const> func = Foo();
+ * // because Foo does not have a member function of the form:
+ * // void operator()() const;
+ *
+ * // This will compile just fine:
+ * folly::Function<void(void)> func = Foo();
+ * // and it will wrap the existing member function:
+ * // void operator()();
+ *
+ * When should a const function type be used? As a matter of fact, you will
+ * probably not need to use const function types very often. See the following
+ * example:
+ *
+ * class Bar {
+ * folly::Function<void()> func_;
+ * folly::Function<void() const> constFunc_;
+ *
+ * void someMethod() {
+ * // Can call func_.
+ * func_();
+ * // Can call constFunc_.
+ * constFunc_();
+ * }
+ *
+ * void someConstMethod() const {
+ * // Can call constFunc_.
+ * constFunc_();
+ * // However, cannot call func_ because a non-const method cannot
+ * // be called from a const one.
+ * }
+ * };
+ *
+ * As you can see, whether the `folly::Function`'s function type should
+ * be declared const or not is identical to whether a corresponding method
+ * would be declared const or not.
+ *
+ * You only require a `folly::Function` to hold a const function type, if you
+ * intend to invoke it from within a const context. This is to ensure that
+ * you cannot mutate its inner state when calling in a const context.
+ *
+ * This is how the const/non-const choice relates to lambda functions:
+ *
+ * // Non-mutable lambdas: can be stored in a non-const...
+ * folly::Function<void(int)> print_number =
+ * [] (int number) { std::cout << number << std::endl; };
+ *
+ * // ...as well as in a const folly::Function
+ * folly::Function<void(int) const> print_number_const =
+ * [] (int number) { std::cout << number << std::endl; };
+ *
+ * // Mutable lambda: can only be stored in a non-const folly::Function:
+ * int number = 0;
+ * folly::Function<void()> print_number =
+ * [number] () mutable { std::cout << ++number << std::endl; };
+ * // Trying to store the above mutable lambda in a
+ * // `folly::Function<void() const>` would lead to a compiler error:
+ * // error: no viable conversion from '(lambda at ...)' to
+ * // 'folly::Function<void () const>'
+ *
+ * Casting between const and non-const `folly::Function`s:
+ * conversion from const to non-const signatures happens implicitly. Any
+ * function that takes a `folly::Function<R(Args...)>` can be passed
+ * a `folly::Function<R(Args...) const>` without explicit conversion.
+ * This is safe, because casting from const to non-const only entails giving
+ * up the ability to invoke the function from a const context.
+ * 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
+ * be used to perform the cast.
+ *
+ * // Mutable lambda: can only be stored in a non-const folly::Function:
+ * int number = 0;
+ * folly::Function<void()> print_number =
+ * [number] () mutable { std::cout << ++number << std::endl; };
+ *
+ * // const-cast to a const folly::Function:
+ * folly::Function<void() const> print_number_const =
+ * constCastfolly::Function(std::move(print_number));
+ *
+ * When to use const function types?
+ * Generally, only when you need them. When you use a `folly::Function` as a
+ * member of a struct or class, only use a const function signature when you
+ * need to invoke the function from const context.
+ * When passing a `folly::Function` to a function, the function should accept
+ * a non-const `folly::Function` whenever possible, i.e. when it does not
+ * need to pass on or store a const `folly::Function`. This is the least
+ * possible constraint: you can always pass a const `folly::Function` when
+ * the function accepts a non-const one.
+ *
+ * How does the const behaviour compare to `std::function`?
+ * `std::function` can wrap object with non-const invokation behaviour but
+ * exposes them as const. The equivalent behaviour can be achieved with
+ * `folly::Function` like so:
+ *
+ * std::function<void(void)> stdfunc = someCallable;
+ *
+ * folly::Function<void(void) const> uniqfunc = constCastfolly::Function(
+ * 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
+ * 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
+ * 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`.
+ * 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.
+ */
+
+#pragma once
+
+#include <functional>
+#include <type_traits>
+#include <typeinfo>
+#include <utility>
+
+#include <folly/ScopeGuard.h>
+#include <folly/portability/Constexpr.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)>)>
+class Function;
+
+} // folly
+
+// boring predeclarations and details
+#include "Function-pre.h"
+
+namespace folly {
+
+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;
+ };
+
+ public:
+ // not copyable
+ Function(Function const&) = delete;
+ Function& operator=(Function const&) = delete;
+
+ /**
+ * Default constructor. Constructs an empty Function.
+ */
+ Function() noexcept {
+ initializeEmptyExecutor();
+ }
+
+ ~Function() {
+ destroyExecutor();
+
+ static_assert(
+ kStorageSize == sizeof(*this),
+ "There is something wrong with the size of Function");
+ }
+
+ // construct/assign from Function
+ /**
+ * Move constructor
+ */
+ Function(Function&& other) noexcept(hasNoExceptMoveCtor());
+ /**
+ * Move assignment operator
+ */
+ Function& operator=(Function&& rhs) noexcept(hasNoExceptMoveCtor());
+
+ /**
+ * 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);
+
+ /**
+ * Moves a `Function` with different template parameters with regards
+ * to const-ness, no-except-movability and internal storage size into this
+ * one.
+ */
+ template <
+ typename RhsFunctionType,
+ FunctionMoveCtor RhsNTM,
+ size_t RhsEmbedFunctorSize>
+ Function& operator=(Function<RhsFunctionType, RhsNTM, RhsEmbedFunctorSize>&&
+ rhs) noexcept(RhsNTM == FunctionMoveCtor::NO_THROW);
+
+ /**
+ * Constructs an empty `Function`.
+ */
+ /* implicit */ Function(std::nullptr_t) noexcept : Function() {}
+
+ /**
+ * Clears this `Function`.
+ */
+ Function& operator=(std::nullptr_t) noexcept {
+ destroyExecutor();
+ initializeEmptyExecutor();
+ return *this;
+ }
+
+ /**
+ * 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
+ */
+ 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));
+ }
+
+ /**
+ * Assigns a callable object to this `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));
+ 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.
+ */
+ template <FunctionMoveCtor OtherNTM, size_t OtherEmbedFunctorSize>
+ void
+ swap(Function<FunctionType, OtherNTM, OtherEmbedFunctorSize>& o) noexcept(
+ hasNoExceptMoveCtor() && OtherNTM == FunctionMoveCtor::NO_THROW);
+
+ /**
+ * Returns `true` if this `Function` contains a callable, i.e. is
+ * non-empty.
+ */
+ explicit operator bool() const noexcept;
+
+ /**
+ * 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.
+ */
+ bool hasAllocatedMemory() const noexcept;
+
+ /**
+ * Returns the `type_info` (as returned by `typeid`) of the callable stored
+ * in this `Function`. Returns `typeid(void)` if empty.
+ */
+ std::type_info const& target_type() const noexcept;
+
+ /**
+ * Returns a pointer to the stored callable if its type matches `T`, and
+ * `nullptr` otherwise.
+ */
+ template <typename T>
+ T* target() noexcept;
+
+ /**
+ * Returns a const-pointer to the stored callable if its type matches `T`,
+ * and `nullptr` otherwise.
+ */
+ template <typename T>
+ const T* target() const noexcept;
+
+ /**
+ * 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!
+ */
+ 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;
+
+ typename std::aligned_storage<MinStorageSize::value>::type data_;
+ static constexpr size_t kStorageSize = sizeof(data_);
+};
+
+// 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, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+inline bool operator==(
+ std::nullptr_t,
+ Function<FunctionType, NTM, EmbedFunctorSize> const& f) noexcept {
+ return !f;
+}
+
+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, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
+inline bool operator!=(
+ std::nullptr_t,
+ Function<FunctionType, NTM, EmbedFunctorSize> const& f) noexcept {
+ return !!f;
+}
+
+/**
+ * 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!
+ */
+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
+
+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
+
+#include "Function-inl.h"
TimeoutQueue.h \
Traits.h \
Unicode.h \
+ Function.h \
+ Function-inl.h \
+ Function-pre.h \
Uri.h \
Uri-inl.h \
Varint.h \
--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdarg>
+
+#include <folly/Function.h>
+
+#include <folly/Memory.h>
+#include <gtest/gtest.h>
+
+using folly::FunctionMoveCtor;
+using folly::Function;
+
+namespace {
+int func_int_int_add_25(int x) {
+ return x + 25;
+}
+int func_int_int_add_111(int x) {
+ return x + 111;
+}
+int func_int_return_987() {
+ return 987;
+}
+float floatMult(float a, float b) {
+ return a * b;
+}
+
+template <class T, size_t S>
+struct Functor {
+ std::array<T, S> data = {{0}};
+
+ // Two operator() with different argument types.
+ // The InvokeReference tests use both
+ T const& operator()(size_t index) const {
+ return data[index];
+ }
+ T operator()(size_t index, T const& value) {
+ T oldvalue = data[index];
+ data[index] = value;
+ return oldvalue;
+ }
+};
+
+// TEST =====================================================================
+// NoExceptMovable
+
+struct MoveMayThrow {
+ bool doThrow{false};
+
+ MoveMayThrow() = default;
+ MoveMayThrow(MoveMayThrow const&) = default;
+ MoveMayThrow& operator=(MoveMayThrow const&) = default;
+ MoveMayThrow(MoveMayThrow&&) noexcept(false) {
+ if (doThrow) {
+ throw std::runtime_error("MoveMayThrow(MoveMayThrow&&)");
+ }
+ }
+ MoveMayThrow& operator=(MoveMayThrow&&) noexcept(false) {
+ if (doThrow) {
+ throw std::runtime_error("MoveMayThrow::operator=(MoveMayThrow&&)");
+ }
+ return *this;
+ }
+};
+}
+
+TEST(Function, NoExceptMovable) {
+ // callable_noexcept is noexcept-movable
+ auto callable_noexcept = [](int x) { return x + 1; };
+ EXPECT_TRUE(
+ std::is_nothrow_move_constructible<decltype(callable_noexcept)>::value);
+
+ // callable_throw may throw when moved
+ MoveMayThrow mmt;
+ auto callable_throw = [mmt](int x) { return x + 10; };
+ EXPECT_FALSE(
+ std::is_nothrow_move_constructible<decltype(callable_throw)>::value);
+
+ // callable_noexcept can be stored in the Function object
+ Function<int(int), FunctionMoveCtor::NO_THROW> func(callable_noexcept);
+ EXPECT_EQ(func(42), 43);
+ EXPECT_FALSE(func.hasAllocatedMemory());
+ EXPECT_TRUE(std::is_nothrow_move_constructible<decltype(func)>::value);
+
+ // callable_throw cannot be stored in the Function object,
+ // because Function guarantees noexcept-movability, but
+ // callable_throw may throw when moved
+ Function<int(int), FunctionMoveCtor::NO_THROW> func_safe_move(callable_throw);
+ EXPECT_EQ(func_safe_move(42), 52);
+ EXPECT_TRUE(func_safe_move.hasAllocatedMemory());
+ EXPECT_TRUE(
+ std::is_nothrow_move_constructible<decltype(func_safe_move)>::value);
+
+ // callable_throw can be stored in the Function object when
+ // the NoExceptMovable template parameter is set to NO
+ Function<int(int), FunctionMoveCtor::MAY_THROW> func_movethrows(
+ callable_throw);
+ EXPECT_EQ(func_movethrows(42), 52);
+ EXPECT_FALSE(func_movethrows.hasAllocatedMemory());
+ EXPECT_FALSE(
+ std::is_nothrow_move_constructible<decltype(func_movethrows)>::value);
+}
+
+// TEST =====================================================================
+// InvokeFunctor & InvokeReference
+
+template <FunctionMoveCtor NEM, size_t S>
+void invoke_functor_test() {
+ Functor<int, 100> func;
+ func(5, 123);
+
+ // Try Functions with differently sized storage areas
+ // S=0: request storage for functors of size 0. The storage size
+ // will be actually larger, because there is a lower limit which
+ // still allows to store at least pointers to functors on the heap.
+ // S=1: request minimum storage size of 0.5x the sizeof(func)
+ // S=2: request sizeof(func)
+ // S=3: request 1.5*sizeof(func)
+ Function<int(size_t) const, NEM, sizeof(func)* S / 2> getter =
+ std::move(func);
+
+ // Function will allocate memory on the heap to store
+ // the functor object if the internal storage area is smaller than
+ // sizeof(func).
+ EXPECT_EQ(getter.hasAllocatedMemory(), S < 2);
+
+ EXPECT_EQ(getter(5), 123);
+}
+TEST(Function, InvokeFunctor_T0) {
+ invoke_functor_test<FunctionMoveCtor::MAY_THROW, 0>();
+}
+TEST(Function, InvokeFunctor_N0) {
+ invoke_functor_test<FunctionMoveCtor::NO_THROW, 0>();
+}
+TEST(Function, InvokeFunctor_T1) {
+ invoke_functor_test<FunctionMoveCtor::MAY_THROW, 1>();
+}
+TEST(Function, InvokeFunctor_N1) {
+ invoke_functor_test<FunctionMoveCtor::NO_THROW, 1>();
+}
+TEST(Function, InvokeFunctor_T2) {
+ invoke_functor_test<FunctionMoveCtor::MAY_THROW, 2>();
+}
+TEST(Function, InvokeFunctor_N2) {
+ invoke_functor_test<FunctionMoveCtor::NO_THROW, 2>();
+}
+TEST(Function, InvokeFunctor_T3) {
+ invoke_functor_test<FunctionMoveCtor::MAY_THROW, 3>();
+}
+TEST(Function, InvokeFunctor_N3) {
+ invoke_functor_test<FunctionMoveCtor::NO_THROW, 3>();
+}
+
+template <FunctionMoveCtor NEM>
+void invoke_reference_test() {
+ Functor<int, 10> func;
+ func(5, 123);
+
+ // Have Functions for getter and setter, both referencing the
+ // same funtor
+ Function<int(size_t) const, NEM, 0> getter = std::ref(func);
+ Function<int(size_t, int), NEM, 0> setter = std::ref(func);
+
+ EXPECT_EQ(getter(5), 123);
+ EXPECT_EQ(setter(5, 456), 123);
+ EXPECT_EQ(setter(5, 567), 456);
+ EXPECT_EQ(getter(5), 567);
+}
+TEST(Function, InvokeReference_T) {
+ invoke_reference_test<FunctionMoveCtor::MAY_THROW>();
+}
+TEST(Function, InvokeReference_N) {
+ invoke_reference_test<FunctionMoveCtor::NO_THROW>();
+}
+
+// TEST =====================================================================
+// Emptiness
+
+template <FunctionMoveCtor NEM>
+void emptiness_test() {
+ Function<int(int), NEM> f;
+ EXPECT_EQ(f, nullptr);
+ EXPECT_EQ(nullptr, f);
+ EXPECT_FALSE(f);
+ EXPECT_THROW(f(98), std::bad_function_call);
+
+ Function<int(int), NEM> g([](int x) { return x + 1; });
+ EXPECT_NE(g, nullptr);
+ EXPECT_NE(nullptr, g);
+ EXPECT_TRUE(g);
+ EXPECT_EQ(g(99), 100);
+
+ Function<int(int), NEM> h(&func_int_int_add_25);
+ EXPECT_NE(h, nullptr);
+ EXPECT_NE(nullptr, h);
+ EXPECT_TRUE(h);
+ EXPECT_EQ(h(100), 125);
+
+ h = {};
+ EXPECT_EQ(h, nullptr);
+ EXPECT_EQ(nullptr, h);
+ EXPECT_FALSE(h);
+ EXPECT_THROW(h(101), std::bad_function_call);
+}
+
+TEST(Function, Emptiness_T) {
+ emptiness_test<FunctionMoveCtor::MAY_THROW>();
+}
+TEST(Function, Emptiness_N) {
+ emptiness_test<FunctionMoveCtor::NO_THROW>();
+}
+
+// TEST =====================================================================
+// Types
+
+TEST(Function, Types) {
+ EXPECT_TRUE((
+ !std::is_base_of<std::unary_function<int, int>, Function<int()>>::value));
+ EXPECT_TRUE(
+ (!std::is_base_of<std::binary_function<int, int, int>, Function<int()>>::
+ value));
+ EXPECT_TRUE((std::is_same<Function<int()>::ResultType, int>::value));
+
+ EXPECT_TRUE((
+ std::is_base_of<std::unary_function<int, double>, Function<double(int)>>::
+ value));
+ EXPECT_TRUE((!std::is_base_of<
+ std::binary_function<int, int, double>,
+ Function<double(int)>>::value));
+ EXPECT_TRUE((std::is_same<Function<double(int)>::ResultType, double>::value));
+ EXPECT_TRUE(
+ (std::is_same<Function<double(int)>::result_type, double>::value));
+ EXPECT_TRUE((std::is_same<Function<double(int)>::argument_type, int>::value));
+
+ EXPECT_TRUE((!std::is_base_of<
+ std::unary_function<int, double>,
+ Function<double(int, char)>>::value));
+ EXPECT_TRUE((std::is_base_of<
+ std::binary_function<int, char, double>,
+ Function<double(int, char)>>::value));
+ EXPECT_TRUE(
+ (std::is_same<Function<double(int, char)>::ResultType, double>::value));
+ EXPECT_TRUE(
+ (std::is_same<Function<double(int, char)>::result_type, double>::value));
+ EXPECT_TRUE(
+ (std::is_same<Function<double(int, char)>::first_argument_type, int>::
+ value));
+ EXPECT_TRUE(
+ (std::is_same<Function<double(int, char)>::second_argument_type, char>::
+ value));
+}
+
+// TEST =====================================================================
+// Swap
+
+template <FunctionMoveCtor NEM1, FunctionMoveCtor NEM2>
+void swap_test() {
+ Function<int(int), NEM1> mf1(func_int_int_add_25);
+ Function<int(int), NEM2> mf2(func_int_int_add_111);
+
+ EXPECT_EQ(mf1(100), 125);
+ EXPECT_EQ(mf2(100), 211);
+
+ mf1.swap(mf2);
+
+ EXPECT_EQ(mf2(100), 125);
+ EXPECT_EQ(mf1(100), 211);
+
+ Function<int(int)> mf3(nullptr);
+ EXPECT_EQ(mf3, nullptr);
+
+ mf1.swap(mf3);
+
+ EXPECT_EQ(mf3(100), 211);
+ EXPECT_EQ(mf1, nullptr);
+
+ Function<int(int)> mf4([](int x) { return x + 222; });
+ EXPECT_EQ(mf4(100), 322);
+
+ mf4.swap(mf3);
+ EXPECT_EQ(mf4(100), 211);
+ EXPECT_EQ(mf3(100), 322);
+
+ mf3.swap(mf1);
+ EXPECT_EQ(mf3, nullptr);
+ EXPECT_EQ(mf1(100), 322);
+}
+TEST(Function, Swap_TT) {
+ swap_test<FunctionMoveCtor::MAY_THROW, FunctionMoveCtor::MAY_THROW>();
+}
+TEST(Function, Swap_TN) {
+ swap_test<FunctionMoveCtor::MAY_THROW, FunctionMoveCtor::NO_THROW>();
+}
+TEST(Function, Swap_NT) {
+ swap_test<FunctionMoveCtor::NO_THROW, FunctionMoveCtor::MAY_THROW>();
+}
+TEST(Function, Swap_NN) {
+ swap_test<FunctionMoveCtor::NO_THROW, FunctionMoveCtor::NO_THROW>();
+}
+
+// TEST =====================================================================
+// Bind
+
+template <FunctionMoveCtor NEM>
+void bind_test() {
+ Function<float(float, float), NEM> fnc = floatMult;
+ auto task = std::bind(std::move(fnc), 2.f, 4.f);
+ EXPECT_THROW(fnc(0, 0), std::bad_function_call);
+ EXPECT_EQ(task(), 8);
+ auto task2(std::move(task));
+ EXPECT_THROW(task(), std::bad_function_call);
+ EXPECT_EQ(task2(), 8);
+}
+TEST(Function, Bind_T) {
+ bind_test<FunctionMoveCtor::MAY_THROW>();
+}
+TEST(Function, Bind_N) {
+ bind_test<FunctionMoveCtor::NO_THROW>();
+}
+
+// TEST =====================================================================
+// NonCopyableLambda
+
+template <FunctionMoveCtor NEM, size_t S>
+void non_copyable_lambda_test() {
+ auto unique_ptr_int = folly::make_unique<int>(900);
+ EXPECT_EQ(*unique_ptr_int, 900);
+
+ char fooData[64] = {0};
+ EXPECT_EQ(fooData[0], 0); // suppress gcc warning about fooData not being used
+
+ auto functor = std::bind(
+ [fooData](std::unique_ptr<int>& up) mutable { return ++*up; },
+ std::move(unique_ptr_int));
+
+ EXPECT_EQ(functor(), 901);
+
+ Function<int(void), NEM, sizeof(functor)* S / 2> func = std::move(functor);
+ EXPECT_EQ(
+ func.hasAllocatedMemory(),
+ S < 2 || (NEM == FunctionMoveCtor::NO_THROW &&
+ !std::is_nothrow_move_constructible<decltype(functor)>::value));
+
+ EXPECT_EQ(func(), 902);
+}
+TEST(Function, NonCopyableLambda_T0) {
+ non_copyable_lambda_test<FunctionMoveCtor::MAY_THROW, 0>();
+}
+TEST(Function, NonCopyableLambda_N0) {
+ non_copyable_lambda_test<FunctionMoveCtor::NO_THROW, 0>();
+}
+TEST(Function, NonCopyableLambda_T1) {
+ non_copyable_lambda_test<FunctionMoveCtor::MAY_THROW, 1>();
+}
+TEST(Function, NonCopyableLambda_N1) {
+ non_copyable_lambda_test<FunctionMoveCtor::NO_THROW, 1>();
+}
+TEST(Function, NonCopyableLambda_T2) {
+ non_copyable_lambda_test<FunctionMoveCtor::MAY_THROW, 2>();
+}
+TEST(Function, NonCopyableLambda_N2) {
+ non_copyable_lambda_test<FunctionMoveCtor::NO_THROW, 2>();
+}
+TEST(Function, NonCopyableLambda_T3) {
+ non_copyable_lambda_test<FunctionMoveCtor::MAY_THROW, 3>();
+}
+TEST(Function, NonCopyableLambda_N3) {
+ non_copyable_lambda_test<FunctionMoveCtor::NO_THROW, 3>();
+}
+
+// TEST =====================================================================
+// Downsize
+
+template <FunctionMoveCtor NEM>
+void downsize_test() {
+ Functor<int, 10> functor;
+
+ // set element 3
+ functor(3, 123);
+ EXPECT_EQ(functor(3), 123);
+
+ // Function with large callable storage area (twice the size of
+ // the functor)
+ Function<int(size_t, int), NEM, sizeof(functor)* 2> func2x =
+ std::move(functor);
+ EXPECT_FALSE(func2x.hasAllocatedMemory());
+ EXPECT_EQ(func2x(3, 200), 123);
+ EXPECT_EQ(func2x(3, 201), 200);
+
+ // Function with sufficient callable storage area (equal to
+ // size of the functor)
+ Function<int(size_t, int), NEM, sizeof(functor)> func1x = std::move(func2x);
+ EXPECT_THROW(func2x(0, 0), std::bad_function_call);
+ EXPECT_FALSE(func2x);
+ EXPECT_FALSE(func1x.hasAllocatedMemory());
+ EXPECT_EQ(func1x(3, 202), 201);
+ EXPECT_EQ(func1x(3, 203), 202);
+
+ // Function with minimal callable storage area (functor does
+ // not fit and will be moved to memory on the heap)
+ Function<int(size_t, int), NEM, 0> func0x = std::move(func1x);
+ EXPECT_THROW(func1x(0, 0), std::bad_function_call);
+ EXPECT_FALSE(func1x);
+ EXPECT_TRUE(func0x.hasAllocatedMemory());
+ EXPECT_EQ(func0x(3, 204), 203);
+ EXPECT_EQ(func0x(3, 205), 204);
+
+ // bonus test: move to Function with opposite NoExceptMovable
+ // setting
+ Function<
+ int(size_t, int),
+ NEM == FunctionMoveCtor::NO_THROW ? FunctionMoveCtor::MAY_THROW
+ : FunctionMoveCtor::NO_THROW,
+ 0>
+ funcnot = std::move(func0x);
+ EXPECT_THROW(func0x(0, 0), std::bad_function_call);
+ EXPECT_FALSE(func0x);
+ EXPECT_TRUE(funcnot.hasAllocatedMemory());
+ EXPECT_EQ(funcnot(3, 206), 205);
+ EXPECT_EQ(funcnot(3, 207), 206);
+}
+TEST(Function, Downsize_T) {
+ downsize_test<FunctionMoveCtor::MAY_THROW>();
+}
+TEST(Function, Downsize_N) {
+ downsize_test<FunctionMoveCtor::NO_THROW>();
+}
+
+// TEST =====================================================================
+// Refcount
+
+template <FunctionMoveCtor NEM>
+void refcount_test() {
+ Functor<int, 100> functor;
+ functor(3, 999);
+ auto shared_int = std::make_shared<int>(100);
+
+ EXPECT_EQ(*shared_int, 100);
+ EXPECT_EQ(shared_int.use_count(), 1);
+
+ Function<int(void), NEM> func1 = [shared_int]() { return ++*shared_int; };
+ EXPECT_EQ(shared_int.use_count(), 2);
+ EXPECT_EQ(func1(), 101);
+ EXPECT_EQ(*shared_int, 101);
+
+ // func2: made to not fit functor.
+ Function<int(void), NEM, sizeof(functor) / 2> func2 = std::move(func1);
+ EXPECT_THROW(func1(), std::bad_function_call);
+ EXPECT_EQ(shared_int.use_count(), 2);
+ EXPECT_FALSE(func1);
+ EXPECT_EQ(func2(), 102);
+ EXPECT_EQ(*shared_int, 102);
+
+ func2 = [shared_int]() { return ++*shared_int; };
+ EXPECT_EQ(shared_int.use_count(), 2);
+ EXPECT_EQ(func2(), 103);
+ EXPECT_EQ(*shared_int, 103);
+
+ // We set func2 to a lambda that captures 'functor', which forces it on
+ // the heap
+ func2 = [functor]() { return functor(3); };
+ EXPECT_TRUE(func2.hasAllocatedMemory());
+ EXPECT_EQ(func2(), 999);
+ EXPECT_EQ(shared_int.use_count(), 1);
+ EXPECT_EQ(*shared_int, 103);
+
+ func2 = [shared_int]() { return ++*shared_int; };
+ EXPECT_EQ(shared_int.use_count(), 2);
+ EXPECT_EQ(func2(), 104);
+ EXPECT_EQ(*shared_int, 104);
+
+ // We set func2 to function pointer, which always fits into the
+ // Function object and is no-except-movable
+ func2 = &func_int_return_987;
+ EXPECT_FALSE(func2.hasAllocatedMemory());
+ EXPECT_EQ(func2(), 987);
+ EXPECT_EQ(shared_int.use_count(), 1);
+ EXPECT_EQ(*shared_int, 104);
+}
+TEST(Function, Refcount_T) {
+ refcount_test<FunctionMoveCtor::MAY_THROW>();
+}
+TEST(Function, Refcount_N) {
+ refcount_test<FunctionMoveCtor::NO_THROW>();
+}
+
+// TEST =====================================================================
+// Target
+
+template <FunctionMoveCtor NEM>
+void target_test() {
+ std::function<int(int)> func = [](int x) { return x + 25; };
+ EXPECT_EQ(func(100), 125);
+
+ Function<int(int), NEM> ufunc = std::move(func);
+ EXPECT_THROW(func(0), std::bad_function_call);
+ EXPECT_EQ(ufunc(200), 225);
+
+ EXPECT_EQ(ufunc.target_type(), typeid(std::function<int(int)>));
+
+ EXPECT_FALSE(ufunc.template target<int>());
+ EXPECT_FALSE(ufunc.template target<std::function<void(void)>>());
+
+ std::function<int(int)>& ufunc_target =
+ *ufunc.template target<std::function<int(int)>>();
+
+ EXPECT_EQ(ufunc_target(300), 325);
+}
+
+TEST(Function, Target_T) {
+ target_test<FunctionMoveCtor::MAY_THROW>();
+}
+TEST(Function, Target_N) {
+ target_test<FunctionMoveCtor::NO_THROW>();
+}
+
+// TEST =====================================================================
+// OverloadedFunctor
+
+TEST(Function, OverloadedFunctor) {
+ struct OverloadedFunctor {
+ // variant 1
+ int operator()(int x) {
+ return 100 + 1 * x;
+ }
+
+ // variant 2 (const-overload of v1)
+ int operator()(int x) const {
+ return 100 + 2 * x;
+ }
+
+ // variant 3
+ int operator()(int x, int) {
+ return 100 + 3 * x;
+ }
+
+ // variant 4 (const-overload of v3)
+ int operator()(int x, int) const {
+ return 100 + 4 * x;
+ }
+
+ // variant 5 (non-const, has no const-overload)
+ int operator()(int x, char const*) {
+ return 100 + 5 * x;
+ }
+
+ // variant 6 (const only)
+ int operator()(int x, std::vector<int> const&) const {
+ return 100 + 6 * x;
+ }
+ };
+ OverloadedFunctor of;
+
+ Function<int(int)> variant1 = of;
+ EXPECT_EQ(variant1(15), 100 + 1 * 15);
+
+ Function<int(int) const> variant2 = of;
+ EXPECT_EQ(variant2(16), 100 + 2 * 16);
+
+ Function<int(int, int)> variant3 = of;
+ EXPECT_EQ(variant3(17, 0), 100 + 3 * 17);
+
+ Function<int(int, int) const> variant4 = of;
+ EXPECT_EQ(variant4(18, 0), 100 + 4 * 18);
+
+ Function<int(int, char const*)> variant5 = of;
+ EXPECT_EQ(variant5(19, "foo"), 100 + 5 * 19);
+
+ Function<int(int, std::vector<int> const&)> variant6 = of;
+ EXPECT_EQ(variant6(20, {}), 100 + 6 * 20);
+ EXPECT_EQ(variant6(20, {1, 2, 3}), 100 + 6 * 20);
+
+ Function<int(int, std::vector<int> const&) const> variant6const = of;
+ EXPECT_EQ(variant6const(21, {}), 100 + 6 * 21);
+
+ // Cast const-functions to non-const and the other way around: if the functor
+ // has both const and non-const operator()s for a given parameter signature,
+ // constructing a Function must select one of them, depending on
+ // whether the function type template parameter is const-qualified or not.
+ // When the const-ness is later changed (by moving the
+ // Function<R(Args...)const> into a Function<R(Args...)> or by
+ // calling the folly::constCastFunction which moves it into a
+ // Function<R(Args...)const>), the Function must still execute
+ // the initially selected function.
+
+ auto variant1_const = folly::constCastFunction(std::move(variant1));
+ EXPECT_THROW(variant1(0), std::bad_function_call);
+ EXPECT_EQ(variant1_const(22), 100 + 1 * 22);
+
+ Function<int(int)> variant2_nonconst = std::move(variant2);
+ EXPECT_THROW(variant2(0), std::bad_function_call);
+ EXPECT_EQ(variant2_nonconst(23), 100 + 2 * 23);
+
+ auto variant3_const = folly::constCastFunction(std::move(variant3));
+ EXPECT_THROW(variant3(0, 0), std::bad_function_call);
+ EXPECT_EQ(variant3_const(24, 0), 100 + 3 * 24);
+
+ Function<int(int, int)> variant4_nonconst = std::move(variant4);
+ EXPECT_THROW(variant4(0, 0), std::bad_function_call);
+ EXPECT_EQ(variant4_nonconst(25, 0), 100 + 4 * 25);
+
+ auto variant5_const = folly::constCastFunction(std::move(variant5));
+ EXPECT_THROW(variant5(0, ""), std::bad_function_call);
+ EXPECT_EQ(variant5_const(26, "foo"), 100 + 5 * 26);
+
+ auto variant6_const = folly::constCastFunction(std::move(variant6));
+ EXPECT_THROW(variant6(0, {}), std::bad_function_call);
+ EXPECT_EQ(variant6_const(27, {}), 100 + 6 * 27);
+
+ Function<int(int, std::vector<int> const&)> variant6const_nonconst =
+ std::move(variant6const);
+ EXPECT_THROW(variant6const(0, {}), std::bad_function_call);
+ EXPECT_EQ(variant6const_nonconst(28, {}), 100 + 6 * 28);
+}
+
+// TEST =====================================================================
+// Lambda
+
+TEST(Function, Lambda) {
+ // Non-mutable lambdas: can be stored in a non-const...
+ Function<int(int)> func = [](int x) { return 1000 + x; };
+ EXPECT_EQ(func(1), 1001);
+
+ // ...as well as in a const Function
+ Function<int(int) const> func_const = [](int x) { return 2000 + x; };
+ EXPECT_EQ(func_const(1), 2001);
+
+ // Mutable lambda: can only be stored in a const Function:
+ int number = 3000;
+ Function<int()> func_mutable = [number]() mutable { return ++number; };
+ EXPECT_EQ(func_mutable(), 3001);
+ EXPECT_EQ(func_mutable(), 3002);
+
+ // test after const-casting
+
+ Function<int(int) const> func_made_const =
+ folly::constCastFunction(std::move(func));
+ EXPECT_EQ(func_made_const(2), 1002);
+ EXPECT_THROW(func(0), std::bad_function_call);
+
+ Function<int(int)> func_const_made_nonconst = std::move(func_const);
+ EXPECT_EQ(func_const_made_nonconst(2), 2002);
+ EXPECT_THROW(func_const(0), std::bad_function_call);
+
+ Function<int() const> func_mutable_made_const =
+ folly::constCastFunction(std::move(func_mutable));
+ EXPECT_EQ(func_mutable_made_const(), 3003);
+ EXPECT_EQ(func_mutable_made_const(), 3004);
+ EXPECT_THROW(func_mutable(), std::bad_function_call);
+}
+
+// TEST =====================================================================
+// DataMember & MemberFunction
+
+struct MemberFunc {
+ int x;
+ int getX() const {
+ return x;
+ }
+ void setX(int xx) {
+ x = xx;
+ }
+};
+
+TEST(Function, DataMember) {
+ MemberFunc mf;
+ MemberFunc const& cmf = mf;
+ mf.x = 123;
+
+ Function<int(MemberFunc const*)> data_getter1 = &MemberFunc::x;
+ EXPECT_EQ(data_getter1(&cmf), 123);
+ Function<int(MemberFunc*)> data_getter2 = &MemberFunc::x;
+ EXPECT_EQ(data_getter2(&mf), 123);
+ Function<int(MemberFunc const&)> data_getter3 = &MemberFunc::x;
+ EXPECT_EQ(data_getter3(cmf), 123);
+ Function<int(MemberFunc&)> data_getter4 = &MemberFunc::x;
+ EXPECT_EQ(data_getter4(mf), 123);
+}
+
+TEST(Function, MemberFunction) {
+ MemberFunc mf;
+ MemberFunc const& cmf = mf;
+ mf.x = 123;
+
+ Function<int(MemberFunc const*)> getter1 = &MemberFunc::getX;
+ EXPECT_EQ(getter1(&cmf), 123);
+ Function<int(MemberFunc*)> getter2 = &MemberFunc::getX;
+ EXPECT_EQ(getter2(&mf), 123);
+ Function<int(MemberFunc const&)> getter3 = &MemberFunc::getX;
+ EXPECT_EQ(getter3(cmf), 123);
+ Function<int(MemberFunc&)> getter4 = &MemberFunc::getX;
+ EXPECT_EQ(getter4(mf), 123);
+
+ Function<void(MemberFunc*, int)> setter1 = &MemberFunc::setX;
+ setter1(&mf, 234);
+ EXPECT_EQ(mf.x, 234);
+
+ Function<void(MemberFunc&, int)> setter2 = &MemberFunc::setX;
+ setter2(mf, 345);
+ EXPECT_EQ(mf.x, 345);
+}
+
+// TEST =====================================================================
+// CaptureCopyMoveCount & ParameterCopyMoveCount
+
+class CopyMoveTracker {
+ public:
+ struct ConstructorTag {};
+
+ CopyMoveTracker() = delete;
+ explicit CopyMoveTracker(ConstructorTag)
+ : data_(std::make_shared<std::pair<size_t, size_t>>(0, 0)) {}
+
+ CopyMoveTracker(CopyMoveTracker const& o) noexcept : data_(o.data_) {
+ ++data_->first;
+ }
+ CopyMoveTracker& operator=(CopyMoveTracker const& o) noexcept {
+ data_ = o.data_;
+ ++data_->first;
+ return *this;
+ }
+
+ CopyMoveTracker(CopyMoveTracker&& o) noexcept : data_(o.data_) {
+ ++data_->second;
+ }
+ CopyMoveTracker& operator=(CopyMoveTracker&& o) noexcept {
+ data_ = o.data_;
+ ++data_->second;
+ return *this;
+ }
+
+ size_t copyCount() const {
+ return data_->first;
+ }
+ size_t moveCount() const {
+ return data_->second;
+ }
+ size_t refCount() const {
+ return data_.use_count();
+ }
+ void resetCounters() {
+ data_->first = data_->second = 0;
+ }
+
+ private:
+ // copy, move
+ std::shared_ptr<std::pair<size_t, size_t>> data_;
+};
+
+TEST(Function, CaptureCopyMoveCount) {
+ // This test checks that no unnecessary copies/moves are made.
+
+ CopyMoveTracker cmt(CopyMoveTracker::ConstructorTag{});
+ EXPECT_EQ(cmt.copyCount(), 0);
+ EXPECT_EQ(cmt.moveCount(), 0);
+ EXPECT_EQ(cmt.refCount(), 1);
+
+ // Move into lambda, move lambda into Function
+ auto lambda1 = [cmt = std::move(cmt)]() {
+ return cmt.moveCount();
+ };
+ Function<size_t(void)> uf1 = std::move(lambda1);
+
+ // Max copies: 0. Max copy+moves: 2.
+ EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 2);
+ EXPECT_LE(cmt.copyCount(), 0);
+
+ cmt.resetCounters();
+
+ // Move into lambda, copy lambda into Function
+ auto lambda2 = [cmt = std::move(cmt)]() {
+ return cmt.moveCount();
+ };
+ Function<size_t(void)> uf2 = lambda2;
+
+ // Max copies: 1. Max copy+moves: 2.
+ EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 2);
+ EXPECT_LE(cmt.copyCount(), 1);
+
+ // Invoking Function must not make copies/moves of the callable
+ cmt.resetCounters();
+ uf1();
+ uf2();
+ EXPECT_EQ(cmt.copyCount(), 0);
+ EXPECT_EQ(cmt.moveCount(), 0);
+}
+
+TEST(Function, ParameterCopyMoveCount) {
+ // This test checks that no unnecessary copies/moves are made.
+
+ CopyMoveTracker cmt(CopyMoveTracker::ConstructorTag{});
+ EXPECT_EQ(cmt.copyCount(), 0);
+ EXPECT_EQ(cmt.moveCount(), 0);
+ EXPECT_EQ(cmt.refCount(), 1);
+
+ // pass by value
+ Function<size_t(CopyMoveTracker)> uf1 = [](CopyMoveTracker c) {
+ return c.moveCount();
+ };
+
+ cmt.resetCounters();
+ uf1(cmt);
+ // Max copies: 1. Max copy+moves: 2.
+ EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 2);
+ EXPECT_LE(cmt.copyCount(), 1);
+
+ cmt.resetCounters();
+ uf1(std::move(cmt));
+ // Max copies: 1. Max copy+moves: 2.
+ EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 2);
+ EXPECT_LE(cmt.copyCount(), 0);
+
+ // pass by reference
+ Function<size_t(CopyMoveTracker&)> uf2 = [](CopyMoveTracker& c) {
+ return c.moveCount();
+ };
+
+ cmt.resetCounters();
+ uf2(cmt);
+ // Max copies: 0. Max copy+moves: 0.
+ EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 0);
+ EXPECT_LE(cmt.copyCount(), 0);
+
+ // pass by const reference
+ Function<size_t(CopyMoveTracker const&)> uf3 = [](CopyMoveTracker const& c) {
+ return c.moveCount();
+ };
+
+ cmt.resetCounters();
+ uf3(cmt);
+ // Max copies: 0. Max copy+moves: 0.
+ EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 0);
+ EXPECT_LE(cmt.copyCount(), 0);
+
+ // pass by rvalue reference
+ Function<size_t(CopyMoveTracker &&)> uf4 = [](CopyMoveTracker&& c) {
+ return c.moveCount();
+ };
+
+ cmt.resetCounters();
+ uf4(std::move(cmt));
+ // Max copies: 0. Max copy+moves: 0.
+ EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 0);
+ EXPECT_LE(cmt.copyCount(), 0);
+}
+
+// TEST =====================================================================
+// CopyMoveThrows
+
+enum ExceptionType { COPY, MOVE };
+
+template <ExceptionType ET>
+class CopyMoveException : public std::runtime_error {
+ public:
+ using std::runtime_error::runtime_error;
+};
+
+template <bool CopyThrows, bool MoveThrows>
+struct CopyMoveThrowsCallable {
+ int allowCopyOperations{0};
+ int allowMoveOperations{0};
+
+ CopyMoveThrowsCallable() = default;
+ CopyMoveThrowsCallable(CopyMoveThrowsCallable const& o) noexcept(
+ !CopyThrows) {
+ *this = o;
+ }
+ CopyMoveThrowsCallable& operator=(CopyMoveThrowsCallable const& o) noexcept(
+ !CopyThrows) {
+ allowCopyOperations = o.allowCopyOperations;
+ allowMoveOperations = o.allowMoveOperations;
+
+ if (allowCopyOperations > 0) {
+ --allowCopyOperations;
+ } else if (CopyThrows) {
+ throw CopyMoveException<COPY>("CopyMoveThrowsCallable copy");
+ }
+ return *this;
+ }
+ CopyMoveThrowsCallable(CopyMoveThrowsCallable&& o) noexcept(!MoveThrows) {
+ *this = std::move(o);
+ }
+ CopyMoveThrowsCallable& operator=(CopyMoveThrowsCallable&& o) noexcept(
+ !MoveThrows) {
+ allowCopyOperations = o.allowCopyOperations;
+ allowMoveOperations = o.allowMoveOperations;
+
+ if (o.allowMoveOperations > 0) {
+ --allowMoveOperations;
+ } else if (MoveThrows) {
+ throw CopyMoveException<MOVE>("CopyMoveThrowsCallable move");
+ }
+ return *this;
+ }
+
+ void operator()() const {}
+};
+
+TEST(Function, CopyMoveThrowsCallable) {
+ EXPECT_TRUE((std::is_nothrow_move_constructible<
+ CopyMoveThrowsCallable<false, false>>::value));
+ EXPECT_TRUE((std::is_nothrow_move_constructible<
+ CopyMoveThrowsCallable<true, false>>::value));
+ EXPECT_FALSE((std::is_nothrow_move_constructible<
+ CopyMoveThrowsCallable<false, true>>::value));
+ EXPECT_FALSE((std::is_nothrow_move_constructible<
+ CopyMoveThrowsCallable<true, true>>::value));
+
+ EXPECT_TRUE((std::is_nothrow_copy_constructible<
+ CopyMoveThrowsCallable<false, false>>::value));
+ EXPECT_FALSE((std::is_nothrow_copy_constructible<
+ CopyMoveThrowsCallable<true, false>>::value));
+ EXPECT_TRUE((std::is_nothrow_copy_constructible<
+ CopyMoveThrowsCallable<false, true>>::value));
+ EXPECT_FALSE((std::is_nothrow_copy_constructible<
+ CopyMoveThrowsCallable<true, true>>::value));
+}
+
+template <FunctionMoveCtor NEM, bool CopyThrows, bool MoveThrows>
+void copy_and_move_throws_test() {
+ CopyMoveThrowsCallable<CopyThrows, MoveThrows> c;
+ Function<void(void), NEM> uf;
+
+ if (CopyThrows) {
+ EXPECT_THROW((uf = c), CopyMoveException<COPY>);
+ } else {
+ EXPECT_NO_THROW((uf = c));
+ }
+
+ if (MoveThrows) {
+ EXPECT_THROW((uf = std::move(c)), CopyMoveException<MOVE>);
+ } else {
+ EXPECT_NO_THROW((uf = std::move(c)));
+ }
+
+ c.allowMoveOperations = 1;
+ uf = std::move(c);
+ if (NEM == FunctionMoveCtor::MAY_THROW && MoveThrows) {
+ Function<void(void), NEM> uf2;
+ EXPECT_THROW((uf2 = std::move(uf)), CopyMoveException<MOVE>);
+ } else {
+ Function<void(void), NEM> uf2;
+ EXPECT_NO_THROW((uf2 = std::move(uf)));
+ }
+
+ c.allowMoveOperations = 0;
+ c.allowCopyOperations = 1;
+ uf = c;
+ if (NEM == FunctionMoveCtor::MAY_THROW && MoveThrows) {
+ Function<void(void), NEM> uf2;
+ EXPECT_THROW((uf2 = std::move(uf)), CopyMoveException<MOVE>);
+ } else {
+ Function<void(void), NEM> uf2;
+ EXPECT_NO_THROW((uf2 = std::move(uf)));
+ }
+}
+
+TEST(Function, CopyAndMoveThrows_TNN) {
+ copy_and_move_throws_test<FunctionMoveCtor::MAY_THROW, false, false>();
+}
+
+TEST(Function, CopyAndMoveThrows_NNN) {
+ copy_and_move_throws_test<FunctionMoveCtor::NO_THROW, false, false>();
+}
+
+TEST(Function, CopyAndMoveThrows_TTN) {
+ copy_and_move_throws_test<FunctionMoveCtor::MAY_THROW, true, false>();
+}
+
+TEST(Function, CopyAndMoveThrows_NTN) {
+ copy_and_move_throws_test<FunctionMoveCtor::NO_THROW, true, false>();
+}
+
+TEST(Function, CopyAndMoveThrows_TNT) {
+ copy_and_move_throws_test<FunctionMoveCtor::MAY_THROW, false, true>();
+}
+
+TEST(Function, CopyAndMoveThrows_NNT) {
+ copy_and_move_throws_test<FunctionMoveCtor::NO_THROW, false, true>();
+}
+
+TEST(Function, CopyAndMoveThrows_TTT) {
+ copy_and_move_throws_test<FunctionMoveCtor::MAY_THROW, true, true>();
+}
+
+TEST(Function, CopyAndMoveThrows_NTT) {
+ copy_and_move_throws_test<FunctionMoveCtor::NO_THROW, true, true>();
+}
+
+// TEST =====================================================================
+// VariadicTemplate & VariadicArguments
+
+struct VariadicTemplateSum {
+ int operator()() const {
+ return 0;
+ }
+ template <class... Args>
+ int operator()(int x, Args... args) const {
+ return x + (*this)(args...);
+ }
+};
+
+TEST(Function, VariadicTemplate) {
+ Function<int(int)> uf1 = VariadicTemplateSum();
+ Function<int(int, int)> uf2 = VariadicTemplateSum();
+ Function<int(int, int, int)> uf3 = VariadicTemplateSum();
+
+ EXPECT_EQ(uf1(66), 66);
+ EXPECT_EQ(uf2(55, 44), 99);
+ EXPECT_EQ(uf3(33, 22, 11), 66);
+}
+
+struct VariadicArgumentsSum {
+ int operator()(int count, ...) const {
+ int result = 0;
+ va_list args;
+ va_start(args, count);
+ for (int i = 0; i < count; ++i) {
+ result += va_arg(args, int);
+ }
+ va_end(args);
+ return result;
+ }
+};
+
+TEST(Function, VariadicArguments) {
+ Function<int(int)> uf1 = VariadicArgumentsSum();
+ Function<int(int, int)> uf2 = VariadicArgumentsSum();
+ Function<int(int, int, int)> uf3 = VariadicArgumentsSum();
+
+ EXPECT_EQ(uf1(0), 0);
+ EXPECT_EQ(uf2(1, 66), 66);
+ EXPECT_EQ(uf3(2, 55, 44), 99);
+}
+
+// TEST =====================================================================
+// SafeCaptureByReference
+
+// A function can use Function const& as a parameter to signal that it
+// is safe to pass a lambda that captures local variables by reference.
+// It is safe because we know the function called can only invoke the
+// Function until it returns. It can't store a copy of the Function
+// (because it's not copyable), and it can't move the Function somewhere
+// else (because it gets only a const&).
+
+template <typename T>
+void for_each(
+ T const& range,
+ Function<void(typename T::value_type const&) const> const& func) {
+ for (auto const& elem : range) {
+ func(elem);
+ }
+}
+
+TEST(Function, SafeCaptureByReference) {
+ std::vector<int> const vec = {20, 30, 40, 2, 3, 4, 200, 300, 400};
+
+ int sum = 0;
+
+ // for_each's second parameter is of type Function<...> const&.
+ // Hence we know we can safely pass it a lambda that references local
+ // variables. There is no way the reference to x will be stored anywhere.
+ for_each<std::vector<int>>(vec, [&sum](int x) { sum += x; });
+
+ // gcc versions before 4.9 cannot deduce the type T in the above call
+ // to for_each. Modern compiler versions can compile the following line:
+ // for_each(vec, [&sum](int x) { sum += x; });
+
+ EXPECT_EQ(sum, 999);
+}
futures_test_LDADD = libfollytestmain.la
TESTS += futures_test
+function_test_SOURCES = FunctionTest.cpp
+function_test_LDADD = libfollytestmain.la
+TESTS += function_test
+
check_PROGRAMS += $(TESTS)
}
}
+void BM_Function_invoke_impl(int iters,
+ const folly::Function<void() const>& fn) {
+ for (int n = 0; n < iters; ++n) {
+ fn();
+ }
+}
+
void BM_mem_fn_invoke_impl(int iters,
TestClass* tc,
void (TestClass::*memfn)()) {
#include <functional>
+#include <folly/Function.h>
+
class TestClass;
class VirtualClass;
void BM_fn_ptr_invoke_impl(int iters, void (*fn)());
void BM_std_function_invoke_impl(int iters, const std::function<void()>& fn);
+void BM_Function_invoke_impl(int iters,
+ const folly::Function<void() const>& fn);
void BM_mem_fn_invoke_impl(int iters,
TestClass* tc,
void (TestClass::*memfn)());
BM_std_function_invoke_impl(iters, doNothing);
}
+// Invoking a function through a folly::Function object
+BENCHMARK(Function_invoke, iters) {
+ BM_Function_invoke_impl(iters, doNothing);
+}
+
// Invoking a member function through a member function pointer
BENCHMARK(mem_fn_invoke, iters) {
TestClass tc;
}
}
+// Creating a folly::Function object from a function pointer, and
+// invoking it
+BENCHMARK(Function_create_invoke, iters) {
+ for (size_t n = 0; n < iters; ++n) {
+ folly::Function<void()> fn = doNothing;
+ fn();
+ }
+}
+
// Creating a pointer-to-member and invoking it
BENCHMARK(mem_fn_create_invoke, iters) {
TestClass tc;
}
}
+// Using ScopeGuard to invoke a folly::Function,
+// but create the ScopeGuard with an rvalue to a folly::Function
+BENCHMARK(scope_guard_Function_rvalue, iters) {
+ for (size_t n = 0; n < iters; ++n) {
+ ScopeGuard g = makeGuard(folly::Function<void()>(doNothing));
+ }
+}
+
// Using ScopeGuard to invoke a function pointer
BENCHMARK(scope_guard_fn_ptr, iters) {
for (size_t n = 0; n < iters; ++n) {