Flesh out Optional members swap, reset, emplace, has_value
[folly.git] / folly / Optional.h
index a57f065a5da1629b167d7d46f9866f1bcaf3aee8..39abcc1c6db900ddc5a4118c0335dec55e115b98 100644 (file)
@@ -63,6 +63,7 @@
 
 #include <folly/Launder.h>
 #include <folly/Portability.h>
+#include <folly/Traits.h>
 #include <folly/Utility.h>
 
 namespace folly {
@@ -104,7 +105,7 @@ class Optional {
       !std::is_abstract<Value>::value,
       "Optional may not be used with abstract types");
 
-  Optional() noexcept {}
+  FOLLY_CPP14_CONSTEXPR Optional() noexcept {}
 
   Optional(const Optional& src) noexcept(
       std::is_nothrow_copy_constructible<Value>::value) {
@@ -121,20 +122,20 @@ class Optional {
     }
   }
 
-  /* implicit */ Optional(const None&) noexcept {}
+  FOLLY_CPP14_CONSTEXPR /* implicit */ Optional(const None&) noexcept {}
 
-  /* implicit */ Optional(Value&& newValue) noexcept(
+  FOLLY_CPP14_CONSTEXPR /* implicit */ Optional(Value&& newValue) noexcept(
       std::is_nothrow_move_constructible<Value>::value) {
     storage_.construct(std::move(newValue));
   }
 
-  /* implicit */ Optional(const Value& newValue) noexcept(
+  FOLLY_CPP14_CONSTEXPR /* implicit */ Optional(const Value& newValue) noexcept(
       std::is_nothrow_copy_constructible<Value>::value) {
     storage_.construct(newValue);
   }
 
   template <typename... Args>
-  explicit Optional(in_place_t, Args&&... args) noexcept(
+  FOLLY_CPP14_CONSTEXPR explicit Optional(in_place_t, Args&&... args) noexcept(
       std::is_nothrow_constructible<Value, Args...>::value) {
     storage_.construct(std::forward<Args>(args)...);
   }
@@ -203,31 +204,57 @@ class Optional {
   }
 
   template <class... Args>
-  void emplace(Args&&... args) {
+  Value& emplace(Args&&... args) {
     clear();
-    storage_.construct(std::forward<Args>(args)...);
+    return storage_.construct(std::forward<Args>(args)...);
+  }
+
+  template <class U, class... Args>
+  typename std::enable_if<
+      std::is_constructible<Value, std::initializer_list<U>&, Args&&...>::value,
+      Value&>::type
+  emplace(std::initializer_list<U> ilist, Args&&... args) {
+    clear();
+    return storage_.construct(ilist, std::forward<Args>(args)...);
   }
 
-  void clear() {
+  void reset() noexcept {
     storage_.clear();
   }
 
-  const Value& value() const & {
+  void clear() noexcept {
+    reset();
+  }
+
+  void swap(Optional& that) noexcept(IsNothrowSwappable<Value>::value) {
+    if (hasValue() && that.hasValue()) {
+      using std::swap;
+      swap(value(), that.value());
+    } else if (hasValue()) {
+      that.emplace(std::move(value()));
+      reset();
+    } else if (that.hasValue()) {
+      emplace(std::move(that.value()));
+      that.reset();
+    }
+  }
+
+  FOLLY_CPP14_CONSTEXPR const Value& value() const & {
     require_value();
     return *storage_.value_pointer();
   }
 
-  Value& value() & {
+  FOLLY_CPP14_CONSTEXPR Value& value() & {
     require_value();
     return *storage_.value_pointer();
   }
 
-  Value&& value() && {
+  FOLLY_CPP14_CONSTEXPR Value&& value() && {
     require_value();
     return std::move(*storage_.value_pointer());
   }
 
-  const Value&& value() const && {
+  FOLLY_CPP14_CONSTEXPR const Value&& value() const && {
     require_value();
     return std::move(*storage_.value_pointer());
   }
@@ -240,37 +267,41 @@ class Optional {
   }
   Value* get_pointer() && = delete;
 
-  bool hasValue() const noexcept {
+  FOLLY_CPP14_CONSTEXPR bool has_value() const noexcept {
     return storage_.hasValue();
   }
 
-  explicit operator bool() const noexcept {
-    return hasValue();
+  FOLLY_CPP14_CONSTEXPR bool hasValue() const noexcept {
+    return has_value();
+  }
+
+  FOLLY_CPP14_CONSTEXPR explicit operator bool() const noexcept {
+    return has_value();
   }
 
-  const Value& operator*() const & {
+  FOLLY_CPP14_CONSTEXPR const Value& operator*() const & {
     return value();
   }
-  Value& operator*() & {
+  FOLLY_CPP14_CONSTEXPR Value& operator*() & {
     return value();
   }
-  const Value&& operator*() const && {
+  FOLLY_CPP14_CONSTEXPR const Value&& operator*() const && {
     return std::move(value());
   }
-  Value&& operator*() && {
+  FOLLY_CPP14_CONSTEXPR Value&& operator*() && {
     return std::move(value());
   }
 
-  const Value* operator->() const {
+  FOLLY_CPP14_CONSTEXPR const Value* operator->() const {
     return &value();
   }
-  Value* operator->() {
+  FOLLY_CPP14_CONSTEXPR Value* operator->() {
     return &value();
   }
 
   // Return a copy of the value if set, or a given default if not.
   template <class U>
-  Value value_or(U&& dflt) const & {
+  FOLLY_CPP14_CONSTEXPR Value value_or(U&& dflt) const & {
     if (storage_.hasValue()) {
       return *storage_.value_pointer();
     }
@@ -279,7 +310,7 @@ class Optional {
   }
 
   template <class U>
-  Value value_or(U&& dflt) && {
+  FOLLY_CPP14_CONSTEXPR Value value_or(U&& dflt) && {
     if (storage_.hasValue()) {
       return std::move(*storage_.value_pointer());
     }
@@ -350,9 +381,10 @@ class Optional {
     }
 
     template <class... Args>
-    void construct(Args&&... args) {
+    Value& construct(Args&&... args) {
       new (raw_pointer()) Value(std::forward<Args>(args)...);
       this->hasValue_ = true;
+      return *launder(reinterpret_cast<Value*>(this->value_));
     }
 
    private:
@@ -375,18 +407,12 @@ T* get_pointer(Optional<T>& opt) {
 }
 
 template <class T>
-void swap(Optional<T>& a, Optional<T>& b) {
-  if (a.hasValue() && b.hasValue()) {
-    // both full
-    using std::swap;
-    swap(a.value(), b.value());
-  } else if (a.hasValue() || b.hasValue()) {
-    std::swap(a, b); // fall back to default implementation if they're mixed.
-  }
+void swap(Optional<T>& a, Optional<T>& b) noexcept(noexcept(a.swap(b))) {
+  a.swap(b);
 }
 
 template <class T, class Opt = Optional<typename std::decay<T>::type>>
-Opt make_optional(T&& v) {
+constexpr Opt make_optional(T&& v) {
   return Opt(std::forward<T>(v));
 }
 
@@ -394,27 +420,29 @@ Opt make_optional(T&& v) {
 // Comparisons.
 
 template <class U, class V>
-bool operator==(const Optional<U>& a, const V& b) {
+constexpr bool operator==(const Optional<U>& a, const V& b) {
   return a.hasValue() && a.value() == b;
 }
 
 template <class U, class V>
-bool operator!=(const Optional<U>& a, const V& b) {
+constexpr bool operator!=(const Optional<U>& a, const V& b) {
   return !(a == b);
 }
 
 template <class U, class V>
-bool operator==(const U& a, const Optional<V>& b) {
+constexpr bool operator==(const U& a, const Optional<V>& b) {
   return b.hasValue() && b.value() == a;
 }
 
 template <class U, class V>
-bool operator!=(const U& a, const Optional<V>& b) {
+constexpr bool operator!=(const U& a, const Optional<V>& b) {
   return !(a == b);
 }
 
 template <class U, class V>
-bool operator==(const Optional<U>& a, const Optional<V>& b) {
+FOLLY_CPP14_CONSTEXPR bool operator==(
+    const Optional<U>& a,
+    const Optional<V>& b) {
   if (a.hasValue() != b.hasValue()) {
     return false;
   }
@@ -425,12 +453,14 @@ bool operator==(const Optional<U>& a, const Optional<V>& b) {
 }
 
 template <class U, class V>
-bool operator!=(const Optional<U>& a, const Optional<V>& b) {
+constexpr bool operator!=(const Optional<U>& a, const Optional<V>& b) {
   return !(a == b);
 }
 
 template <class U, class V>
-bool operator<(const Optional<U>& a, const Optional<V>& b) {
+FOLLY_CPP14_CONSTEXPR bool operator<(
+    const Optional<U>& a,
+    const Optional<V>& b) {
   if (a.hasValue() != b.hasValue()) {
     return a.hasValue() < b.hasValue();
   }
@@ -441,17 +471,17 @@ bool operator<(const Optional<U>& a, const Optional<V>& b) {
 }
 
 template <class U, class V>
-bool operator>(const Optional<U>& a, const Optional<V>& b) {
+constexpr bool operator>(const Optional<U>& a, const Optional<V>& b) {
   return b < a;
 }
 
 template <class U, class V>
-bool operator<=(const Optional<U>& a, const Optional<V>& b) {
+constexpr bool operator<=(const Optional<U>& a, const Optional<V>& b) {
   return !(b < a);
 }
 
 template <class U, class V>
-bool operator>=(const Optional<U>& a, const Optional<V>& b) {
+constexpr bool operator>=(const Optional<U>& a, const Optional<V>& b) {
   return !(a < b);
 }
 
@@ -475,43 +505,43 @@ bool operator>(const V& other, const Optional<V>&) = delete;
 
 // Comparisons with none
 template <class V>
-bool operator==(const Optional<V>& a, None) noexcept {
+constexpr bool operator==(const Optional<V>& a, None) noexcept {
   return !a.hasValue();
 }
 template <class V>
-bool operator==(None, const Optional<V>& a) noexcept {
+constexpr bool operator==(None, const Optional<V>& a) noexcept {
   return !a.hasValue();
 }
 template <class V>
-bool operator<(const Optional<V>&, None) noexcept {
+constexpr bool operator<(const Optional<V>&, None) noexcept {
   return false;
 }
 template <class V>
-bool operator<(None, const Optional<V>& a) noexcept {
+constexpr bool operator<(None, const Optional<V>& a) noexcept {
   return a.hasValue();
 }
 template <class V>
-bool operator>(const Optional<V>& a, None) noexcept {
+constexpr bool operator>(const Optional<V>& a, None) noexcept {
   return a.hasValue();
 }
 template <class V>
-bool operator>(None, const Optional<V>&) noexcept {
+constexpr bool operator>(None, const Optional<V>&) noexcept {
   return false;
 }
 template <class V>
-bool operator<=(None, const Optional<V>&) noexcept {
+constexpr bool operator<=(None, const Optional<V>&) noexcept {
   return true;
 }
 template <class V>
-bool operator<=(const Optional<V>& a, None) noexcept {
+constexpr bool operator<=(const Optional<V>& a, None) noexcept {
   return !a.hasValue();
 }
 template <class V>
-bool operator>=(const Optional<V>&, None) noexcept {
+constexpr bool operator>=(const Optional<V>&, None) noexcept {
   return true;
 }
 template <class V>
-bool operator>=(None, const Optional<V>& a) noexcept {
+constexpr bool operator>=(None, const Optional<V>& a) noexcept {
   return !a.hasValue();
 }
 
@@ -594,13 +624,14 @@ struct OptionalAwaitable {
     return o_.hasValue();
   }
   Value await_resume() {
-    return o_.value();
+    return std::move(o_.value());
   }
-  template <typename CoroHandle>
-  void await_suspend(CoroHandle h) const {
-    // make sure the coroutine returns an empty Optional:
-    h.promise().value_->clear();
-    // Abort the rest of the coroutine:
+
+  // Explicitly only allow suspension into an OptionalPromise
+  template <typename U>
+  void await_suspend(
+      std::experimental::coroutine_handle<OptionalPromise<U>> h) const {
+    // Abort the rest of the coroutine. resume() is not going to be called
     h.destroy();
   }
 };
@@ -613,13 +644,13 @@ detail::OptionalAwaitable<Value>
 }
 } // namespace folly
 
-// This makes std::optional<Value> useable as a coroutine return type..
+// This makes folly::Optional<Value> useable as a coroutine return type..
 FOLLY_NAMESPACE_STD_BEGIN
 namespace experimental {
 template <typename Value, typename... Args>
 struct coroutine_traits<folly::Optional<Value>, Args...> {
   using promise_type = folly::detail::OptionalPromise<Value>;
 };
-} // experimental
+} // namespace experimental
 FOLLY_NAMESPACE_STD_END
 #endif // FOLLY_HAS_COROUTINES