Draft prototype of hazard pointers C++ template library
[folly.git] / folly / Function.h
index dc04efe4d1dd8b22a971cf40f7ff8b839677276c..91a8c6a15a6aca74728185d777bb65a491a7f779 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,22 +242,21 @@ 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;
 };
 
-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) &&
+    (sizeof(FunT) <= sizeof(Data::tiny) &&
      // Same as is_nothrow_move_constructible, but w/ no template instantiation.
      noexcept(FunT(std::declval<FunT&&>()))
      )>;
 using SmallTag = std::true_type;
 using HeapTag = std::false_type;
 
+struct CoerceTag {};
+
 template <typename T>
 bool isNullPtrFn(T* p) {
   return p == nullptr;
@@ -268,21 +266,138 @@ 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;
+}
+
 } // 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.
@@ -290,100 +405,59 @@ class Function<ReturnType(Args...), Const> final {
   using Op = detail::function::Op;
   using SmallTag = detail::function::SmallTag;
   using HeapTag = detail::function::HeapTag;
-  using Call = ReturnType (*)(Data&, Args&&...);
+  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_;
+  using OtherSignature = typename Traits::OtherSignature;
 
-  friend Function<ReturnType(Args...), true> constCastFunction<>(
-      Function<ReturnType(Args...), false>&&) noexcept;
-  friend class Function<ReturnType(Args...), !Const>;
+  // 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};
 
-  template <typename Fun>
-  struct OpsSmall {
-    using FunT = typename std::decay<Fun>::type;
-    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;
-    }
-  };
+  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 : Function() {
-    using Ops = OpsSmall<Fun>;
+  Function(Fun&& fun, SmallTag) noexcept {
+    using FunT = typename std::decay<Fun>::type;
     if (!detail::function::isNullPtrFn(fun)) {
-      ::new (&data_.small) typename Ops::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>
-  struct OpsHeap {
+  Function(Fun&& fun, HeapTag) {
     using FunT = typename std::decay<Fun>::type;
-    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) : Function() {
-    using Ops = OpsHeap<Fun>;
-    data_.big = new typename Ops::FunT(static_cast<Fun&&>(fun));
-    call_ = &Ops::call;
-    exec_ = &Ops::exec;
+    data_.big = new FunT(static_cast<Fun&&>(fun));
+    call_ = &Traits::template callBig<FunT>;
+    exec_ = &detail::function::execBig<FunT>;
   }
 
-  template <typename F, typename G = typename std::decay<F>::type>
-  using ResultOf = decltype(static_cast<ReturnType>(
-      std::declval<ConstIf<G>&>()(std::declval<Args>()...)));
+  Function(Function<OtherSignature>&& that, CoerceTag) noexcept {
+    that.exec_(Op::MOVE, &that.data_, &data_);
+    std::swap(call_, that.call_);
+    std::swap(exec_, that.exec_);
+  }
 
  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
@@ -392,11 +466,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_);
@@ -405,7 +480,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
@@ -423,7 +498,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>>
   /* implicit */ Function(Fun&& fun) noexcept(IsSmall<Fun>::value)
       : Function(static_cast<Fun&&>(fun), IsSmall<Fun>{}) {}
 
@@ -431,14 +506,10 @@ class Function<ReturnType(Args...), Const> final {
    * 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,
@@ -450,7 +521,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);
     }
@@ -486,7 +557,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.
@@ -523,31 +594,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`.
@@ -579,76 +627,52 @@ 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 {
-  return std::move(that);
-}
-
-template <typename FunctionType>
-struct MakeFunction {};
-
-template <typename ReturnType, typename... Args>
-struct MakeFunction<ReturnType(Args...)> {
-  using type = Function<ReturnType(Args...), false>;
-};
-
 template <typename ReturnType, typename... Args>
-struct MakeFunction<ReturnType(Args...) const> {
-  using type = Function<ReturnType(Args...), true>;
-};
-} // namespace impl
-
-/* using override */ using impl::constCastFunction;
-
-template <typename FunctionType>
-using Function = typename impl::MakeFunction<FunctionType>::type;
+Function<ReturnType(Args...) const> constCastFunction(
+    Function<ReturnType(Args...) const>&& that) noexcept {
+  return std::move(that);
 }
+} // namespace folly