Add an emplace(...) method to llvm::Optional<T>.
authorJordan Rose <jordan_rose@apple.com>
Wed, 1 Oct 2014 02:12:35 +0000 (02:12 +0000)
committerJordan Rose <jordan_rose@apple.com>
Wed, 1 Oct 2014 02:12:35 +0000 (02:12 +0000)
This can be used for in-place initialization of non-moveable types.
For compilers that don't support variadic templates, only up to four
arguments are supported. We can always add more, of course, but this
should be good enough until we move to a later MSVC that has full
support for variadic templates.

Inspired by std::experimental::optional from the "Library Fundamentals" C++ TS.
Reviewed by David Blaikie.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@218732 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/ADT/Optional.h
unittests/ADT/OptionalTest.cpp

index 0386e57f05f44b8f86010e3b59dbe9102312a2b9..591872e6591a03c65e6774276e82eeeaffa542e8 100644 (file)
@@ -20,6 +20,7 @@
 #include "llvm/Support/AlignOf.h"
 #include "llvm/Support/Compiler.h"
 #include <cassert>
+#include <new>
 #include <utility>
 
 namespace llvm {
@@ -69,6 +70,61 @@ public:
     return *this;
   }
 
+#if LLVM_HAS_VARIADIC_TEMPLATES
+
+  /// Create a new object by constructing it in place with the given arguments.
+  template<typename ...ArgTypes>
+  void emplace(ArgTypes &&...Args) {
+    reset();
+    hasVal = true;
+    new (storage.buffer) T(std::forward<ArgTypes>(Args)...);
+  }
+
+#else
+  
+  /// Create a new object by default-constructing it in place.
+  void emplace() {
+    reset();
+    hasVal = true;
+    new (storage.buffer) T();
+  }
+  
+  /// Create a new object by constructing it in place with the given arguments.
+  template<typename T1>
+  void emplace(T1 &&A1) {
+    reset();
+    hasVal = true;
+    new (storage.buffer) T(std::forward<T1>(A1));
+  }
+  
+  /// Create a new object by constructing it in place with the given arguments.
+  template<typename T1, typename T2>
+  void emplace(T1 &&A1, T2 &&A2) {
+    reset();
+    hasVal = true;
+    new (storage.buffer) T(std::forward<T1>(A1), std::forward<T2>(A2));
+  }
+  
+  /// Create a new object by constructing it in place with the given arguments.
+  template<typename T1, typename T2, typename T3>
+  void emplace(T1 &&A1, T2 &&A2, T3 &&A3) {
+    reset();
+    hasVal = true;
+    new (storage.buffer) T(std::forward<T1>(A1), std::forward<T2>(A2),
+        std::forward<T3>(A3));
+  }
+  
+  /// Create a new object by constructing it in place with the given arguments.
+  template<typename T1, typename T2, typename T3, typename T4>
+  void emplace(T1 &&A1, T2 &&A2, T3 &&A3, T4 &&A4) {
+    reset();
+    hasVal = true;
+    new (storage.buffer) T(std::forward<T1>(A1), std::forward<T2>(A2),
+        std::forward<T3>(A3), std::forward<T4>(A4));
+  }
+
+#endif // LLVM_HAS_VARIADIC_TEMPLATES
+
   static inline Optional create(const T* y) {
     return y ? Optional(*y) : Optional();
   }
index 51c54523b88d89356bc92e31ea4048956cd53b6e..5a763d76f45560a6dd3df08e7c7e1a15fdfc5bb7 100644 (file)
@@ -177,6 +177,44 @@ TEST_F(OptionalTest, GetValueOr) {
   EXPECT_EQ(5, A.getValueOr(42));
 }
 
+struct MultiArgConstructor {
+  int x, y;
+  MultiArgConstructor(int x, int y) : x(x), y(y) {}
+  explicit MultiArgConstructor(int x, bool positive)
+    : x(x), y(positive ? x : -x) {}
+
+  MultiArgConstructor(const MultiArgConstructor &) = delete;
+  MultiArgConstructor(MultiArgConstructor &&) = delete;
+  MultiArgConstructor &operator=(const MultiArgConstructor &) = delete;
+  MultiArgConstructor &operator=(MultiArgConstructor &&) = delete;
+
+  static unsigned Destructions;
+  ~MultiArgConstructor() {
+    ++Destructions;
+  }
+  static void ResetCounts() {
+    Destructions = 0;
+  }
+};
+unsigned MultiArgConstructor::Destructions = 0;
+
+TEST_F(OptionalTest, Emplace) {
+  MultiArgConstructor::ResetCounts();
+  Optional<MultiArgConstructor> A;
+  
+  A.emplace(1, 2);
+  EXPECT_TRUE(A.hasValue());
+  EXPECT_EQ(1, A->x);
+  EXPECT_EQ(2, A->y);
+  EXPECT_EQ(0u, MultiArgConstructor::Destructions);
+
+  A.emplace(5, false);
+  EXPECT_TRUE(A.hasValue());
+  EXPECT_EQ(5, A->x);
+  EXPECT_EQ(-5, A->y);
+  EXPECT_EQ(1u, MultiArgConstructor::Destructions);
+}
+
 struct MoveOnly {
   static unsigned MoveConstructions;
   static unsigned Destructions;
@@ -286,6 +324,17 @@ TEST_F(OptionalTest, MoveOnlyAssigningAssignment) {
   EXPECT_EQ(1u, MoveOnly::Destructions);
 }
 
+TEST_F(OptionalTest, MoveOnlyEmplace) {
+  Optional<MoveOnly> A;
+  MoveOnly::ResetCounts();
+  A.emplace(4);
+  EXPECT_TRUE((bool)A);
+  EXPECT_EQ(4, A->val);
+  EXPECT_EQ(0u, MoveOnly::MoveConstructions);
+  EXPECT_EQ(0u, MoveOnly::MoveAssignments);
+  EXPECT_EQ(0u, MoveOnly::Destructions);
+}
+
 #if LLVM_HAS_RVALUE_REFERENCE_THIS
 
 TEST_F(OptionalTest, MoveGetValueOr) {