Backed out changeset 09c1712854b3
[folly.git] / folly / Function.h
index c5f4a4834aad636197599c077a414880e939f53e..a201c95c2cd44baddaba604bcce390d1efab60cc 100644 (file)
 #include <utility>
 
 #include <folly/CppAttributes.h>
+#include <folly/Portability.h>
 
 namespace folly {
 
-namespace impl {
-template <typename FunctionType, bool Const = false>
+template <typename FunctionType>
 class Function;
 
 template <typename ReturnType, typename... Args>
-Function<ReturnType(Args...), true> constCastFunction(
-    Function<ReturnType(Args...), false>&&) noexcept;
-}
+Function<ReturnType(Args...) const> constCastFunction(
+    Function<ReturnType(Args...)>&&) noexcept;
 
 namespace detail {
 namespace function {
@@ -243,28 +242,19 @@ enum class Op { MOVE, NUKE, FULL, HEAP };
 
 union Data {
   void* big;
-  typename std::aligned_storage<6 * sizeof(void*)>::type small;
+  std::aligned_storage<6 * sizeof(void*)>::type tiny;
 };
 
-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
+    (sizeof(FunT) <= sizeof(Data::tiny) &&
      // Same as is_nothrow_move_constructible, but w/ no template instantiation.
-     noexcept(FunT(std::declval<FunT&&>()))
-#endif
-     )>;
+     noexcept(FunT(std::declval<FunT&&>())))>;
+using SmallTag = std::true_type;
+using HeapTag = std::false_type;
+
+struct CoerceTag {};
 
 template <typename T>
 bool isNullPtrFn(T* p) {
@@ -275,113 +265,211 @@ std::false_type isNullPtrFn(T&&) {
   return {};
 }
 
-template <typename ReturnType, typename... Args>
-ReturnType uninitCall(Data&, Args&&...) {
-  throw std::bad_function_call();
-}
 inline bool uninitNoop(Op, Data*, Data*) {
   return false;
 }
+
+template <typename FunctionType>
+struct FunctionTraits;
+
+template <typename ReturnType, typename... Args>
+struct FunctionTraits<ReturnType(Args...)> {
+  using Call = ReturnType (*)(Data&, Args&&...);
+  using IsConst = std::false_type;
+  using ConstSignature = ReturnType(Args...) const;
+  using NonConstSignature = ReturnType(Args...);
+  using OtherSignature = ConstSignature;
+
+  template <typename F, typename G = typename std::decay<F>::type>
+  using ResultOf = decltype(
+      static_cast<ReturnType>(std::declval<G&>()(std::declval<Args>()...)));
+
+  template <typename Fun>
+  static ReturnType callSmall(Data& p, Args&&... args) {
+    return static_cast<ReturnType>((*static_cast<Fun*>(
+        static_cast<void*>(&p.tiny)))(static_cast<Args&&>(args)...));
+  }
+
+  template <typename Fun>
+  static ReturnType callBig(Data& p, Args&&... args) {
+    return static_cast<ReturnType>(
+        (*static_cast<Fun*>(p.big))(static_cast<Args&&>(args)...));
+  }
+
+  static ReturnType uninitCall(Data&, Args&&...) {
+    throw std::bad_function_call();
+  }
+
+  ReturnType operator()(Args... args) {
+    auto& fn = *static_cast<Function<ReturnType(Args...)>*>(this);
+    return fn.call_(fn.data_, static_cast<Args&&>(args)...);
+  }
+
+  struct SharedFunctionImpl {
+    std::shared_ptr<Function<ReturnType(Args...)>> sp_;
+    ReturnType operator()(Args&&... args) const {
+      return (*sp_)(static_cast<Args&&>(args)...);
+    }
+  };
+};
+
+template <typename ReturnType, typename... Args>
+struct FunctionTraits<ReturnType(Args...) const> {
+  using Call = ReturnType (*)(Data&, Args&&...);
+  using IsConst = std::true_type;
+  using ConstSignature = ReturnType(Args...) const;
+  using NonConstSignature = ReturnType(Args...);
+  using OtherSignature = NonConstSignature;
+
+  template <typename F, typename G = typename std::decay<F>::type>
+  using ResultOf = decltype(static_cast<ReturnType>(
+      std::declval<const G&>()(std::declval<Args>()...)));
+
+  template <typename Fun>
+  static ReturnType callSmall(Data& p, Args&&... args) {
+    return static_cast<ReturnType>((*static_cast<const Fun*>(
+        static_cast<void*>(&p.tiny)))(static_cast<Args&&>(args)...));
+  }
+
+  template <typename Fun>
+  static ReturnType callBig(Data& p, Args&&... args) {
+    return static_cast<ReturnType>(
+        (*static_cast<const Fun*>(p.big))(static_cast<Args&&>(args)...));
+  }
+
+  static ReturnType uninitCall(Data&, Args&&...) {
+    throw std::bad_function_call();
+  }
+
+  ReturnType operator()(Args... args) const {
+    auto& fn = *static_cast<const Function<ReturnType(Args...) const>*>(this);
+    return fn.call_(fn.data_, static_cast<Args&&>(args)...);
+  }
+
+  struct SharedFunctionImpl {
+    std::shared_ptr<Function<ReturnType(Args...) const>> sp_;
+    ReturnType operator()(Args&&... args) const {
+      return (*sp_)(static_cast<Args&&>(args)...);
+    }
+  };
+};
+
+template <typename Fun>
+bool execSmall(Op o, Data* src, Data* dst) {
+  switch (o) {
+    case Op::MOVE:
+      ::new (static_cast<void*>(&dst->tiny))
+          Fun(std::move(*static_cast<Fun*>(static_cast<void*>(&src->tiny))));
+      FOLLY_FALLTHROUGH;
+    case Op::NUKE:
+      static_cast<Fun*>(static_cast<void*>(&src->tiny))->~Fun();
+      break;
+    case Op::FULL:
+      return true;
+    case Op::HEAP:
+      break;
+  }
+  return false;
+}
+
+template <typename Fun>
+bool execBig(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<Fun*>(src->big);
+      break;
+    case Op::FULL:
+    case Op::HEAP:
+      break;
+  }
+  return true;
+}
+
+// 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)...);
+}
+
 } // namespace function
 } // namespace detail
 
