From 81c9d2a6bd4e062b2fa68190175e3b20f566d2cb Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Fri, 7 Jul 2017 16:31:31 -0700 Subject: [PATCH] make FunctionRef constexpr on compilers that support it Summary: `constexpr` All The Things! Although only very recent versions of modern compilers support this, FunctionRef can and should be `constexpr` (demonstration here: https://godbolt.org/g/NJ72RG). This change also changes the `FunctionRef` constructor such that it no longer participates in overload resolution if the argument is not callable with the specified arguments. In addition, it perfectly forwards the callable object such that if it was an rvalue when it was passed to the constructor, it will be invoked as an rvalue in `operator()`. Reviewed By: yfeldblum Differential Revision: D5366720 fbshipit-source-id: bc64053213478aab5a5bd5950c7b2d6f364d86bd --- folly/Function.h | 64 +++++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/folly/Function.h b/folly/Function.h index 263b0af0..b91de9ca 100644 --- a/folly/Function.h +++ b/folly/Function.h @@ -410,13 +410,13 @@ bool execBig(Op o, Data* src, Data* dst) { // Invoke helper template -inline auto invoke(F&& f, Args&&... args) +inline constexpr 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) +inline constexpr auto invoke(M(C::*d), Args&&... args) -> decltype(std::mem_fn(d)(std::forward(args)...)) { return std::mem_fn(d)(std::forward(args)...); } @@ -740,6 +740,25 @@ Function constCastFunction( return std::move(that); } +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 {}; +} +} + /** * @class FunctionRef * @@ -767,19 +786,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) { + using Pointer = _t>; return static_cast(detail::function::invoke( - *static_cast(object), static_cast(args)...)); + static_cast(*static_cast(object)), + static_cast(args)...)); } + void* object_{nullptr}; + Call call_{&FunctionRef::uninitCall}; + public: /** * Default constructor. Constructs an empty FunctionRef. @@ -794,31 +815,24 @@ class FunctionRef final { template < typename Fun, typename std::enable_if< - !std::is_same::type>::value, + Conjunction< + Negation>>>, + detail::function::IsCallableAs>::value, int>::type = 0> - /* 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; - } + 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)...); } - explicit operator bool() const { + constexpr explicit operator bool() const { return object_; } }; -- 2.34.1