+++ /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,
- typename std::enable_if<std::is_same<
- typename Traits::NonConstFunctionType,
- typename detail::function::FunctionTypeTraits<
- OtherFunctionType>::NonConstFunctionType>::value>::
- type*) noexcept(OtherNTM == FunctionMoveCtor::NO_THROW &&
- EmbedFunctorSize >= OtherEmbedFunctorSize) {
- using OtherFunction =
- Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>;
-
- 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 to check whether the return type of a callable matches that of
-// a folly::Function object. Either because the former is convertible to
-// the latter, or the latter is void (possibly cv-qualified)
-template <typename CallableR, typename FollyFunctionR>
-using ReturnTypeMatches = std::integral_constant<
- bool,
- std::is_convertible<CallableR, FollyFunctionR>::value ||
- std::is_same<typename std::decay<FollyFunctionR>::type, void>::value>;
-
-// 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 =
- ReturnTypeMatches<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 =
- ReturnTypeMatches<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 T is a Function with the same
-// function type as OtherFunctionType (except for const-ness which may differ)
-template <typename T, typename OtherFunctionType>
-struct IsFunction : std::false_type {};
-
-template <
- typename FunctionType,
- FunctionMoveCtor NTM,
- size_t EmbedFunctorSize,
- typename OtherFunctionType>
-struct IsFunction<
- ::folly::Function<FunctionType, NTM, EmbedFunctorSize>,
- OtherFunctionType>
- : std::is_same<
- typename FunctionTypeTraits<FunctionType>::NonConstFunctionType,
- typename FunctionTypeTraits<
- OtherFunctionType>::NonConstFunctionType> {};
-
-// Helper template to check if a functor can be called with arguments of type
-// Args..., if it returns a type convertible to R (or R is void), and also is
-// not a folly::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, FunctionType>::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 static_cast<R>(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 static_cast<R>(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 <class Function>
-struct InvokeFromSharedPtr final {
- std::shared_ptr<Function> ptr_;
-
- explicit InvokeFromSharedPtr(std::shared_ptr<Function> ptr)
- : ptr_(std::move(ptr)) {}
-
- template <typename... Args>
- auto operator()(Args&&... args)
- -> decltype((*ptr_)(std::forward<Args>(args)...)) {
- return (*ptr_)(std::forward<Args>(args)...);
- }
-};
-
-} // namespace function
-} // namespace detail
-} // namespace folly
/*
* 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)
*/
/**
* 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;
* However, in that case what you do is potentially dangerous and requires
* the equivalent of a `const_cast`, hence you need to call
* `constCastFunction`.
- *
- * `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 <memory>
+#include <new>
#include <type_traits>
-#include <typeinfo>
#include <utility>
-#include <folly/ScopeGuard.h>
-#include <folly/portability/Constexpr.h>
+#include <folly/CppAttributes.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)>)>
+namespace impl {
+template <typename FunctionType, bool Const = false>
class Function;
-} // folly
+template <typename ReturnType, typename... Args>
+Function<ReturnType(Args...), true> constCastFunction(
+ Function<ReturnType(Args...), false>&&) 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;
+ typename std::aligned_storage<6 * sizeof(void*)>::type small;
+};
- public:
- // not copyable
- Function(Function const&) = delete;
- Function& operator=(Function const&) = delete;
+struct Tag {};
+
+template <bool If, typename T>
+using ConstIf = typename std::conditional<If, const T, T>::type;
+
+template <typename Fun, typename FunT = typename std::decay<Fun>::type>
+using IsSmall = std::integral_constant<
+ bool,
+ (sizeof(FunT) <= sizeof(Data::small) &&
+#if defined(__GNUC__) && !defined(__clang__)
+ // GCC has a name mangling bug that causes hard errors if we use noexcept
+ // directly here. Last tested at gcc 5.3.0.
+ // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70790
+ std::is_nothrow_move_constructible<FunT>::value
+#else
+ // Same as is_nothrow_move_constructible, but w/ no template instantiation.
+ noexcept(FunT(std::declval<FunT&&>()))
+#endif
+ )>;
+
+template <typename T>
+bool isNullPtrFn(T* p) {
+ return p == nullptr;
+}
+template <typename T>
+std::false_type isNullPtrFn(T&&) {
+ return {};
+}
- /**
- * Default constructor. Constructs an empty Function.
- */
- Function() noexcept {
- initializeEmptyExecutor();
- }
+template <typename ReturnType, typename... Args>
+ReturnType uninitCall(Data&, Args&&...) {
+ throw std::bad_function_call();
+}
+inline bool uninitNoop(Op, Data*, Data*) {
+ return false;
+}
+} // namespace function
+} // namespace detail
- ~Function() {
- destroyExecutor();
+namespace impl {
- static_assert(
- kStorageSize == sizeof(*this),
- "There is something wrong with the size of Function");
- }
+template <typename ReturnType, typename... Args, bool Const>
+class Function<ReturnType(Args...), Const> final {
+ using Data = detail::function::Data;
+ using Op = detail::function::Op;
+ using Tag = detail::function::Tag;
+ using Call = ReturnType (*)(Data&, Args&&...);
+ using Exec = bool (*)(Op, Data*, Data*);
- // construct/assign from Function
- /**
- * Move constructor
- */
- Function(Function&& other) noexcept(hasNoExceptMoveCtor());
- /**
- * Move assignment operator
- */
- Function& operator=(Function&& rhs) noexcept(hasNoExceptMoveCtor());
+ template <typename T>
+ using ConstIf = detail::function::ConstIf<Const, T>;
+ template <typename Fun>
+ using IsSmall = detail::function::IsSmall<Fun>;
- /**
- * 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::NonConstFunctionType> asStdFunction() && {
- return detail::function::InvokeFromSharedPtr<Function>(
- std::make_shared<Function>(std::move(*this)));
+ Data data_;
+ Call call_;
+ Exec exec_;
+
+ friend Function<ReturnType(Args...), true> constCastFunction<>(
+ Function<ReturnType(Args...), false>&&) noexcept;
+ friend class Function<ReturnType(Args...), !Const>;
+
+ template <typename Fun, typename FunT = typename std::decay<Fun>::type>
+ Function(
+ Fun&& fun,
+ typename std::enable_if<IsSmall<Fun>::value, Tag>::
+ type) noexcept(noexcept(FunT(std::declval<Fun>())))
+ : Function() {
+ struct Ops {
+ static ReturnType call(Data& p, Args&&... args) {
+ return static_cast<ReturnType>((*static_cast<ConstIf<FunT>*>(
+ (void*)&p.small))(static_cast<Args&&>(args)...));
+ }
+ static bool exec(Op o, Data* src, Data* dst) {
+ switch (o) {
+ case Op::MOVE:
+ ::new ((void*)&dst->small)
+ FunT(std::move(*static_cast<FunT*>((void*)&src->small)));
+ FOLLY_FALLTHROUGH;
+ case Op::NUKE:
+ static_cast<FunT*>((void*)&src->small)->~FunT();
+ break;
+ case Op::FULL:
+ return true;
+ case Op::HEAP:
+ break;
+ }
+ return false;
+ }
+ };
+ if (!detail::function::isNullPtrFn(fun)) {
+ ::new (&data_.small) FunT(static_cast<Fun&&>(fun));
+ exec_ = &Ops::exec;
+ call_ = &Ops::call;
+ }
+ }
+
+ template <typename Fun, typename FunT = typename std::decay<Fun>::type>
+ Function(Fun&& fun, typename std::enable_if<!IsSmall<Fun>::value, Tag>::type)
+ : Function() {
+ struct Ops {
+ static ReturnType call(Data& p, Args&&... args) {
+ return static_cast<ReturnType>((*static_cast<ConstIf<FunT>*>(p.big))(
+ static_cast<Args&&>(args)...));
+ }
+ static bool exec(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<FunT*>(src->big);
+ break;
+ case Op::FULL:
+ case Op::HEAP:
+ break;
+ }
+ return true;
+ }
+ };
+ data_.big = new FunT(static_cast<Fun&&>(fun));
+ call_ = &Ops::call;
+ exec_ = &Ops::exec;
}
+ template <typename F, typename G = typename std::decay<F>::type>
+ using ResultOf = decltype(static_cast<ReturnType>(
+ std::declval<ConstIf<G>&>()(std::declval<Args>()...)));
+ public:
/**
- * Constructs a `Function` by moving from one with different template
- * parameters with regards to const-ness, no-except-movability and internal
- * storage size.
+ * Default constructor. Constructs an empty Function.
*/
- template <
- typename OtherFunctionType,
- FunctionMoveCtor OtherNTM,
- size_t OtherEmbedFunctorSize>
- Function(
- Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>&& other,
- typename std::enable_if<std::is_same<
- typename Traits::NonConstFunctionType,
- typename detail::function::FunctionTypeTraits<
- OtherFunctionType>::NonConstFunctionType>::value>::type* =
- 0) noexcept(OtherNTM == FunctionMoveCtor::NO_THROW &&
- EmbedFunctorSize >= OtherEmbedFunctorSize);
+ Function() noexcept
+ : call_(&detail::function::uninitCall<ReturnType, Args...>),
+ exec_(&detail::function::uninitNoop) {}
+
+ // 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;
/**
- * Moves a `Function` with different template parameters with regards
- * to const-ness, no-except-movability and internal storage size into this
- * one.
+ * Move constructor
*/
- template <
- typename RhsFunctionType,
- FunctionMoveCtor RhsNTM,
- size_t RhsEmbedFunctorSize>
- Function& operator=(Function<RhsFunctionType, RhsNTM, RhsEmbedFunctorSize>&&
- rhs) noexcept(RhsNTM == FunctionMoveCtor::NO_THROW);
+ Function(Function&& that) noexcept : Function() {
+ that.exec_(Op::MOVE, &that.data_, &data_);
+ std::swap(call_, that.call_);
+ std::swap(exec_, that.exec_);
+ }
/**
* 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,
* 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 <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 <class Fun, typename = ResultOf<Fun>>
+ /* implicit */ Function(Fun&& fun) noexcept(
+ noexcept(Function(std::declval<Fun>(), Tag{})))
+ : Function(static_cast<Fun&&>(fun), Tag{}) {}
/**
- * Assigns a callable object to this `Function`.
+ * For moving a `Function<X(Ys..) const>` into a `Function<X(Ys...)>`.
*/
- 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;
+ template <
+ bool OtherConst,
+ typename std::enable_if<!Const && OtherConst, int>::type = 0>
+ Function(Function<ReturnType(Args...), OtherConst>&& that) noexcept
+ : Function() {
+ that.exec_(Op::MOVE, &that.data_, &data_);
+ std::swap(call_, that.call_);
+ std::swap(exec_, that.exec_);
}
/**
- * 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.
+ * If `ptr` is null, constructs an empty `Function`. Otherwise,
+ * this constructor is equivalent to `Function(std::mem_fn(ptr))`.
*/
- template <FunctionMoveCtor OtherNTM, size_t OtherEmbedFunctorSize>
- void
- swap(Function<FunctionType, OtherNTM, OtherEmbedFunctorSize>& o) noexcept(
- hasNoExceptMoveCtor() && OtherNTM == FunctionMoveCtor::NO_THROW);
+ 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 : Function() {
+ if (ptr) {
+ *this = std::mem_fn(ptr);
+ }
+ }
+
+ ~Function() {
+ exec_(Op::NUKE, &data_, nullptr);
+ }
+
+ Function& operator=(Function&) = delete;
+ Function& operator=(const Function&) = delete;
/**
- * Returns `true` if this `Function` contains a callable, i.e. is
- * non-empty.
+ * Move assignment operator
*/
- explicit operator bool() const noexcept;
+ 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;
+ }
/**
- * 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.
+ * 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.
*/
- bool hasAllocatedMemory() const noexcept;
+ template <class Fun, typename = 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 the `type_info` (as returned by `typeid`) of the callable stored
- * in this `Function`. Returns `typeid(void)` if empty.
+ * Clears this `Function`.
*/
- std::type_info const& target_type() const noexcept;
+ Function& operator=(std::nullptr_t) noexcept {
+ return (*this = Function());
+ }
/**
- * Returns a pointer to the stored callable if its type matches `T`, and
- * `nullptr` otherwise.
+ * If `ptr` is null, clears this `Function`. Otherwise, this assignment
+ * operator is equivalent to `*this = std::mem_fn(ptr)`.
*/
- template <typename T>
- T* target() 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 a const-pointer to the stored callable if its type matches `T`,
- * and `nullptr` otherwise.
+ * Call the wrapped callable object with the specified arguments.
+ * If this `Function` object is a const `folly::Function` object,
+ * this overload shall not participate in overload resolution.
*/
- template <typename T>
- const T* target() const noexcept;
+ template <
+ // `True` makes `operator()` a template so we can SFINAE on `Const`,
+ // which is non-deduced here.
+ bool True = true,
+ typename std::enable_if<True && !Const, int>::type = 0>
+ ReturnType operator()(Args... args) {
+ return call_(data_, static_cast<Args&&>(args)...);
+ }
/**
- * 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!
+ * Call the wrapped callable object with the specified arguments.
+ * If this `Function` object is not a const `folly::Function` object,
+ * this overload shall not participate in overload resolution.
*/
- 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 <
+ // `True` makes `operator()` a template so we can SFINAE on `Const`,
+ // which is non-deduced here.
+ bool True = true,
+ typename std::enable_if<True && Const, int>::type = 0>
+ ReturnType operator()(Args... args) const {
+ return call_(const_cast<Data&>(data_), static_cast<Args&&>(args)...);
+ }
- template <typename F>
- void createExecutor(F&& f) noexcept(
- noexcept(typename std::decay<F>::type(std::forward<F>(f))));
+ /**
+ * Exchanges the callable objects of `*this` and `that`.
+ */
+ void swap(Function& that) noexcept {
+ std::swap(*this, that);
+ }
- void destroyExecutor() noexcept;
+ /**
+ * Returns `true` if this `Function` contains a callable, i.e. is
+ * non-empty.
+ */
+ explicit operator bool() const noexcept {
+ return exec_(Op::FULL, nullptr, nullptr);
+ }
- struct MinStorageSize;
+ /**
+ * 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 {
+ 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<ReturnType(Args...)> asStdFunction() && {
+ struct Impl {
+ std::shared_ptr<Function> sp_;
+ ReturnType operator()(Args&&... args) const {
+ return (*sp_)(static_cast<Args&&>(args)...);
+ }
+ };
+ return Impl{std::make_shared<Function>(std::move(*this))};
+ }
};
-// operator==
-template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
-inline bool operator==(
- Function<FunctionType, NTM, EmbedFunctorSize> const& f,
- std::nullptr_t) noexcept {
- return !f;
+template <typename FunctionType, bool Const>
+void swap(
+ Function<FunctionType, Const>& lhs,
+ Function<FunctionType, Const>& rhs) noexcept {
+ lhs.swap(rhs);
}
-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 Const>
+bool operator==(const Function<FunctionType, Const>& fn, std::nullptr_t) {
+ 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 Const>
+bool operator==(std::nullptr_t, const Function<FunctionType, Const>& fn) {
+ 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 Const>
+bool operator!=(const Function<FunctionType, Const>& fn, std::nullptr_t) {
+ return !(fn == nullptr);
}
-/**
- * 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();
+template <typename FunctionType, bool Const>
+bool operator!=(std::nullptr_t, const Function<FunctionType, Const>& fn) {
+ return !(nullptr == fn);
}
-template <typename FunctionType, FunctionMoveCtor NOM, size_t S>
-void swap(
- Function<FunctionType, NOM, S>& lhs,
- Function<FunctionType, NOM, S>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
- lhs.swap(rhs);
+template <typename ReturnType, typename... Args>
+Function<ReturnType(Args...), true> constCastFunction(
+ Function<ReturnType(Args...), false>&& that) noexcept {
+ Function<ReturnType(Args...), true> fn{};
+ that.exec_(detail::function::Op::MOVE, &that.data_, &fn.data_);
+ std::swap(fn.call_, that.call_);
+ std::swap(fn.exec_, that.exec_);
+ return fn;
}
-template <
- typename FunctionType,
- FunctionMoveCtor NOM1,
- FunctionMoveCtor NOM2,
- size_t S1,
- size_t S2>
-void swap(
- Function<FunctionType, NOM1, S1>& lhs,
- Function<FunctionType, NOM2, S2>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
- lhs.swap(rhs);
+template <typename FunctionType>
+Function<FunctionType, true> constCastFunction(
+ Function<FunctionType, true>&& that) noexcept {
+ return std::move(that);
}
-} // namespace folly
+template <typename FunctionType>
+struct MakeFunction {};
+
+template <typename ReturnType, typename... Args>
+struct MakeFunction<ReturnType(Args...)> {
+ using type = Function<ReturnType(Args...), false>;
+};
-#include "Function-inl.h"
+template <typename ReturnType, typename... Args>
+struct MakeFunction<ReturnType(Args...) const> {
+ using type = Function<ReturnType(Args...), true>;
+};
+} // namespace impl
+
+/* using override */ using impl::constCastFunction;
+
+template <typename FunctionType>
+using Function = typename impl::MakeFunction<FunctionType>::type;
+}
Traits.h \
Unicode.h \
Function.h \
- Function-inl.h \
- Function-pre.h \
Uri.h \
Uri-inl.h \
Varint.h \
// sizeof(Core<T>) == size(Core<U>).
// See Core::convert for details.
- folly::Function<
- void(Try<T>&&),
- folly::FunctionMoveCtor::MAY_THROW,
- 8 * sizeof(void*)>
- callback_;
+ folly::Function<void(Try<T>&&)> callback_;
// place result_ next to increase the likelihood that the value will be
// contained entirely in one cache line
folly::Optional<Try<T>> result_;
#include <folly/Memory.h>
#include <gtest/gtest.h>
-using folly::FunctionMoveCtor;
using folly::Function;
namespace {
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;
}
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(43, func(42));
- 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(52, func_safe_move(42));
- 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(52, func_movethrows(42));
- EXPECT_FALSE(func_movethrows.hasAllocatedMemory());
- EXPECT_FALSE(
- std::is_nothrow_move_constructible<decltype(func_movethrows)>::value);
-}
+} // namespace
// TEST =====================================================================
// InvokeFunctor & InvokeReference
-template <FunctionMoveCtor NEM, size_t S>
-void invoke_functor_test() {
+TEST(Function, InvokeFunctor) {
Functor<int, 100> func;
+ static_assert(
+ sizeof(func) > sizeof(Function<int(size_t)>),
+ "sizeof(Function) is much larger than expected");
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);
+ Function<int(size_t) const> getter = std::move(func);
+
+ // Function will allocate memory on the heap to store the functor object
+ EXPECT_TRUE(getter.hasAllocatedMemory());
EXPECT_EQ(123, getter(5));
}
-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() {
+TEST(Function, InvokeReference) {
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);
+ // Have Functions for getter and setter, both referencing the same funtor
+ Function<int(size_t) const> getter = std::ref(func);
+ Function<int(size_t, int)> setter = std::ref(func);
EXPECT_EQ(123, getter(5));
EXPECT_EQ(123, setter(5, 456));
EXPECT_EQ(456, setter(5, 567));
EXPECT_EQ(567, getter(5));
}
-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;
+TEST(Function, Emptiness_T) {
+ Function<int(int)> 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; });
+ Function<int(int)> g([](int x) { return x + 1; });
EXPECT_NE(g, nullptr);
EXPECT_NE(nullptr, g);
EXPECT_TRUE(g);
EXPECT_EQ(100, g(99));
- Function<int(int), NEM> h(&func_int_int_add_25);
+ Function<int(int)> h(&func_int_int_add_25);
EXPECT_NE(h, nullptr);
EXPECT_NE(nullptr, h);
EXPECT_TRUE(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, bool UseSwapMethod>
+template <bool UseSwapMethod>
void swap_test() {
- Function<int(int), NEM1> mf1(func_int_int_add_25);
- Function<int(int), NEM2> mf2(func_int_int_add_111);
+ Function<int(int)> mf1(func_int_int_add_25);
+ Function<int(int)> mf2(func_int_int_add_111);
EXPECT_EQ(125, mf1(100));
EXPECT_EQ(211, mf2(100));
EXPECT_EQ(nullptr, mf3);
EXPECT_EQ(322, mf1(100));
}
-TEST(Function, SwapMethod_TT) {
- swap_test<FunctionMoveCtor::MAY_THROW, FunctionMoveCtor::MAY_THROW, true>();
-}
-TEST(Function, SwapMethod_TN) {
- swap_test<FunctionMoveCtor::MAY_THROW, FunctionMoveCtor::NO_THROW, true>();
-}
-TEST(Function, SwapMethod_NT) {
- swap_test<FunctionMoveCtor::NO_THROW, FunctionMoveCtor::MAY_THROW, true>();
+TEST(Function, SwapMethod) {
+ swap_test<true>();
}
-TEST(Function, SwapMethod_NN) {
- swap_test<FunctionMoveCtor::NO_THROW, FunctionMoveCtor::NO_THROW, true>();
-}
-TEST(Function, SwapFunction_TT) {
- swap_test<FunctionMoveCtor::MAY_THROW, FunctionMoveCtor::MAY_THROW, false>();
-}
-TEST(Function, SwapFunction_TN) {
- swap_test<FunctionMoveCtor::MAY_THROW, FunctionMoveCtor::NO_THROW, false>();
-}
-TEST(Function, SwapFunction_NT) {
- swap_test<FunctionMoveCtor::NO_THROW, FunctionMoveCtor::MAY_THROW, false>();
-}
-TEST(Function, SwapFunction_NN) {
- swap_test<FunctionMoveCtor::NO_THROW, FunctionMoveCtor::NO_THROW, false>();
+TEST(Function, SwapFunction) {
+ swap_test<false>();
}
// TEST =====================================================================
// Bind
-template <FunctionMoveCtor NEM>
-void bind_test() {
- Function<float(float, float), NEM> fnc = floatMult;
+TEST(Function, Bind) {
+ Function<float(float, float)> fnc = floatMult;
auto task = std::bind(std::move(fnc), 2.f, 4.f);
EXPECT_THROW(fnc(0, 0), std::bad_function_call);
EXPECT_EQ(8, task());
EXPECT_THROW(task(), std::bad_function_call);
EXPECT_EQ(8, task2());
}
-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() {
+TEST(Function, NonCopyableLambda) {
auto unique_ptr_int = folly::make_unique<int>(900);
EXPECT_EQ(900, *unique_ptr_int);
EXPECT_EQ(901, functor());
- 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));
+ Function<int(void)> func = std::move(functor);
+ EXPECT_TRUE(func.hasAllocatedMemory());
EXPECT_EQ(902, func());
}
-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(123, functor(3));
-
- // 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(123, func2x(3, 200));
- EXPECT_EQ(200, func2x(3, 201));
-
- // 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(201, func1x(3, 202));
- EXPECT_EQ(202, func1x(3, 203));
-
- // 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(203, func0x(3, 204));
- EXPECT_EQ(204, func0x(3, 205));
-
- // 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(205, funcnot(3, 206));
- EXPECT_EQ(206, funcnot(3, 207));
-}
-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(100, *shared_int);
- EXPECT_EQ(1, shared_int.use_count());
-
- Function<int(void), NEM> func1 = [shared_int]() { return ++*shared_int; };
- EXPECT_EQ(2, shared_int.use_count());
- EXPECT_EQ(101, func1());
- EXPECT_EQ(101, *shared_int);
-
- // 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(2, shared_int.use_count());
- EXPECT_FALSE(func1);
- EXPECT_EQ(102, func2());
- EXPECT_EQ(102, *shared_int);
-
- func2 = [shared_int]() { return ++*shared_int; };
- EXPECT_EQ(2, shared_int.use_count());
- EXPECT_EQ(103, func2());
- EXPECT_EQ(103, *shared_int);
-
- // 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(999, func2());
- EXPECT_EQ(1, shared_int.use_count());
- EXPECT_EQ(103, *shared_int);
-
- func2 = [shared_int]() { return ++*shared_int; };
- EXPECT_EQ(2, shared_int.use_count());
- EXPECT_EQ(104, func2());
- EXPECT_EQ(104, *shared_int);
-
- // 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(987, func2());
- EXPECT_EQ(1, shared_int.use_count());
- EXPECT_EQ(104, *shared_int);
-}
-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(125, func(100));
-
- Function<int(int), NEM> ufunc = std::move(func);
- EXPECT_THROW(func(0), std::bad_function_call);
- EXPECT_EQ(225, ufunc(200));
-
- EXPECT_EQ(typeid(std::function<int(int)>), ufunc.target_type());
-
- 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(325, ufunc_target(300));
-}
-
-TEST(Function, Target_T) {
- target_test<FunctionMoveCtor::MAY_THROW>();
-}
-TEST(Function, Target_N) {
- target_test<FunctionMoveCtor::NO_THROW>();
-}
// TEST =====================================================================
// OverloadedFunctor
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
EXPECT_EQ(66, cf9().x);
}
+// TEST =====================================================================
+// asStdFunction_*
+
TEST(Function, asStdFunction_void) {
int i = 0;
folly::Function<void()> f = [&] { ++i; };
sf(42, 42);
EXPECT_EQ(1, i);
}
+
+TEST(Function, NoAllocatedMemoryAfterMove) {
+ Functor<int, 100> foo;
+
+ Function<int(size_t)> func = foo;
+ EXPECT_TRUE(func.hasAllocatedMemory());
+
+ Function<int(size_t)> func2 = std::move(func);
+ EXPECT_TRUE(func2.hasAllocatedMemory());
+ EXPECT_FALSE(func.hasAllocatedMemory());
+}
+
+TEST(Function, ConstCastEmbedded) {
+ int x = 0;
+ auto functor = [&x]() { ++x; };
+
+ Function<void() const> func(functor);
+ EXPECT_FALSE(func.hasAllocatedMemory());
+
+ Function<void()> func2(std::move(func));
+ EXPECT_FALSE(func2.hasAllocatedMemory());
+}
+
+TEST(Function, EmptyAfterConstCast) {
+ Function<int(size_t)> func;
+ EXPECT_FALSE(func);
+
+ Function<int(size_t) const> func2 = constCastFunction(std::move(func));
+ EXPECT_FALSE(func2);
+}
+
+TEST(Function, SelfMoveAssign) {
+ Function<int()> f = [] { return 0; };
+ Function<int()>& g = f;
+ f = std::move(g);
+ EXPECT_TRUE(f);
+}