Commit 81c9d2a6 authored by Eric Niebler's avatar Eric Niebler Committed by Facebook Github Bot

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
parent 7f15a179
...@@ -410,13 +410,13 @@ bool execBig(Op o, Data* src, Data* dst) { ...@@ -410,13 +410,13 @@ bool execBig(Op o, Data* src, Data* dst) {
// Invoke helper // Invoke helper
template <typename F, typename... Args> template <typename F, typename... Args>
inline auto invoke(F&& f, Args&&... args) inline constexpr auto invoke(F&& f, Args&&... args)
-> decltype(std::forward<F>(f)(std::forward<Args>(args)...)) { -> decltype(std::forward<F>(f)(std::forward<Args>(args)...)) {
return std::forward<F>(f)(std::forward<Args>(args)...); return std::forward<F>(f)(std::forward<Args>(args)...);
} }
template <typename M, typename C, typename... Args> template <typename M, typename C, typename... Args>
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>(args)...)) { -> decltype(std::mem_fn(d)(std::forward<Args>(args)...)) {
return std::mem_fn(d)(std::forward<Args>(args)...); return std::mem_fn(d)(std::forward<Args>(args)...);
} }
...@@ -740,6 +740,25 @@ Function<ReturnType(Args...) const> constCastFunction( ...@@ -740,6 +740,25 @@ Function<ReturnType(Args...) const> constCastFunction(
return std::move(that); return std::move(that);
} }
namespace detail {
namespace function {
template <typename Fun, typename FunctionType, typename = void>
struct IsCallableAsImpl : std::false_type {};
template <typename Fun, typename ReturnType, typename... Args>
struct IsCallableAsImpl<
Fun,
ReturnType(Args...),
void_t<typename std::result_of<Fun && (Args && ...)>::type>>
: std::is_convertible<
typename std::result_of<Fun && (Args && ...)>::type,
ReturnType> {};
template <typename Fun, typename FunctionType>
struct IsCallableAs : IsCallableAsImpl<Fun, FunctionType> {};
}
}
/** /**
* @class FunctionRef * @class FunctionRef
* *
...@@ -767,19 +786,21 @@ template <typename ReturnType, typename... Args> ...@@ -767,19 +786,21 @@ template <typename ReturnType, typename... Args>
class FunctionRef<ReturnType(Args...)> final { class FunctionRef<ReturnType(Args...)> final {
using Call = ReturnType (*)(void*, Args&&...); using Call = ReturnType (*)(void*, Args&&...);
void* object_{nullptr};
Call call_{&FunctionRef::uninitCall};
static ReturnType uninitCall(void*, Args&&...) { static ReturnType uninitCall(void*, Args&&...) {
throw std::bad_function_call(); throw std::bad_function_call();
} }
template <typename Fun> template <typename Fun>
static ReturnType call(void* object, Args&&... args) { static ReturnType call(void* object, Args&&... args) {
using Pointer = _t<std::add_pointer<Fun>>;
return static_cast<ReturnType>(detail::function::invoke( return static_cast<ReturnType>(detail::function::invoke(
*static_cast<Fun*>(object), static_cast<Args&&>(args)...)); static_cast<Fun&&>(*static_cast<Pointer>(object)),
static_cast<Args&&>(args)...));
} }
void* object_{nullptr};
Call call_{&FunctionRef::uninitCall};
public: public:
/** /**
* Default constructor. Constructs an empty FunctionRef. * Default constructor. Constructs an empty FunctionRef.
...@@ -794,31 +815,24 @@ class FunctionRef<ReturnType(Args...)> final { ...@@ -794,31 +815,24 @@ class FunctionRef<ReturnType(Args...)> final {
template < template <
typename Fun, typename Fun,
typename std::enable_if< typename std::enable_if<
!std::is_same<FunctionRef, typename std::decay<Fun>::type>::value, Conjunction<
Negation<std::is_same<FunctionRef, _t<std::decay<Fun>>>>,
detail::function::IsCallableAs<Fun, ReturnType(Args...)>>::value,
int>::type = 0> int>::type = 0>
/* implicit */ FunctionRef(Fun&& fun) noexcept { constexpr /* implicit */ FunctionRef(Fun&& fun) noexcept
using ReferencedType = typename std::remove_reference<Fun>::type; // `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*`
static_assert( // will be cast back to `Fun*` (which is a const pointer whenever `Fun`
std::is_convertible< // is a const type) inside `FunctionRef::call`
typename std::result_of<ReferencedType&(Args && ...)>::type, : object_(
ReturnType>::value, const_cast<void*>(static_cast<void const*>(std::addressof(fun)))),
"FunctionRef cannot be constructed from object with " call_(&FunctionRef::call<Fun>) {}
"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<void*>(static_cast<void const*>(std::addressof(fun)));
call_ = &FunctionRef::call<ReferencedType>;
}
ReturnType operator()(Args... args) const { ReturnType operator()(Args... args) const {
return call_(object_, static_cast<Args&&>(args)...); return call_(object_, static_cast<Args&&>(args)...);
} }
explicit operator bool() const { constexpr explicit operator bool() const {
return object_; return object_;
} }
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment