| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690 |
- #include "cm_optional.hxx"
- #include "cm_utility.hxx"
- #include <iostream>
- #include <type_traits>
- #include <utility>
- #include <vector>
- 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,
- };
- 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;
- };
- // 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
- 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;
- }
- 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) };
- expected = {
- { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
- { Event::MOVE_CONSTRUCT, &*o2, &o1.value(), 4 },
- { Event::DESTRUCT, &*o2, nullptr, 4 },
- { Event::DESTRUCT, &*o1, nullptr, 4 },
- };
- return true;
- }
- static bool testNulloptAssign(std::vector<Event>& expected)
- {
- cm::optional<EventLogger> o1{ 4 };
- o1 = cm::nullopt;
- cm::optional<EventLogger> o2{};
- o2 = cm::nullopt;
- expected = {
- { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
- { Event::DESTRUCT, &*o1, nullptr, 4 },
- };
- return true;
- }
- static bool testCopyAssign(std::vector<Event>& expected)
- {
- cm::optional<EventLogger> o1{};
- const cm::optional<EventLogger> o2{ 4 };
- o1 = o2;
- const cm::optional<EventLogger> o3{ 5 };
- o1 = o3;
- const cm::optional<EventLogger> o4{};
- o1 = o4;
- o1 = o4; // Intentionally duplicated to test assigning an empty optional to
- // an empty optional
- expected = {
- { Event::VALUE_CONSTRUCT, &*o2, nullptr, 4 },
- { Event::COPY_CONSTRUCT, &*o1, &*o2, 4 },
- { Event::VALUE_CONSTRUCT, &*o3, nullptr, 5 },
- { Event::COPY_ASSIGN, &*o1, &*o3, 5 },
- { Event::DESTRUCT, &*o1, nullptr, 5 },
- { Event::DESTRUCT, &o3.value(), nullptr, 5 },
- { Event::DESTRUCT, &o2.value(), nullptr, 4 },
- };
- return true;
- }
- static bool testMoveAssign(std::vector<Event>& expected)
- {
- cm::optional<EventLogger> o1{};
- cm::optional<EventLogger> o2{ 4 };
- o1 = std::move(o2);
- cm::optional<EventLogger> o3{ 5 };
- o1 = std::move(o3);
- cm::optional<EventLogger> o4{};
- o1 = std::move(o4);
- expected = {
- { Event::VALUE_CONSTRUCT, &*o2, nullptr, 4 },
- { Event::MOVE_CONSTRUCT, &*o1, &*o2, 4 },
- { Event::VALUE_CONSTRUCT, &*o3, nullptr, 5 },
- { Event::MOVE_ASSIGN, &*o1, &*o3, 5 },
- { Event::DESTRUCT, &*o1, nullptr, 5 },
- { Event::DESTRUCT, &*o3, nullptr, 5 },
- { Event::DESTRUCT, &*o2, 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 };
- const cm::optional<EventLogger> o2{ 5 };
- (*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, &*o1, nullptr, 4 },
- { Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
- { Event::REFERENCE, &*o1, nullptr, 4 },
- { Event::CONST_REFERENCE, &*o2, nullptr, 5 },
- { Event::RVALUE_REFERENCE, &*o1, nullptr, 4 },
- #ifdef ALLOW_CONST_RVALUE
- { Event::CONST_RVALUE_REFERENCE, &*o2, nullptr, 5 },
- #endif
- { Event::DESTRUCT, &*o2, nullptr, 5 },
- { Event::DESTRUCT, &*o1, nullptr, 4 },
- };
- return true;
- }
- static bool testHasValue(std::vector<Event>& expected)
- {
- bool retval = true;
- const cm::optional<EventLogger> o1{ 4 };
- const cm::optional<EventLogger> o2{};
- if (!o1.has_value()) {
- std::cout << "o1 should have a value" << std::endl;
- retval = false;
- }
- if (!o1) {
- std::cout << "(bool)o1 should be true" << std::endl;
- retval = false;
- }
- if (o2.has_value()) {
- std::cout << "o2 should not have a value" << std::endl;
- retval = false;
- }
- if (o2) {
- std::cout << "(bool)o2 should be false" << std::endl;
- retval = false;
- }
- expected = {
- { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
- { Event::DESTRUCT, &*o1, nullptr, 4 },
- };
- return retval;
- }
- static bool testValue(std::vector<Event>& expected)
- {
- bool retval = true;
- 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;
- }
- if (!thrown) {
- std::cout << "o3.value() did not throw" << std::endl;
- retval = false;
- }
- thrown = false;
- try {
- (void)o4.value();
- } catch (cm::bad_optional_access&) {
- thrown = true;
- }
- if (!thrown) {
- std::cout << "o4.value() did not throw" << std::endl;
- retval = false;
- }
- 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 retval;
- }
- static bool testValueOr()
- {
- bool retval = true;
- 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);
- if (r1.Value != 4) {
- std::cout << "r1.Value should be 4" << std::endl;
- retval = false;
- }
- EventLogger r2 = std::move(o2).value_or(e2);
- if (r2.Value != 5) {
- std::cout << "r2.Value should be 5" << std::endl;
- retval = false;
- }
- EventLogger r3 = o3.value_or(e3);
- if (r3.Value != 8) {
- std::cout << "r3.Value should be 8" << std::endl;
- retval = false;
- }
- EventLogger r4 = std::move(o4).value_or(e4);
- if (r4.Value != 9) {
- std::cout << "r4.Value should be 9" << std::endl;
- retval = false;
- }
- return retval;
- }
- static bool testSwap(std::vector<Event>& expected)
- {
- bool retval = true;
- cm::optional<EventLogger> o1{ 4 };
- cm::optional<EventLogger> o2{};
- o1.swap(o2);
- if (o1.has_value()) {
- std::cout << "o1 should not have value" << std::endl;
- retval = false;
- }
- if (!o2.has_value()) {
- std::cout << "o2 should have value" << std::endl;
- retval = false;
- }
- if (o2.value().Value != 4) {
- std::cout << "value of o2 should be 4" << std::endl;
- retval = false;
- }
- o1.swap(o2);
- if (!o1.has_value()) {
- std::cout << "o1 should have value" << std::endl;
- retval = false;
- }
- if (o1.value().Value != 4) {
- std::cout << "value of o1 should be 4" << std::endl;
- retval = false;
- }
- if (o2.has_value()) {
- std::cout << "o2 should not have value" << std::endl;
- retval = false;
- }
- o2.emplace(5);
- o1.swap(o2);
- if (!o1.has_value()) {
- std::cout << "o1 should have value" << std::endl;
- retval = false;
- }
- if (o1.value().Value != 5) {
- std::cout << "value of o1 should be 5" << std::endl;
- retval = false;
- }
- if (!o2.has_value()) {
- std::cout << "o2 should not have value" << std::endl;
- retval = false;
- }
- if (o2.value().Value != 4) {
- std::cout << "value of o2 should be 4" << std::endl;
- retval = false;
- }
- o1.reset();
- o2.reset();
- o1.swap(o2);
- if (o1.has_value()) {
- std::cout << "o1 should not have value" << std::endl;
- retval = false;
- }
- if (o2.has_value()) {
- std::cout << "o2 should not have value" << std::endl;
- retval = false;
- }
- expected = {
- { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
- { Event::MOVE_CONSTRUCT, &*o2, &*o1, 4 },
- { Event::DESTRUCT, &*o1, nullptr, 4 },
- { Event::MOVE_CONSTRUCT, &*o1, &*o2, 4 },
- { Event::DESTRUCT, &*o2, nullptr, 4 },
- { Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
- { Event::SWAP, &*o1, &*o2, 5 },
- { Event::DESTRUCT, &*o1, nullptr, 5 },
- { Event::DESTRUCT, &*o2, nullptr, 4 },
- };
- return retval;
- }
- static bool testReset(std::vector<Event>& expected)
- {
- bool retval = true;
- cm::optional<EventLogger> o{ 4 };
- o.reset();
- if (o.has_value()) {
- std::cout << "o should not have value" << std::endl;
- retval = false;
- }
- o.reset();
- expected = {
- { Event::VALUE_CONSTRUCT, &*o, nullptr, 4 },
- { Event::DESTRUCT, &*o, nullptr, 4 },
- };
- return retval;
- }
- 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)
- {
- bool retval = true;
- cm::optional<EventLogger> o{ 4 };
- auto* ostart = &o;
- auto* oend = ostart + 1;
- auto* estart = &o.value();
- auto* eend = estart + 1;
- if (static_cast<void*>(estart) < static_cast<void*>(ostart) ||
- static_cast<void*>(eend) > static_cast<void*>(oend)) {
- std::cout << "value is not within memory range of optional" << std::endl;
- retval = false;
- }
- expected = {
- { Event::VALUE_CONSTRUCT, &*o, nullptr, 4 },
- { Event::DESTRUCT, &*o, nullptr, 4 },
- };
- return retval;
- }
- 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(testSwap);
- DO_EVENT_TEST(testReset);
- DO_EVENT_TEST(testEmplace);
- DO_EVENT_TEST(testMakeOptional);
- DO_EVENT_TEST(testMemoryRange);
- return retval;
- }
|