Flesh out Optional members swap, reset, emplace, has_value
[folly.git] / folly / Optional.h
index 2693423d63e67eb73bfc195c02692b9f0e62f755..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 {
@@ -203,15 +204,41 @@ 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();
   }
 
+  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();
@@ -240,12 +267,16 @@ class Optional {
   }
   Value* get_pointer() && = delete;
 
-  FOLLY_CPP14_CONSTEXPR bool hasValue() const noexcept {
+  FOLLY_CPP14_CONSTEXPR bool has_value() const noexcept {
     return storage_.hasValue();
   }
 
+  FOLLY_CPP14_CONSTEXPR bool hasValue() const noexcept {
+    return has_value();
+  }
+
   FOLLY_CPP14_CONSTEXPR explicit operator bool() const noexcept {
-    return hasValue();
+    return has_value();
   }
 
   FOLLY_CPP14_CONSTEXPR const Value& operator*() const & {
@@ -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,14 +407,8 @@ 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>>
@@ -598,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();
   }
 };
@@ -617,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