Flesh out Optional members swap, reset, emplace, has_value
authorYedidya Feldblum <yfeldblum@fb.com>
Wed, 25 Oct 2017 05:05:23 +0000 (22:05 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Wed, 25 Oct 2017 05:08:24 +0000 (22:08 -0700)
Summary:
[Folly] Flesh out `Optional` members `swap`, `reset`, `emplace`, `has_value`.

* `swap` as a member and deriving `noexcept`-ness to mimic `std::optional::swap`.
* `reset` v.s. `clear` to mimic `std::optional::reset`.
* `emplace` returning ref and overload taking initializer list to mimic `std::optional::emplace`.
* `has_value` v.s. `hasValue` to mimic `std::optional::has_value`.

Reviewed By: WillerZ

Differential Revision: D6132775

fbshipit-source-id: 34c58367b9dc63289e4b9721c5e79b1c41ba31e4

folly/Optional.h
folly/test/OptionalTest.cpp

index 31e1f4fdc8b6a4a84ab7bdd952d63041971e65e2..39abcc1c6db900ddc5a4118c0335dec55e115b98 100644 (file)
@@ -63,6 +63,7 @@
 
 #include <folly/Launder.h>
 #include <folly/Portability.h>
 
 #include <folly/Launder.h>
 #include <folly/Portability.h>
+#include <folly/Traits.h>
 #include <folly/Utility.h>
 
 namespace folly {
 #include <folly/Utility.h>
 
 namespace folly {
@@ -203,15 +204,41 @@ class Optional {
   }
 
   template <class... Args>
   }
 
   template <class... Args>
-  void emplace(Args&&... args) {
+  Value& emplace(Args&&... args) {
     clear();
     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();
   }
 
     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();
   FOLLY_CPP14_CONSTEXPR const Value& value() const & {
     require_value();
     return *storage_.value_pointer();
@@ -240,12 +267,16 @@ class Optional {
   }
   Value* get_pointer() && = delete;
 
   }
   Value* get_pointer() && = delete;
 
-  FOLLY_CPP14_CONSTEXPR bool hasValue() const noexcept {
+  FOLLY_CPP14_CONSTEXPR bool has_value() const noexcept {
     return storage_.hasValue();
   }
 
     return storage_.hasValue();
   }
 
+  FOLLY_CPP14_CONSTEXPR bool hasValue() const noexcept {
+    return has_value();
+  }
+
   FOLLY_CPP14_CONSTEXPR explicit operator bool() const noexcept {
   FOLLY_CPP14_CONSTEXPR explicit operator bool() const noexcept {
-    return hasValue();
+    return has_value();
   }
 
   FOLLY_CPP14_CONSTEXPR const Value& operator*() const & {
   }
 
   FOLLY_CPP14_CONSTEXPR const Value& operator*() const & {
@@ -350,9 +381,10 @@ class Optional {
     }
 
     template <class... Args>
     }
 
     template <class... Args>
-    void construct(Args&&... args) {
+    Value& construct(Args&&... args) {
       new (raw_pointer()) Value(std::forward<Args>(args)...);
       this->hasValue_ = true;
       new (raw_pointer()) Value(std::forward<Args>(args)...);
       this->hasValue_ = true;
+      return *launder(reinterpret_cast<Value*>(this->value_));
     }
 
    private:
     }
 
    private:
@@ -375,14 +407,8 @@ T* get_pointer(Optional<T>& opt) {
 }
 
 template <class T>
 }
 
 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>>
 }
 
 template <class T, class Opt = Optional<typename std::decay<T>::type>>
index 091dd9a3714e262669172e99c1a95d35df77eef9..a5e12d1504ef2f38333b9c730b3a8c3786aaa7d5 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <folly/Optional.h>
 #include <folly/Portability.h>
 
 #include <folly/Optional.h>
 #include <folly/Portability.h>
+#include <folly/portability/GMock.h>
 #include <folly/portability/GTest.h>
 
 #include <algorithm>
 #include <folly/portability/GTest.h>
 
 #include <algorithm>
@@ -65,6 +66,28 @@ TEST(Optional, NoDefault) {
   EXPECT_FALSE(x);
 }
 
   EXPECT_FALSE(x);
 }
 
+TEST(Optional, Emplace) {
+  Optional<std::vector<int>> opt;
+  auto& values1 = opt.emplace(3, 4);
+  EXPECT_THAT(values1, testing::ElementsAre(4, 4, 4));
+  auto& values2 = opt.emplace(2, 5);
+  EXPECT_THAT(values2, testing::ElementsAre(5, 5));
+}
+
+TEST(Optional, EmplaceInitializerList) {
+  Optional<std::vector<int>> opt;
+  auto& values1 = opt.emplace({3, 4, 5});
+  EXPECT_THAT(values1, testing::ElementsAre(3, 4, 5));
+  auto& values2 = opt.emplace({4, 5, 6});
+  EXPECT_THAT(values2, testing::ElementsAre(4, 5, 6));
+}
+
+TEST(Optional, Reset) {
+  Optional<int> opt(3);
+  opt.reset();
+  EXPECT_FALSE(opt);
+}
+
 TEST(Optional, String) {
   Optional<std::string> maybeString;
   EXPECT_FALSE(maybeString);
 TEST(Optional, String) {
   Optional<std::string> maybeString;
   EXPECT_FALSE(maybeString);
@@ -318,6 +341,10 @@ TEST(Optional, Swap) {
   EXPECT_EQ("bye", a.value());
 
   swap(a, b);
   EXPECT_EQ("bye", a.value());
 
   swap(a, b);
+  EXPECT_TRUE(a.hasValue());
+  EXPECT_TRUE(b.hasValue());
+  EXPECT_EQ("hello", a.value());
+  EXPECT_EQ("bye", b.value());
 }
 
 TEST(Optional, Comparisons) {
 }
 
 TEST(Optional, Comparisons) {