| #include <iostream> |
| #include <vector> |
| |
| #include <cm/optional> |
| #include <cm/utility> |
| |
| class EventLogger; |
| |
| class Event |
| { |
| public: |
| enum EventType |
| { |
| DEFAULT_CONSTRUCT, |
| COPY_CONSTRUCT, |
| MOVE_CONSTRUCT, |
| VALUE_CONSTRUCT, |
| |
| DESTRUCT, |
| |
| COPY_ASSIGN, |
| MOVE_ASSIGN, |
| VALUE_ASSIGN, |
| |
| REFERENCE, |
| CONST_REFERENCE, |
| RVALUE_REFERENCE, |
| CONST_RVALUE_REFERENCE, |
| |
| SWAP, |
| |
| COMPARE_EE_EQ, |
| COMPARE_EE_NE, |
| COMPARE_EE_LT, |
| COMPARE_EE_LE, |
| COMPARE_EE_GT, |
| COMPARE_EE_GE, |
| }; |
| |
| EventType Type; |
| const EventLogger* Logger1; |
| const EventLogger* Logger2; |
| int Value; |
| |
| bool operator==(const Event& other) const; |
| bool operator!=(const Event& other) const; |
| }; |
| |
| bool Event::operator==(const Event& other) const |
| { |
| return this->Type == other.Type && this->Logger1 == other.Logger1 && |
| this->Logger2 == other.Logger2 && this->Value == other.Value; |
| } |
| |
| bool Event::operator!=(const Event& other) const |
| { |
| return !(*this == other); |
| } |
| |
| static std::vector<Event> events; |
| |
| class EventLogger |
| { |
| public: |
| EventLogger(); |
| EventLogger(const EventLogger& other); |
| EventLogger(EventLogger&& other); |
| EventLogger(int value); |
| |
| ~EventLogger(); |
| |
| EventLogger& operator=(const EventLogger& other); |
| EventLogger& operator=(EventLogger&& other); |
| EventLogger& operator=(int value); |
| |
| void Reference() &; |
| void Reference() const&; |
| void Reference() &&; |
| void Reference() const&&; |
| |
| int Value = 0; |
| }; |
| |
| class NoMoveAssignEventLogger : public EventLogger |
| { |
| public: |
| using EventLogger::EventLogger; |
| |
| NoMoveAssignEventLogger(const NoMoveAssignEventLogger&) = default; |
| NoMoveAssignEventLogger(NoMoveAssignEventLogger&&) = default; |
| |
| NoMoveAssignEventLogger& operator=(const NoMoveAssignEventLogger&) = default; |
| NoMoveAssignEventLogger& operator=(NoMoveAssignEventLogger&&) = delete; |
| }; |
| |
| #define ASSERT_TRUE(x) \ |
| do { \ |
| if (!(x)) { \ |
| std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n"; \ |
| return false; \ |
| } \ |
| } while (false) |
| |
| // Certain builds of GCC generate false -Wmaybe-uninitialized warnings when |
| // doing a release build with the system version of std::optional. These |
| // warnings do not manifest when using our own cm::optional implementation. |
| // Silence these false warnings. |
| #if defined(__GNUC__) && !defined(__clang__) |
| # define BEGIN_IGNORE_UNINITIALIZED \ |
| _Pragma("GCC diagnostic push") \ |
| _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") |
| # define END_IGNORE_UNINITIALIZED _Pragma("GCC diagnostic pop") |
| #else |
| # define BEGIN_IGNORE_UNINITIALIZED |
| # define END_IGNORE_UNINITIALIZED |
| #endif |
| |
| static void swap(EventLogger& e1, EventLogger& e2) |
| { |
| BEGIN_IGNORE_UNINITIALIZED |
| events.push_back({ Event::SWAP, &e1, &e2, e2.Value }); |
| END_IGNORE_UNINITIALIZED |
| auto tmp = e1.Value; |
| e1.Value = e2.Value; |
| e2.Value = tmp; |
| } |
| |
| EventLogger::EventLogger() |
| : Value(0) |
| { |
| events.push_back({ Event::DEFAULT_CONSTRUCT, this, nullptr, 0 }); |
| } |
| |
| EventLogger::EventLogger(const EventLogger& other) |
| : Value(other.Value) |
| { |
| events.push_back({ Event::COPY_CONSTRUCT, this, &other, other.Value }); |
| } |
| |
| BEGIN_IGNORE_UNINITIALIZED |
| EventLogger::EventLogger(EventLogger&& other) |
| : Value(other.Value) |
| { |
| events.push_back({ Event::MOVE_CONSTRUCT, this, &other, other.Value }); |
| } |
| END_IGNORE_UNINITIALIZED |
| |
| EventLogger::EventLogger(int value) |
| : Value(value) |
| { |
| events.push_back({ Event::VALUE_CONSTRUCT, this, nullptr, value }); |
| } |
| |
| EventLogger::~EventLogger() |
| { |
| BEGIN_IGNORE_UNINITIALIZED |
| events.push_back({ Event::DESTRUCT, this, nullptr, this->Value }); |
| END_IGNORE_UNINITIALIZED |
| } |
| |
| EventLogger& EventLogger::operator=(const EventLogger& other) |
| { |
| events.push_back({ Event::COPY_ASSIGN, this, &other, other.Value }); |
| this->Value = other.Value; |
| return *this; |
| } |
| |
| EventLogger& EventLogger::operator=(EventLogger&& other) |
| { |
| events.push_back({ Event::MOVE_ASSIGN, this, &other, other.Value }); |
| this->Value = other.Value; |
| return *this; |
| } |
| |
| EventLogger& EventLogger::operator=(int value) |
| { |
| events.push_back({ Event::VALUE_ASSIGN, this, nullptr, value }); |
| this->Value = value; |
| return *this; |
| } |
| |
| static bool operator==(const EventLogger& lhs, const EventLogger& rhs) |
| { |
| events.push_back({ Event::COMPARE_EE_EQ, &lhs, &rhs, lhs.Value }); |
| return lhs.Value == rhs.Value; |
| } |
| |
| static bool operator!=(const EventLogger& lhs, const EventLogger& rhs) |
| { |
| events.push_back({ Event::COMPARE_EE_NE, &lhs, &rhs, lhs.Value }); |
| return lhs.Value != rhs.Value; |
| } |
| |
| static bool operator<(const EventLogger& lhs, const EventLogger& rhs) |
| { |
| events.push_back({ Event::COMPARE_EE_LT, &lhs, &rhs, lhs.Value }); |
| return lhs.Value < rhs.Value; |
| } |
| |
| static bool operator<=(const EventLogger& lhs, const EventLogger& rhs) |
| { |
| events.push_back({ Event::COMPARE_EE_LE, &lhs, &rhs, lhs.Value }); |
| return lhs.Value <= rhs.Value; |
| } |
| |
| static bool operator>(const EventLogger& lhs, const EventLogger& rhs) |
| { |
| events.push_back({ Event::COMPARE_EE_GT, &lhs, &rhs, lhs.Value }); |
| return lhs.Value > rhs.Value; |
| } |
| |
| static bool operator>=(const EventLogger& lhs, const EventLogger& rhs) |
| { |
| events.push_back({ Event::COMPARE_EE_GE, &lhs, &rhs, lhs.Value }); |
| return lhs.Value >= rhs.Value; |
| } |
| |
| void EventLogger::Reference() & |
| { |
| events.push_back({ Event::REFERENCE, this, nullptr, this->Value }); |
| } |
| |
| void EventLogger::Reference() const& |
| { |
| events.push_back({ Event::CONST_REFERENCE, this, nullptr, this->Value }); |
| } |
| |
| void EventLogger::Reference() && |
| { |
| events.push_back({ Event::RVALUE_REFERENCE, this, nullptr, this->Value }); |
| } |
| |
| void EventLogger::Reference() const&& |
| { |
| events.push_back( |
| { Event::CONST_RVALUE_REFERENCE, this, nullptr, this->Value }); |
| } |
| |
| static bool testDefaultConstruct(std::vector<Event>& expected) |
| { |
| const cm::optional<EventLogger> o{}; |
| |
| expected = {}; |
| return true; |
| } |
| |
| static bool testNulloptConstruct(std::vector<Event>& expected) |
| { |
| const cm::optional<EventLogger> o{ cm::nullopt }; |
| |
| expected = {}; |
| return true; |
| } |
| |
| static bool testValueConstruct(std::vector<Event>& expected) |
| { |
| const cm::optional<EventLogger> o{ 4 }; |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, &*o, nullptr, 4 }, |
| { Event::DESTRUCT, &*o, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| static bool testInPlaceConstruct(std::vector<Event>& expected) |
| { |
| const cm::optional<EventLogger> o1{ cm::in_place, 4 }; |
| const cm::optional<EventLogger> o2{ cm::in_place_t{}, 4 }; |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 }, |
| { Event::VALUE_CONSTRUCT, &*o2, nullptr, 4 }, |
| { Event::DESTRUCT, &*o2, nullptr, 4 }, |
| { Event::DESTRUCT, &*o1, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| static bool testCopyConstruct(std::vector<Event>& expected) |
| { |
| const cm::optional<EventLogger> o1{ 4 }; |
| const cm::optional<EventLogger> o2{ o1 }; |
| const cm::optional<EventLogger> o3{}; |
| const cm::optional<EventLogger> o4{ o3 }; |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 }, |
| { Event::COPY_CONSTRUCT, &*o2, &o1.value(), 4 }, |
| { Event::DESTRUCT, &*o2, nullptr, 4 }, |
| { Event::DESTRUCT, &*o1, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| static bool testMoveConstruct(std::vector<Event>& expected) |
| { |
| cm::optional<EventLogger> o1{ 4 }; |
| const cm::optional<EventLogger> o2{ std::move(o1) }; |
| cm::optional<EventLogger> o3{}; |
| const cm::optional<EventLogger> o4{ std::move(o3) }; |
| |
| #ifndef __clang_analyzer__ /* cplusplus.Move */ |
| expected = { |
| { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 }, |
| { Event::MOVE_CONSTRUCT, &*o2, &*o1, 4 }, |
| { Event::DESTRUCT, &*o2, nullptr, 4 }, |
| { Event::DESTRUCT, &*o1, nullptr, 4 }, |
| }; |
| #endif |
| return true; |
| } |
| |
| static bool testNulloptAssign(std::vector<Event>& expected) |
| { |
| cm::optional<EventLogger> o1{ 4 }; |
| auto const* v1 = &*o1; |
| o1 = cm::nullopt; |
| cm::optional<EventLogger> o2{}; |
| o2 = cm::nullopt; |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, v1, nullptr, 4 }, |
| { Event::DESTRUCT, v1, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| static bool testCopyAssign(std::vector<Event>& expected) |
| { |
| cm::optional<EventLogger> o1{}; |
| const cm::optional<EventLogger> o2{ 4 }; |
| auto const* v2 = &*o2; |
| o1 = o2; |
| auto const* v1 = &*o1; |
| const cm::optional<EventLogger> o3{ 5 }; |
| auto const* v3 = &*o3; |
| o1 = o3; |
| const cm::optional<EventLogger> o4{}; |
| o1 = o4; |
| o1 = o4; // Intentionally duplicated to test assigning an empty optional to |
| // an empty optional |
| |
| cm::optional<NoMoveAssignEventLogger> o5{ 1 }; |
| auto const* v5 = &*o5; |
| const cm::optional<NoMoveAssignEventLogger> o6{ 2 }; |
| auto const* v6 = &*o6; |
| o5 = std::move(o6); |
| const NoMoveAssignEventLogger e7{ 3 }; |
| o5 = std::move(e7); |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, v2, nullptr, 4 }, |
| { Event::COPY_CONSTRUCT, v1, v2, 4 }, |
| { Event::VALUE_CONSTRUCT, v3, nullptr, 5 }, |
| { Event::COPY_ASSIGN, v1, v3, 5 }, |
| { Event::DESTRUCT, v1, nullptr, 5 }, |
| { Event::VALUE_CONSTRUCT, v5, nullptr, 1 }, |
| { Event::VALUE_CONSTRUCT, v6, nullptr, 2 }, |
| { Event::COPY_ASSIGN, v5, v6, 2 }, |
| { Event::VALUE_CONSTRUCT, &e7, nullptr, 3 }, |
| { Event::COPY_ASSIGN, v5, &e7, 3 }, |
| { Event::DESTRUCT, &e7, nullptr, 3 }, |
| { Event::DESTRUCT, v6, nullptr, 2 }, |
| { Event::DESTRUCT, v5, nullptr, 3 }, |
| { Event::DESTRUCT, v3, nullptr, 5 }, |
| { Event::DESTRUCT, v2, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| static bool testMoveAssign(std::vector<Event>& expected) |
| { |
| cm::optional<EventLogger> o1{}; |
| cm::optional<EventLogger> o2{ 4 }; |
| auto const* v2 = &*o2; |
| o1 = std::move(o2); |
| auto const* v1 = &*o1; |
| cm::optional<EventLogger> o3{ 5 }; |
| auto const* v3 = &*o3; |
| o1 = std::move(o3); |
| cm::optional<EventLogger> o4{}; |
| o1 = std::move(o4); |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, v2, nullptr, 4 }, |
| { Event::MOVE_CONSTRUCT, v1, v2, 4 }, |
| { Event::VALUE_CONSTRUCT, v3, nullptr, 5 }, |
| { Event::MOVE_ASSIGN, v1, v3, 5 }, |
| { Event::DESTRUCT, v1, nullptr, 5 }, |
| { Event::DESTRUCT, v3, nullptr, 5 }, |
| { Event::DESTRUCT, v2, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| static bool testPointer(std::vector<Event>& expected) |
| { |
| cm::optional<EventLogger> o1{ 4 }; |
| const cm::optional<EventLogger> o2{ 5 }; |
| |
| o1->Reference(); |
| o2->Reference(); |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 }, |
| { Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 }, |
| { Event::REFERENCE, &*o1, nullptr, 4 }, |
| { Event::CONST_REFERENCE, &*o2, nullptr, 5 }, |
| { Event::DESTRUCT, &*o2, nullptr, 5 }, |
| { Event::DESTRUCT, &*o1, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| #if !__GNUC__ || __GNUC__ > 4 |
| # define ALLOW_CONST_RVALUE |
| #endif |
| |
| static bool testDereference(std::vector<Event>& expected) |
| { |
| cm::optional<EventLogger> o1{ 4 }; |
| auto const* v1 = &*o1; |
| const cm::optional<EventLogger> o2{ 5 }; |
| auto const* v2 = &*o2; |
| |
| (*o1).Reference(); |
| (*o2).Reference(); |
| (*std::move(o1)).Reference(); |
| #ifdef ALLOW_CONST_RVALUE |
| (*std::move(o2)).Reference(); // Broken in GCC 4.9.0. Sigh... |
| #endif |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, v1, nullptr, 4 }, |
| { Event::VALUE_CONSTRUCT, v2, nullptr, 5 }, |
| { Event::REFERENCE, v1, nullptr, 4 }, |
| { Event::CONST_REFERENCE, v2, nullptr, 5 }, |
| { Event::RVALUE_REFERENCE, v1, nullptr, 4 }, |
| #ifdef ALLOW_CONST_RVALUE |
| { Event::CONST_RVALUE_REFERENCE, v2, nullptr, 5 }, |
| #endif |
| { Event::DESTRUCT, v2, nullptr, 5 }, |
| { Event::DESTRUCT, v1, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| static bool testHasValue(std::vector<Event>& expected) |
| { |
| const cm::optional<EventLogger> o1{ 4 }; |
| const cm::optional<EventLogger> o2{}; |
| |
| ASSERT_TRUE(o1.has_value()); |
| ASSERT_TRUE(o1); |
| ASSERT_TRUE(!o2.has_value()); |
| ASSERT_TRUE(!o2); |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 }, |
| { Event::DESTRUCT, &*o1, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| static bool testValue(std::vector<Event>& expected) |
| { |
| cm::optional<EventLogger> o1{ 4 }; |
| const cm::optional<EventLogger> o2{ 5 }; |
| cm::optional<EventLogger> o3{}; |
| const cm::optional<EventLogger> o4{}; |
| |
| o1.value().Reference(); |
| o2.value().Reference(); |
| |
| bool thrown = false; |
| try { |
| (void)o3.value(); |
| } catch (cm::bad_optional_access&) { |
| thrown = true; |
| } |
| ASSERT_TRUE(thrown); |
| |
| thrown = false; |
| try { |
| (void)o4.value(); |
| } catch (cm::bad_optional_access&) { |
| thrown = true; |
| } |
| ASSERT_TRUE(thrown); |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 }, |
| { Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 }, |
| { Event::REFERENCE, &*o1, nullptr, 4 }, |
| { Event::CONST_REFERENCE, &*o2, nullptr, 5 }, |
| { Event::DESTRUCT, &*o2, nullptr, 5 }, |
| { Event::DESTRUCT, &*o1, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| static bool testValueOr() |
| { |
| const cm::optional<EventLogger> o1{ 4 }; |
| cm::optional<EventLogger> o2{ 5 }; |
| const cm::optional<EventLogger> o3{}; |
| cm::optional<EventLogger> o4{}; |
| |
| EventLogger e1{ 6 }; |
| EventLogger e2{ 7 }; |
| EventLogger e3{ 8 }; |
| EventLogger e4{ 9 }; |
| |
| EventLogger r1 = o1.value_or(e1); |
| ASSERT_TRUE(r1.Value == 4); |
| EventLogger r2 = std::move(o2).value_or(e2); |
| ASSERT_TRUE(r2.Value == 5); |
| EventLogger r3 = o3.value_or(e3); |
| ASSERT_TRUE(r3.Value == 8); |
| EventLogger r4 = std::move(o4).value_or(e4); |
| ASSERT_TRUE(r4.Value == 9); |
| |
| return true; |
| } |
| |
| static bool testComparison(std::vector<Event>& expected) |
| { |
| const cm::optional<EventLogger> o1{ 1 }; |
| const cm::optional<EventLogger> o2{ 2 }; |
| const cm::optional<EventLogger> o3{ 2 }; |
| const cm::optional<EventLogger> o4{}; |
| const cm::optional<EventLogger> o5{}; |
| const EventLogger e1{ 2 }; |
| |
| ASSERT_TRUE(!(o1 == o2) && o1 != o2); |
| ASSERT_TRUE(o1 < o2 && !(o1 >= o2)); |
| ASSERT_TRUE(!(o1 > o2) && o1 <= o2); |
| |
| ASSERT_TRUE(o2 == o3 && !(o2 != o3)); |
| ASSERT_TRUE(!(o2 < o3) && o2 >= o3); |
| ASSERT_TRUE(!(o2 > o3) && o2 <= o3); |
| |
| ASSERT_TRUE(!(o3 == o4) && o3 != o4); |
| ASSERT_TRUE(!(o3 < o4) && o3 >= o4); |
| ASSERT_TRUE(o3 > o4 && !(o3 <= o4)); |
| |
| ASSERT_TRUE(o4 == o5 && !(o4 != o5)); |
| ASSERT_TRUE(!(o4 < o5) && o4 >= o5); |
| ASSERT_TRUE(!(o4 > o5) && o4 <= o5); |
| |
| ASSERT_TRUE(!(o1 == cm::nullopt) && o1 != cm::nullopt); |
| ASSERT_TRUE(!(o1 < cm::nullopt) && o1 >= cm::nullopt); |
| ASSERT_TRUE(o1 > cm::nullopt && !(o1 <= cm::nullopt)); |
| |
| ASSERT_TRUE(!(cm::nullopt == o1) && cm::nullopt != o1); |
| ASSERT_TRUE(cm::nullopt < o1 && !(cm::nullopt >= o1)); |
| ASSERT_TRUE(!(cm::nullopt > o1) && cm::nullopt <= o1); |
| |
| ASSERT_TRUE(o4 == cm::nullopt && !(o4 != cm::nullopt)); |
| ASSERT_TRUE(!(o4 < cm::nullopt) && o4 >= cm::nullopt); |
| ASSERT_TRUE(!(o4 > cm::nullopt) && o4 <= cm::nullopt); |
| |
| ASSERT_TRUE(cm::nullopt == o4 && !(cm::nullopt != o4)); |
| ASSERT_TRUE(!(cm::nullopt < o4) && cm::nullopt >= o4); |
| ASSERT_TRUE(!(cm::nullopt > o4) && cm::nullopt <= o4); |
| |
| ASSERT_TRUE(!(o1 == e1) && o1 != e1); |
| ASSERT_TRUE(o1 < e1 && !(o1 >= e1)); |
| ASSERT_TRUE(!(o1 > e1) && o1 <= e1); |
| |
| ASSERT_TRUE(o2 == e1 && !(o2 != e1)); |
| ASSERT_TRUE(!(o2 < e1) && o2 >= e1); |
| ASSERT_TRUE(!(o2 > e1) && o2 <= e1); |
| |
| ASSERT_TRUE(!(o4 == e1) && o4 != e1); |
| ASSERT_TRUE(o4 < e1 && !(o4 >= e1)); |
| ASSERT_TRUE(!(o4 > e1) && o4 <= e1); |
| |
| ASSERT_TRUE(!(e1 == o1) && e1 != o1); |
| ASSERT_TRUE(!(e1 < o1) && e1 >= o1); |
| ASSERT_TRUE(e1 > o1 && !(e1 <= o1)); |
| |
| ASSERT_TRUE(e1 == o2 && !(e1 != o2)); |
| ASSERT_TRUE(!(e1 < o2) && e1 >= o2); |
| ASSERT_TRUE(!(e1 > o2) && e1 <= o2); |
| |
| ASSERT_TRUE(!(e1 == o4) && e1 != o4); |
| ASSERT_TRUE(!(e1 < o4) && e1 >= o4); |
| ASSERT_TRUE(e1 > o4 && !(e1 <= o4)); |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, &*o1, nullptr, 1 }, |
| { Event::VALUE_CONSTRUCT, &*o2, nullptr, 2 }, |
| { Event::VALUE_CONSTRUCT, &*o3, nullptr, 2 }, |
| { Event::VALUE_CONSTRUCT, &e1, nullptr, 2 }, |
| { Event::COMPARE_EE_EQ, &*o1, &*o2, 1 }, |
| { Event::COMPARE_EE_NE, &*o1, &*o2, 1 }, |
| { Event::COMPARE_EE_LT, &*o1, &*o2, 1 }, |
| { Event::COMPARE_EE_GE, &*o1, &*o2, 1 }, |
| { Event::COMPARE_EE_GT, &*o1, &*o2, 1 }, |
| { Event::COMPARE_EE_LE, &*o1, &*o2, 1 }, |
| { Event::COMPARE_EE_EQ, &*o2, &*o3, 2 }, |
| { Event::COMPARE_EE_NE, &*o2, &*o3, 2 }, |
| { Event::COMPARE_EE_LT, &*o2, &*o3, 2 }, |
| { Event::COMPARE_EE_GE, &*o2, &*o3, 2 }, |
| { Event::COMPARE_EE_GT, &*o2, &*o3, 2 }, |
| { Event::COMPARE_EE_LE, &*o2, &*o3, 2 }, |
| { Event::COMPARE_EE_EQ, &*o1, &e1, 1 }, |
| { Event::COMPARE_EE_NE, &*o1, &e1, 1 }, |
| { Event::COMPARE_EE_LT, &*o1, &e1, 1 }, |
| { Event::COMPARE_EE_GE, &*o1, &e1, 1 }, |
| { Event::COMPARE_EE_GT, &*o1, &e1, 1 }, |
| { Event::COMPARE_EE_LE, &*o1, &e1, 1 }, |
| { Event::COMPARE_EE_EQ, &*o2, &e1, 2 }, |
| { Event::COMPARE_EE_NE, &*o2, &e1, 2 }, |
| { Event::COMPARE_EE_LT, &*o2, &e1, 2 }, |
| { Event::COMPARE_EE_GE, &*o2, &e1, 2 }, |
| { Event::COMPARE_EE_GT, &*o2, &e1, 2 }, |
| { Event::COMPARE_EE_LE, &*o2, &e1, 2 }, |
| { Event::COMPARE_EE_EQ, &e1, &*o1, 2 }, |
| { Event::COMPARE_EE_NE, &e1, &*o1, 2 }, |
| { Event::COMPARE_EE_LT, &e1, &*o1, 2 }, |
| { Event::COMPARE_EE_GE, &e1, &*o1, 2 }, |
| { Event::COMPARE_EE_GT, &e1, &*o1, 2 }, |
| { Event::COMPARE_EE_LE, &e1, &*o1, 2 }, |
| { Event::COMPARE_EE_EQ, &e1, &*o2, 2 }, |
| { Event::COMPARE_EE_NE, &e1, &*o2, 2 }, |
| { Event::COMPARE_EE_LT, &e1, &*o2, 2 }, |
| { Event::COMPARE_EE_GE, &e1, &*o2, 2 }, |
| { Event::COMPARE_EE_GT, &e1, &*o2, 2 }, |
| { Event::COMPARE_EE_LE, &e1, &*o2, 2 }, |
| { Event::DESTRUCT, &e1, nullptr, 2 }, |
| { Event::DESTRUCT, &*o3, nullptr, 2 }, |
| { Event::DESTRUCT, &*o2, nullptr, 2 }, |
| { Event::DESTRUCT, &*o1, nullptr, 1 }, |
| }; |
| return true; |
| } |
| |
| static bool testSwap(std::vector<Event>& expected) |
| { |
| cm::optional<EventLogger> o1{ 4 }; |
| auto const* v1 = &*o1; |
| cm::optional<EventLogger> o2{}; |
| |
| o1.swap(o2); |
| auto const* v2 = &*o2; |
| |
| ASSERT_TRUE(!o1.has_value()); |
| ASSERT_TRUE(o2.has_value()); |
| ASSERT_TRUE(o2.value().Value == 4); |
| |
| o1.swap(o2); |
| |
| ASSERT_TRUE(o1.has_value()); |
| ASSERT_TRUE(o1.value().Value == 4); |
| ASSERT_TRUE(!o2.has_value()); |
| |
| o2.emplace(5); |
| o1.swap(o2); |
| |
| ASSERT_TRUE(o1.has_value()); |
| ASSERT_TRUE(o1.value().Value == 5); |
| ASSERT_TRUE(o2.has_value()); |
| ASSERT_TRUE(o2.value().Value == 4); |
| |
| o1.reset(); |
| o2.reset(); |
| o1.swap(o2); |
| |
| ASSERT_TRUE(!o1.has_value()); |
| ASSERT_TRUE(!o2.has_value()); |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, v1, nullptr, 4 }, |
| { Event::MOVE_CONSTRUCT, v2, v1, 4 }, |
| { Event::DESTRUCT, v1, nullptr, 4 }, |
| { Event::MOVE_CONSTRUCT, v1, v2, 4 }, |
| { Event::DESTRUCT, v2, nullptr, 4 }, |
| { Event::VALUE_CONSTRUCT, v2, nullptr, 5 }, |
| { Event::SWAP, v1, v2, 5 }, |
| { Event::DESTRUCT, v1, nullptr, 5 }, |
| { Event::DESTRUCT, v2, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| static bool testReset(std::vector<Event>& expected) |
| { |
| cm::optional<EventLogger> o{ 4 }; |
| auto const* v = &*o; |
| |
| o.reset(); |
| |
| ASSERT_TRUE(!o.has_value()); |
| |
| o.reset(); |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, v, nullptr, 4 }, |
| { Event::DESTRUCT, v, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| static bool testEmplace(std::vector<Event>& expected) |
| { |
| cm::optional<EventLogger> o{ 4 }; |
| |
| o.emplace(5); |
| o.reset(); |
| o.emplace(); |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, &*o, nullptr, 4 }, |
| { Event::DESTRUCT, &*o, nullptr, 4 }, |
| { Event::VALUE_CONSTRUCT, &*o, nullptr, 5 }, |
| { Event::DESTRUCT, &*o, nullptr, 5 }, |
| { Event::DEFAULT_CONSTRUCT, &*o, nullptr, 0 }, |
| { Event::DESTRUCT, &*o, nullptr, 0 }, |
| }; |
| return true; |
| } |
| |
| static bool testMakeOptional(std::vector<Event>& expected) |
| { |
| EventLogger e{ 4 }; |
| cm::optional<EventLogger> o1 = cm::make_optional<EventLogger>(e); |
| cm::optional<EventLogger> o2 = cm::make_optional<EventLogger>(5); |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, &e, nullptr, 4 }, |
| { Event::COPY_CONSTRUCT, &*o1, &e, 4 }, |
| { Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 }, |
| { Event::DESTRUCT, &*o2, nullptr, 5 }, |
| { Event::DESTRUCT, &*o1, nullptr, 4 }, |
| { Event::DESTRUCT, &e, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| static bool testMemoryRange(std::vector<Event>& expected) |
| { |
| cm::optional<EventLogger> o{ 4 }; |
| |
| auto* ostart = &o; |
| auto* oend = ostart + 1; |
| auto* estart = &o.value(); |
| auto* eend = estart + 1; |
| |
| ASSERT_TRUE(static_cast<void*>(estart) >= static_cast<void*>(ostart) && |
| static_cast<void*>(eend) <= static_cast<void*>(oend)); |
| |
| expected = { |
| { Event::VALUE_CONSTRUCT, &*o, nullptr, 4 }, |
| { Event::DESTRUCT, &*o, nullptr, 4 }, |
| }; |
| return true; |
| } |
| |
| int testOptional(int /*unused*/, char* /*unused*/[]) |
| { |
| int retval = 0; |
| |
| #define DO_EVENT_TEST(name) \ |
| do { \ |
| events.clear(); \ |
| std::vector<Event> expected; \ |
| if (!name(expected)) { \ |
| std::cout << "in " #name << std::endl; \ |
| retval = 1; \ |
| } else if (expected != events) { \ |
| std::cout << #name " did not produce expected events" << std::endl; \ |
| retval = 1; \ |
| } \ |
| } while (0) |
| |
| #define DO_TEST(name) \ |
| do { \ |
| if (!name()) { \ |
| std::cout << "in " #name << std::endl; \ |
| retval = 1; \ |
| } \ |
| } while (0) |
| |
| DO_EVENT_TEST(testDefaultConstruct); |
| DO_EVENT_TEST(testNulloptConstruct); |
| DO_EVENT_TEST(testValueConstruct); |
| DO_EVENT_TEST(testInPlaceConstruct); |
| DO_EVENT_TEST(testCopyConstruct); |
| DO_EVENT_TEST(testMoveConstruct); |
| DO_EVENT_TEST(testNulloptAssign); |
| DO_EVENT_TEST(testCopyAssign); |
| DO_EVENT_TEST(testMoveAssign); |
| DO_EVENT_TEST(testPointer); |
| DO_EVENT_TEST(testDereference); |
| DO_EVENT_TEST(testHasValue); |
| DO_EVENT_TEST(testValue); |
| DO_TEST(testValueOr); |
| DO_EVENT_TEST(testComparison); |
| DO_EVENT_TEST(testSwap); |
| DO_EVENT_TEST(testReset); |
| DO_EVENT_TEST(testEmplace); |
| DO_EVENT_TEST(testMakeOptional); |
| DO_EVENT_TEST(testMemoryRange); |
| |
| return retval; |
| } |