-namespace impl {
-
-template <typename ReturnType, typename... Args, bool Const>
-class Function<ReturnType(Args...), Const> final {
+FOLLY_PUSH_WARNING
+FOLLY_MSVC_DISABLE_WARNING(4521) // Multiple copy constructors
+FOLLY_MSVC_DISABLE_WARNING(4522) // Multiple assignment operators
+template <typename FunctionType>
+class Function final : private detail::function::FunctionTraits<FunctionType> {
+  // These utility types are defined outside of the template to reduce
+  // the number of instantiations, and then imported in the class
+  // namespace for convenience.
   using Data = detail::function::Data;
   using Op = detail::function::Op;
-  using Tag = detail::function::Tag;
-  using Call = ReturnType (*)(Data&, Args&&...);
+  using SmallTag = detail::function::SmallTag;
+  using HeapTag = detail::function::HeapTag;
+  using CoerceTag = detail::function::CoerceTag;
+
+  using Traits = detail::function::FunctionTraits<FunctionType>;
+  using Call = typename Traits::Call;
   using Exec = bool (*)(Op, Data*, Data*);
 
-  template <typename T>
-  using ConstIf = detail::function::ConstIf<Const, T>;
   template <typename Fun>
   using IsSmall = detail::function::IsSmall<Fun>;
 
-  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;
-      }
-    };
+  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_;
+  Call call_{&Traits::uninitCall};
+  Exec exec_{&detail::function::uninitNoop};
+
+  friend Traits;
+  friend Function<typename Traits::ConstSignature> folly::constCastFunction<>(
+      Function<typename Traits::NonConstSignature>&&) noexcept;
+  friend class Function<OtherSignature>;
+
+  template <typename Fun>
+  Function(Fun&& fun, SmallTag) noexcept {
+    using FunT = typename std::decay<Fun>::type;
     if (!detail::function::isNullPtrFn(fun)) {
-      ::new (&data_.small) FunT(static_cast<Fun&&>(fun));
-      exec_ = &Ops::exec;
-      call_ = &Ops::call;
+      ::new (static_cast<void*>(&data_.tiny)) FunT(static_cast<Fun&&>(fun));
+      call_ = &Traits::template callSmall<FunT>;
+      exec_ = &detail::function::execSmall<FunT>;
     }
   }
 
-  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;
-      }
-    };
+  template <typename Fun>
+  Function(Fun&& fun, HeapTag) {
+    using FunT = typename std::decay<Fun>::type;
     data_.big = new FunT(static_cast<Fun&&>(fun));
-    call_ = &Ops::call;
-    exec_ = &Ops::exec;
+    call_ = &Traits::template callBig<FunT>;
+    exec_ = &detail::function::execBig<FunT>;
+  }
+
+  Function(Function<OtherSignature>&& that, CoerceTag) noexcept {
+    that.exec_(Op::MOVE, &that.data_, &data_);
+    std::swap(call_, that.call_);
+    std::swap(exec_, that.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:
   /**
    * Default constructor. Constructs an empty Function.
    */
-  Function() noexcept
-      : call_(&detail::function::uninitCall<ReturnType, Args...>),
-        exec_(&detail::function::uninitNoop) {}
+  Function() = default;
 
   // not copyable
   // NOTE: Deleting the non-const copy constructor is unusual but necessary to
@@ -390,11 +478,12 @@ class Function<ReturnType(Args...), Const> final {
   // (i.e., `template <typename Fun> Function(Fun&&)`).
   Function(Function&) = delete;
   Function(const Function&) = delete;
+  Function(const Function&&) = delete;
 
   /**
    * Move constructor
    */
-  Function(Function&& that) noexcept : Function() {
+  Function(Function&& that) noexcept {
     that.exec_(Op::MOVE, &that.data_, &data_);
     std::swap(call_, that.call_);
     std::swap(exec_, that.exec_);
@@ -403,7 +492,7 @@ class Function<ReturnType(Args...), Const> final {
   /**
    * Constructs an empty `Function`.
    */
-  /* implicit */ Function(std::nullptr_t) noexcept : Function() {}
+  /* implicit */ Function(std::nullptr_t) noexcept {}
 
   /**
    * Constructs a new `Function` from any callable object. This
@@ -421,23 +510,18 @@ class Function<ReturnType(Args...), Const> final {
    * \note `typename = ResultOf<Fun>` prevents this overload from being
    * selected by overload resolution when `fun` is not a compatible function.
    */
-  template <class Fun, typename = ResultOf<Fun>>
-  /* implicit */ Function(Fun&& fun) noexcept(
-      noexcept(Function(std::declval<Fun>(), Tag{})))
-      : Function(static_cast<Fun&&>(fun), Tag{}) {}
+  template <class Fun, typename = typename Traits::template ResultOf<Fun>>
+  /* implicit */ Function(Fun&& fun) noexcept(IsSmall<Fun>::value)
+      : Function(static_cast<Fun&&>(fun), IsSmall<Fun>{}) {}
 
   /**
    * For moving a `Function<X(Ys..) const>` into a `Function<X(Ys...)>`.
    */
   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_);
-  }
+      bool Const = Traits::IsConst::value,
+      typename std::enable_if<!Const, int>::type = 0>
+  Function(Function<OtherSignature>&& that) noexcept
+      : Function(std::move(that), CoerceTag{}) {}
 
   /**
    * If `ptr` is null, constructs an empty `Function`. Otherwise,
@@ -449,7 +533,7 @@ class Function<ReturnType(Args...), Const> final {
       // 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() {
+  /* implicit */ Function(Member Class::*ptr) noexcept {
     if (ptr) {
       *this = std::mem_fn(ptr);
     }
@@ -485,7 +569,7 @@ class Function<ReturnType(Args...), Const> final {
    * \note `typename = ResultOf<Fun>` prevents this overload from being
    * selected by overload resolution when `fun` is not a compatible function.
    */
-  template <class Fun, typename = ResultOf<Fun>>
+  template <class Fun, typename = typename Traits::template 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.
@@ -522,31 +606,8 @@ class Function<ReturnType(Args...), Const> final {
 
   /**
    * 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 <
-      // `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)...);
-  }
-
-  /**
-   * 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.
-   */
-  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)...);
-  }
+  using Traits::operator();
 
   /**
    * Exchanges the callable objects of `*this` and `that`.
@@ -578,76 +639,128 @@ class Function<ReturnType(Args...), Const> final {
    * 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)...);
-      }
-    };
+  std::function<typename Traits::NonConstSignature> asStdFunction() && {
+    using Impl = typename Traits::SharedFunctionImpl;
     return Impl{std::make_shared<Function>(std::move(*this))};
   }
 };
+FOLLY_POP_WARNING
 
-template <typename FunctionType, bool Const>
-void swap(
-    Function<FunctionType, Const>& lhs,
-    Function<FunctionType, Const>& rhs) noexcept {
+template <typename FunctionType>
+void swap(Function<FunctionType>& lhs, Function<FunctionType>& rhs) noexcept {
   lhs.swap(rhs);
 }
 
-template <typename FunctionType, bool Const>
-bool operator==(const Function<FunctionType, Const>& fn, std::nullptr_t) {
+template <typename FunctionType>
+bool operator==(const Function<FunctionType>& fn, std::nullptr_t) {
   return !fn;
 }
 
-template <typename FunctionType, bool Const>
-bool operator==(std::nullptr_t, const Function<FunctionType, Const>& fn) {
+template <typename FunctionType>
+bool operator==(std::nullptr_t, const Function<FunctionType>& fn) {
   return !fn;
 }
 
-template <typename FunctionType, bool Const>
-bool operator!=(const Function<FunctionType, Const>& fn, std::nullptr_t) {
+template <typename FunctionType>
+bool operator!=(const Function<FunctionType>& fn, std::nullptr_t) {
   return !(fn == nullptr);
 }
 
-template <typename FunctionType, bool Const>
-bool operator!=(std::nullptr_t, const Function<FunctionType, Const>& fn) {
+template <typename FunctionType>
+bool operator!=(std::nullptr_t, const Function<FunctionType>& fn) {
   return !(nullptr == fn);
 }
 
+/**
+ * NOTE: See detailed note about `constCastFunction` at the top of the file.
+ * This is potentially dangerous and requires the equivalent of a `const_cast`.
+ */
 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;
+Function<ReturnType(Args...) const> constCastFunction(
+    Function<ReturnType(Args...)>&& that) noexcept {
+  return Function<ReturnType(Args...) const>{std::move(that),
+                                             detail::function::CoerceTag{}};
 }
 
-template <typename FunctionType>
-Function<FunctionType, true> constCastFunction(
-    Function<FunctionType, true>&& that) noexcept {
+template <typename ReturnType, typename... Args>
+Function<ReturnType(Args...) const> constCastFunction(
+    Function<ReturnType(Args...) const>&& that) noexcept {
   return std::move(that);
 }
 
+/**
+ * @class FunctionRef
+ *
+ * @brief A reference wrapper for callable objects
+ *
+ * FunctionRef is similar to std::reference_wrapper, but the template parameter
+ * is the function signature type rather than the type of the referenced object.
+ * A folly::FunctionRef is cheap to construct as it contains only a pointer to
+ * the referenced callable and a pointer to a function which invokes the
+ * callable.
+ *
+ * The user of FunctionRef must be aware of the reference semantics: storing a
+ * copy of a FunctionRef is potentially dangerous and should be avoided unless
+ * the referenced object definitely outlives the FunctionRef object. Thus any
+ * function that accepts a FunctionRef parameter should only use it to invoke
+ * the referenced function and not store a copy of it. Knowing that FunctionRef
+ * itself has reference semantics, it is generally okay to use it to reference
+ * lambdas that capture by reference.
+ */
+
 template <typename FunctionType>
-struct MakeFunction {};
+class FunctionRef;
 
 template <typename ReturnType, typename... Args>
-struct MakeFunction<ReturnType(Args...)> {
-  using type = Function<ReturnType(Args...), false>;
-};
+class FunctionRef<ReturnType(Args...)> final {
+  using Call = ReturnType (*)(void*, Args&&...);
 
-template <typename ReturnType, typename... Args>
-struct MakeFunction<ReturnType(Args...) const> {
-  using type = Function<ReturnType(Args...), true>;
-};
-} // namespace impl
+  void* object_{nullptr};
+  Call call_{&FunctionRef::uninitCall};
 
-/* using override */ using impl::constCastFunction;
+  static ReturnType uninitCall(void*, Args&&...) {
+    throw std::bad_function_call();
+  }
 
-template <typename FunctionType>
-using Function = typename impl::MakeFunction<FunctionType>::type;
-}
+  template <typename Fun>
+  static ReturnType call(void* object, Args&&... args) {
+    return static_cast<ReturnType>(detail::function::invoke(
+        *static_cast<Fun*>(object), static_cast<Args&&>(args)...));
+  }
+
+ public:
+  /**
+   * Default constructor. Constructs an empty FunctionRef.
+   *
+   * Invoking it will throw std::bad_function_call.
+   */
+  FunctionRef() = default;
+
+  /**
+   * Construct a FunctionRef from a reference to a callable object.
+   */
+  template <typename Fun>
+  /* 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>;
+  }
+
+  ReturnType operator()(Args... args) const {
+    return call_(object_, static_cast<Args&&>(args)...);
+  }
+};
+
+} // namespace folly