make FunctionRef constexpr on compilers that support it
authorEric Niebler <eniebler@fb.com>
Fri, 7 Jul 2017 23:31:31 +0000 (16:31 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Fri, 7 Jul 2017 23:35:42 +0000 (16:35 -0700)
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

index 263b0af07d8ac940b186038dd9bbd5c9e4b436df..b91de9ca16e4679b705e64f8259de9755a27a423 100644 (file)
@@ -410,13 +410,13 @@ bool execBig(Op o, Data* src, Data* dst) {
 
 // Invoke helper
 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)...)) {
   return std::forward<F>(f)(std::forward<Args>(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)...)) {
   return std::mem_fn(d)(std::forward<Args>(args)...);
 }
@@ -740,6 +740,25 @@ Function<ReturnType(Args...) const> constCastFunction(
   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
  *
@@ -767,19 +786,21 @@ template <typename ReturnType, typename... Args>
 class FunctionRef<ReturnType(Args...)> final {
   using Call = ReturnType (*)(void*, Args&&...);
 
-  void* object_{nullptr};
-  Call call_{&FunctionRef::uninitCall};
-
   static ReturnType uninitCall(void*, Args&&...) {
     throw std::bad_function_call();
   }
 
   template <typename Fun>
   static ReturnType call(void* object, Args&&... args) {
+    using Pointer = _t<std::add_pointer<Fun>>;
     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:
   /**
    * Default constructor. Constructs an empty FunctionRef.
@@ -794,31 +815,24 @@ class FunctionRef<ReturnType(Args...)> final {
   template <
       typename Fun,
       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>
-  /* implicit */ FunctionRef(Fun&& fun) noexcept {
-    using ReferencedType = typename std::remove_reference<Fun>::type;
-
-    static_assert(
-        std::is_convertible<
-            typename std::result_of<ReferencedType&(Args && ...)>::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<void*>(static_cast<void const*>(std::addressof(fun)));
-    call_ = &FunctionRef::call<ReferencedType>;
-  }
+  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<void*>(static_cast<void const*>(std::addressof(fun)))),
+        call_(&FunctionRef::call<Fun>) {}
 
   ReturnType operator()(Args... args) const {
     return call_(object_, static_cast<Args&&>(args)...);
   }
 
-  explicit operator bool() const {
+  constexpr explicit operator bool() const {
     return object_;
   }
 };