X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FFunction.h;h=c3c3699a1459a11c5abd386fa588927d450ef113;hp=d6fb29f6010b16735b52ebfd8254e365a1e87f3c;hb=b35b5a98a37b889bd6748d8cc3a7d357b9143e42;hpb=62001f5ebab94e79faac4773023ff8381eff0ff1 diff --git a/folly/Function.h b/folly/Function.h index d6fb29f6..c3c3699a 100644 --- a/folly/Function.h +++ b/folly/Function.h @@ -1,7 +1,5 @@ /* - * Copyright 2016 Facebook, Inc. - * - * @author Eric Niebler (eniebler@fb.com), Sven Over (over@fb.com) + * Copyright 2016-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +12,9 @@ * 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. - * + */ +/* + * @author Eric Niebler (eniebler@fb.com), Sven Over (over@fb.com) * Acknowledgements: Giuseppe Ottaviano (ott@fb.com) */ @@ -225,6 +225,8 @@ #include #include +#include +#include namespace folly { @@ -235,6 +237,12 @@ template Function constCastFunction( Function&&) noexcept; +#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE +template +Function constCastFunction( + Function&&) noexcept; +#endif + namespace detail { namespace function { @@ -245,15 +253,22 @@ union Data { std::aligned_storage<6 * sizeof(void*)>::type tiny; }; -template ::type> -using IsSmall = std::integral_constant< - bool, - (sizeof(FunT) <= sizeof(Data::tiny) && - // Same as is_nothrow_move_constructible, but w/ no template instantiation. - noexcept(FunT(std::declval())))>; +template +using IsSmall = Conjunction< + std::integral_constant, + std::is_nothrow_move_constructible>; using SmallTag = std::true_type; using HeapTag = std::false_type; +template +struct NotFunction : std::true_type {}; +template +struct NotFunction> : std::false_type {}; + +template +using EnableIfNotFunction = + typename std::enable_if::value>::type; + struct CoerceTag {}; template @@ -269,6 +284,16 @@ inline bool uninitNoop(Op, Data*, Data*) { return false; } +template +using CallableResult = decltype(std::declval()(std::declval()...)); + +template < + typename From, + typename To, + typename = typename std::enable_if< + !std::is_reference::value || std::is_reference::value>::type> +using SafeResultOf = decltype(static_cast(std::declval())); + template struct FunctionTraits; @@ -280,9 +305,9 @@ struct FunctionTraits { using NonConstSignature = ReturnType(Args...); using OtherSignature = ConstSignature; - template ::type> - using ResultOf = decltype( - static_cast(std::declval()(std::declval()...))); + template + using ResultOf = + SafeResultOf>&, Args...>, ReturnType>; template static ReturnType callSmall(Data& p, Args&&... args) { @@ -301,17 +326,16 @@ struct FunctionTraits { } ReturnType operator()(Args... args) { - auto& fn = *static_cast*>(this); + auto& fn = *static_cast*>(this); return fn.call_(fn.data_, static_cast(args)...); } class SharedProxy { - std::shared_ptr> sp_; + std::shared_ptr> sp_; public: - explicit SharedProxy(Function&& func) - : sp_(std::make_shared>( - std::move(func))) {} + explicit SharedProxy(Function&& func) + : sp_(std::make_shared>(std::move(func))) {} ReturnType operator()(Args&&... args) const { return (*sp_)(static_cast(args)...); } @@ -326,9 +350,10 @@ struct FunctionTraits { using NonConstSignature = ReturnType(Args...); using OtherSignature = NonConstSignature; - template ::type> - using ResultOf = decltype(static_cast( - std::declval()(std::declval()...))); + template + using ResultOf = SafeResultOf< + CallableResult>&, Args...>, + ReturnType>; template static ReturnType callSmall(Data& p, Args&&... args) { @@ -347,22 +372,114 @@ struct FunctionTraits { } ReturnType operator()(Args... args) const { - auto& fn = *static_cast*>(this); + auto& fn = *static_cast*>(this); + return fn.call_(fn.data_, static_cast(args)...); + } + + class SharedProxy { + std::shared_ptr> sp_; + + public: + explicit SharedProxy(Function&& func) + : sp_(std::make_shared>(std::move(func))) {} + ReturnType operator()(Args&&... args) const { + return (*sp_)(static_cast(args)...); + } + }; +}; + +#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE +template +struct FunctionTraits { + using Call = ReturnType (*)(Data&, Args&&...) noexcept; + using IsConst = std::false_type; + using ConstSignature = ReturnType(Args...) const noexcept; + using NonConstSignature = ReturnType(Args...) noexcept; + using OtherSignature = ConstSignature; + + template + using ResultOf = + SafeResultOf>&, Args...>, ReturnType>; + + template + static ReturnType callSmall(Data& p, Args&&... args) noexcept { + return static_cast((*static_cast( + static_cast(&p.tiny)))(static_cast(args)...)); + } + + template + static ReturnType callBig(Data& p, Args&&... args) noexcept { + return static_cast( + (*static_cast(p.big))(static_cast(args)...)); + } + + static ReturnType uninitCall(Data&, Args&&...) noexcept { + throw std::bad_function_call(); + } + + ReturnType operator()(Args... args) noexcept { + auto& fn = *static_cast*>(this); + return fn.call_(fn.data_, static_cast(args)...); + } + + class SharedProxy { + std::shared_ptr> sp_; + + public: + explicit SharedProxy(Function&& func) + : sp_(std::make_shared>(std::move(func))) {} + ReturnType operator()(Args&&... args) const { + return (*sp_)(static_cast(args)...); + } + }; +}; + +template +struct FunctionTraits { + using Call = ReturnType (*)(Data&, Args&&...) noexcept; + using IsConst = std::true_type; + using ConstSignature = ReturnType(Args...) const noexcept; + using NonConstSignature = ReturnType(Args...) noexcept; + using OtherSignature = NonConstSignature; + + template + using ResultOf = SafeResultOf< + CallableResult>&, Args...>, + ReturnType>; + + template + static ReturnType callSmall(Data& p, Args&&... args) noexcept { + return static_cast((*static_cast( + static_cast(&p.tiny)))(static_cast(args)...)); + } + + template + static ReturnType callBig(Data& p, Args&&... args) noexcept { + return static_cast( + (*static_cast(p.big))(static_cast(args)...)); + } + + static ReturnType uninitCall(Data&, Args&&...) noexcept { + throw std::bad_function_call(); + } + + ReturnType operator()(Args... args) const noexcept { + auto& fn = *static_cast*>(this); return fn.call_(fn.data_, static_cast(args)...); } - struct SharedProxy { - std::shared_ptr> sp_; + class SharedProxy { + std::shared_ptr> sp_; public: - explicit SharedProxy(Function&& func) - : sp_(std::make_shared>( - std::move(func))) {} + explicit SharedProxy(Function&& func) + : sp_(std::make_shared>(std::move(func))) {} ReturnType operator()(Args&&... args) const { return (*sp_)(static_cast(args)...); } }; }; +#endif template bool execSmall(Op o, Data* src, Data* dst) { @@ -399,25 +516,9 @@ bool execBig(Op o, Data* src, Data* dst) { return true; } -// Invoke helper -template -inline auto invoke(F&& f, Args&&... args) - -> decltype(std::forward(f)(std::forward(args)...)) { - return std::forward(f)(std::forward(args)...); -} - -template -inline auto invoke(M(C::*d), Args&&... args) - -> decltype(std::mem_fn(d)(std::forward(args)...)) { - return std::mem_fn(d)(std::forward(args)...); -} - } // namespace function } // namespace detail -FOLLY_PUSH_WARNING -FOLLY_MSVC_DISABLE_WARNING(4521) // Multiple copy constructors -FOLLY_MSVC_DISABLE_WARNING(4522) // Multiple assignment operators template class Function final : private detail::function::FunctionTraits { // These utility types are defined outside of the template to reduce @@ -436,20 +537,18 @@ class Function final : private detail::function::FunctionTraits { template using IsSmall = detail::function::IsSmall; - using OtherSignature = typename Traits::OtherSignature; - // The `data_` member is mutable to allow `constCastFunction` to work without // invoking undefined behavior. Const-correctness is only violated when // `FunctionType` is a const function type (e.g., `int() const`) and `*this` // is the result of calling `constCastFunction`. - mutable Data data_; + mutable Data data_{}; Call call_{&Traits::uninitCall}; Exec exec_{&detail::function::uninitNoop}; friend Traits; friend Function folly::constCastFunction<>( Function&&) noexcept; - friend class Function; + friend class Function; template Function(Fun&& fun, SmallTag) noexcept { @@ -469,7 +568,13 @@ class Function final : private detail::function::FunctionTraits { exec_ = &detail::function::execBig; } - Function(Function&& that, CoerceTag) noexcept { + template + Function(Function&& that, CoerceTag) + : Function(static_cast&&>(that), HeapTag{}) {} + + Function( + Function&& that, + CoerceTag) noexcept { that.exec_(Op::MOVE, &that.data_, &data_); std::swap(call_, that.call_); std::swap(exec_, that.exec_); @@ -482,13 +587,15 @@ class Function final : private detail::function::FunctionTraits { Function() = default; // not copyable - // NOTE: Deleting the non-const copy constructor is unusual but necessary to - // prevent copies from non-const `Function` object from selecting the - // perfect forwarding implicit converting constructor below - // (i.e., `template Function(Fun&&)`). - Function(Function&) = delete; Function(const Function&) = delete; - Function(const Function&&) = delete; + +#if __OBJC__ + // Make sure Objective C blocks are copied + template + /*implicit*/ Function(ReturnType (^objCBlock)(Args... args)) + : Function([blockCopy = (ReturnType (^)(Args...))[objCBlock copy]]( + Args... args) { return blockCopy(args...); }){}; +#endif /** * Move constructor @@ -505,32 +612,47 @@ class Function final : private detail::function::FunctionTraits { /* implicit */ Function(std::nullptr_t) noexcept {} /** - * 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. + * Constructs a new `Function` from any callable object that is _not_ a + * `folly::Function`. This handles function pointers, pointers to static + * member functions, `std::reference_wrapper` objects, `std::function` + * objects, and arbitrary objects that implement `operator()` if the parameter + * signature matches (i.e. it returns an object convertible to `R` when called + * with `Args...`). * - * \note `typename = ResultOf` prevents this overload from being - * selected by overload resolution when `fun` is not a compatible function. + * \note `typename Traits::template ResultOf` prevents this overload + * from being selected by overload resolution when `fun` is not a compatible + * function. + * + * \note The noexcept requires some explanation. `IsSmall` is true when the + * decayed type fits within the internal buffer and is noexcept-movable. But + * this ctor might copy, not move. What we need here, if this ctor does a + * copy, is that this ctor be noexcept when the copy is noexcept. That is not + * checked in `IsSmall`, and shouldn't be, because once the `Function` is + * constructed, the contained object is never copied. This check is for this + * ctor only, in the case that this ctor does a copy. */ - template > - /* implicit */ Function(Fun&& fun) noexcept(IsSmall::value) - : Function(static_cast(fun), IsSmall{}) {} + template < + typename Fun, + typename = detail::function::EnableIfNotFunction, + typename = typename Traits::template ResultOf> + /* implicit */ Function(Fun fun) noexcept( + IsSmall::value && noexcept(Fun(std::declval()))) + : Function(std::move(fun), IsSmall{}) {} /** - * For moving a `Function` into a `Function`. + * For move-constructing from a `folly::Function`. + * For a `Function` with a `const` function type, the object must be + * callable from a `const`-reference, i.e. implement `operator() const`. + * For a `Function` with a non-`const` function type, the object will + * be called from a non-const reference, which means that it will execute + * a non-const `operator()` if it is defined, and falls back to + * `operator() const` otherwise. */ template < - bool Const = Traits::IsConst::value, - typename std::enable_if::type = 0> - Function(Function&& that) noexcept + typename Signature, + typename = typename Traits::template ResultOf>> + Function(Function&& that) noexcept( + noexcept(Function(std::move(that), CoerceTag{}))) : Function(std::move(that), CoerceTag{}) {} /** @@ -553,22 +675,37 @@ class Function final : private detail::function::FunctionTraits { exec_(Op::NUKE, &data_, nullptr); } - Function& operator=(Function&) = delete; Function& operator=(const Function&) = delete; +#if __OBJC__ + // Make sure Objective C blocks are copied + template + /* implicit */ Function &operator=(ReturnType (^objCBlock)(Args... args)) { + (*this) = [blockCopy = (ReturnType (^)(Args...))[objCBlock copy]]( + Args... args) { return blockCopy(args...); }; + return *this; + } +#endif + /** * Move assignment operator + * + * \note Leaves `that` in a valid but unspecified state. If `&that == this` + * then `*this` is left in a valid but unspecified state. */ 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)); - } + // Q: Why is it safe to destroy and reconstruct this object in place? + // A: Two reasons: First, `Function` is a final class, so in doing this + // we aren't slicing off any derived parts. And second, the move + // operation is guaranteed not to throw so we always leave the object + // in a valid state. + // In the case of self-move (this == &that), this leaves the object in + // a default-constructed state. First the object is destroyed, then we + // pass the destroyed object to the move constructor. The first thing the + // move constructor does is default-construct the object. That object is + // "moved" into itself, which is a no-op for a default-constructed Function. + this->~Function(); + ::new (this) Function(std::move(that)); return *this; } @@ -576,25 +713,37 @@ class Function final : private detail::function::FunctionTraits { * Assigns a callable object to this `Function`. If the operation fails, * `*this` is left unmodified. * - * \note `typename = ResultOf` prevents this overload from being - * selected by overload resolution when `fun` is not a compatible function. + * \note `typename = decltype(Function(std::declval()))` prevents this + * overload from being selected by overload resolution when `fun` is not a + * compatible function. */ - template > - Function& operator=(Fun&& fun) noexcept( + template ()))> + Function& operator=(Fun fun) noexcept( noexcept(/* implicit */ Function(std::declval()))) { // Doing this in place is more efficient when we can do so safely. if (noexcept(/* implicit */ Function(std::declval()))) { // Q: Why is is safe to destroy and reconstruct this object in place? // A: See the explanation in the move assignment operator. this->~Function(); - ::new (this) Function(static_cast(fun)); + ::new (this) Function(std::move(fun)); } else { // Construct a temporary and (nothrow) swap. - Function(static_cast(fun)).swap(*this); + Function(std::move(fun)).swap(*this); } return *this; } + /** + * For assigning from a `Function`. + */ + template < + typename Signature, + typename = typename Traits::template ResultOf>> + Function& operator=(Function&& that) noexcept( + noexcept(Function(std::move(that)))) { + return (*this = Function(std::move(that))); + } + /** * Clears this `Function`. */ @@ -663,7 +812,6 @@ class Function final : private detail::function::FunctionTraits { return std::move(*this).asSharedProxy(); } }; -FOLLY_POP_WARNING template void swap(Function& lhs, Function& rhs) noexcept { @@ -707,6 +855,40 @@ Function constCastFunction( return std::move(that); } +#if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE +template +Function constCastFunction( + Function&& that) noexcept { + return Function{ + std::move(that), detail::function::CoerceTag{}}; +} + +template +Function constCastFunction( + Function&& that) noexcept { + return std::move(that); +} +#endif + +namespace detail { +namespace function { +template +struct IsCallableAsImpl : std::false_type {}; + +template +struct IsCallableAsImpl< + Fun, + ReturnType(Args...), + void_t::type>> + : std::is_convertible< + typename std::result_of::type, + ReturnType> {}; + +template +struct IsCallableAs : IsCallableAsImpl {}; +} // namespace function +} // namespace detail + /** * @class FunctionRef * @@ -734,19 +916,21 @@ template class FunctionRef final { using Call = ReturnType (*)(void*, Args&&...); - void* object_{nullptr}; - Call call_{&FunctionRef::uninitCall}; - static ReturnType uninitCall(void*, Args&&...) { throw std::bad_function_call(); } template static ReturnType call(void* object, Args&&... args) { - return static_cast(detail::function::invoke( - *static_cast(object), static_cast(args)...)); + using Pointer = _t>; + return static_cast(invoke( + static_cast(*static_cast(object)), + static_cast(args)...)); } + void* object_{nullptr}; + Call call_{&FunctionRef::uninitCall}; + public: /** * Default constructor. Constructs an empty FunctionRef. @@ -758,28 +942,29 @@ class FunctionRef final { /** * Construct a FunctionRef from a reference to a callable object. */ - template - /* implicit */ FunctionRef(Fun&& fun) noexcept { - using ReferencedType = typename std::remove_reference::type; - - static_assert( - std::is_convertible< - typename std::result_of::type, - ReturnType>::value, - "FunctionRef cannot be constructed from object with " - "incompatible function signature"); - - // `Fun` may be a const type, in which case we have to do a const_cast - // to store the address in a `void*`. This is safe because the `void*` - // will be cast back to `Fun*` (which is a const pointer whenever `Fun` - // is a const type) inside `FunctionRef::call` - object_ = const_cast(static_cast(std::addressof(fun))); - call_ = &FunctionRef::call; - } + template < + typename Fun, + typename std::enable_if< + Conjunction< + Negation>>>, + detail::function::IsCallableAs>::value, + int>::type = 0> + constexpr /* implicit */ FunctionRef(Fun&& fun) noexcept + // `Fun` may be a const type, in which case we have to do a const_cast + // to store the address in a `void*`. This is safe because the `void*` + // will be cast back to `Fun*` (which is a const pointer whenever `Fun` + // is a const type) inside `FunctionRef::call` + : object_( + const_cast(static_cast(std::addressof(fun)))), + call_(&FunctionRef::call) {} ReturnType operator()(Args... args) const { return call_(object_, static_cast(args)...); } + + constexpr explicit operator bool() const { + return object_; + } }; } // namespace folly