From 4f09cd697cdcd0f55b9d74ef5586ca0ea78c00b5 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Wed, 1 Oct 2014 02:12:35 +0000 Subject: Add an emplace(...) method to llvm::Optional. 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. llvm-svn: 218732 --- llvm/include/llvm/ADT/Optional.h | 56 +++++++++++++++++++++++++++++++++++++ llvm/unittests/ADT/OptionalTest.cpp | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) (limited to 'llvm') diff --git a/llvm/include/llvm/ADT/Optional.h b/llvm/include/llvm/ADT/Optional.h index 0386e57f05f..591872e6591 100644 --- a/llvm/include/llvm/ADT/Optional.h +++ b/llvm/include/llvm/ADT/Optional.h @@ -20,6 +20,7 @@ #include "llvm/Support/AlignOf.h" #include "llvm/Support/Compiler.h" #include +#include #include 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 + void emplace(ArgTypes &&...Args) { + reset(); + hasVal = true; + new (storage.buffer) T(std::forward(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 + void emplace(T1 &&A1) { + reset(); + hasVal = true; + new (storage.buffer) T(std::forward(A1)); + } + + /// Create a new object by constructing it in place with the given arguments. + template + void emplace(T1 &&A1, T2 &&A2) { + reset(); + hasVal = true; + new (storage.buffer) T(std::forward(A1), std::forward(A2)); + } + + /// Create a new object by constructing it in place with the given arguments. + template + void emplace(T1 &&A1, T2 &&A2, T3 &&A3) { + reset(); + hasVal = true; + new (storage.buffer) T(std::forward(A1), std::forward(A2), + std::forward(A3)); + } + + /// Create a new object by constructing it in place with the given arguments. + template + void emplace(T1 &&A1, T2 &&A2, T3 &&A3, T4 &&A4) { + reset(); + hasVal = true; + new (storage.buffer) T(std::forward(A1), std::forward(A2), + std::forward(A3), std::forward(A4)); + } + +#endif // LLVM_HAS_VARIADIC_TEMPLATES + static inline Optional create(const T* y) { return y ? Optional(*y) : Optional(); } diff --git a/llvm/unittests/ADT/OptionalTest.cpp b/llvm/unittests/ADT/OptionalTest.cpp index 51c54523b88..5a763d76f45 100644 --- a/llvm/unittests/ADT/OptionalTest.cpp +++ b/llvm/unittests/ADT/OptionalTest.cpp @@ -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 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 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) { -- cgit v1.2.3