Skip to content

Commit

Permalink
Add an emplace(...) method to llvm::Optional<T>.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jrose-apple committed Oct 1, 2014
1 parent 06c1373 commit 771ac70
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 0 deletions.
56 changes: 56 additions & 0 deletions include/llvm/ADT/Optional.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "llvm/Support/AlignOf.h"
#include "llvm/Support/Compiler.h"
#include <cassert>
#include <new>
#include <utility>

namespace llvm {
Expand Down Expand Up @@ -69,6 +70,61 @@ class Optional {
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();
}
Expand Down
49 changes: 49 additions & 0 deletions unittests/ADT/OptionalTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 771ac70

Please sign in to comment.