diff options
Diffstat (limited to '3rdparty/sol2/Optional/test_optional.cpp')
-rw-r--r-- | 3rdparty/sol2/Optional/test_optional.cpp | 1459 |
1 files changed, 1459 insertions, 0 deletions
diff --git a/3rdparty/sol2/Optional/test_optional.cpp b/3rdparty/sol2/Optional/test_optional.cpp new file mode 100644 index 00000000000..8ed356a562b --- /dev/null +++ b/3rdparty/sol2/Optional/test_optional.cpp @@ -0,0 +1,1459 @@ +// Copyright (C) 2011 - 2012 Andrzej Krzemienski. +// +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// The idea and interface is based on Boost.Optional library +// authored by Fernando Luis Cacciola Carballal + +# include "optional.hpp" +# include <vector> +# include <iostream> +# include <functional> +# include <complex> + + + +struct caller { + template <class T> caller(T fun) { fun(); } +}; +# define CAT2(X, Y) X ## Y +# define CAT(X, Y) CAT2(X, Y) +# define TEST(NAME) caller CAT(__VAR, __LINE__) = [] + +enum State +{ + sDefaultConstructed, + sValueCopyConstructed, + sValueMoveConstructed, + sCopyConstructed, + sMoveConstructed, + sMoveAssigned, + sCopyAssigned, + sValueCopyAssigned, + sValueMoveAssigned, + sMovedFrom, + sValueConstructed +}; + +struct OracleVal +{ + State s; + int i; + OracleVal(int i = 0) : s(sValueConstructed), i(i) {} +}; + +struct Oracle +{ + State s; + OracleVal val; + + Oracle() : s(sDefaultConstructed) {} + Oracle(const OracleVal& v) : s(sValueCopyConstructed), val(v) {} + Oracle(OracleVal&& v) : s(sValueMoveConstructed), val(std::move(v)) {v.s = sMovedFrom;} + Oracle(const Oracle& o) : s(sCopyConstructed), val(o.val) {} + Oracle(Oracle&& o) : s(sMoveConstructed), val(std::move(o.val)) {o.s = sMovedFrom;} + + Oracle& operator=(const OracleVal& v) { s = sValueCopyConstructed; val = v; return *this; } + Oracle& operator=(OracleVal&& v) { s = sValueMoveConstructed; val = std::move(v); v.s = sMovedFrom; return *this; } + Oracle& operator=(const Oracle& o) { s = sCopyConstructed; val = o.val; return *this; } + Oracle& operator=(Oracle&& o) { s = sMoveConstructed; val = std::move(o.val); o.s = sMovedFrom; return *this; } +}; + +struct Guard +{ + std::string val; + Guard() : val{} {} + explicit Guard(std::string s, int = 0) : val(s) {} + Guard(const Guard&) = delete; + Guard(Guard&&) = delete; + void operator=(const Guard&) = delete; + void operator=(Guard&&) = delete; +}; + +struct ExplicitStr +{ + std::string s; + explicit ExplicitStr(const char* chp) : s(chp) {}; +}; + +struct Date +{ + int i; + Date() = delete; + Date(int i) : i{i} {}; + Date(Date&& d) : i(d.i) { d.i = 0; } + Date(const Date&) = delete; + Date& operator=(const Date&) = delete; + Date& operator=(Date&& d) { i = d.i; d.i = 0; return *this;}; +}; + +bool operator==( Oracle const& a, Oracle const& b ) { return a.val.i == b.val.i; } +bool operator!=( Oracle const& a, Oracle const& b ) { return a.val.i != b.val.i; } + + +namespace tr2 = std::experimental; + + +TEST(disengaged_ctor) +{ + tr2::optional<int> o1; + assert (!o1); + + tr2::optional<int> o2 = tr2::nullopt; + assert (!o2); + + tr2::optional<int> o3 = o2; + assert (!o3); + + assert (o1 == tr2::nullopt); + assert (o1 == tr2::optional<int>{}); + assert (!o1); + assert (bool(o1) == false); + + assert (o2 == tr2::nullopt); + assert (o2 == tr2::optional<int>{}); + assert (!o2); + assert (bool(o2) == false); + + assert (o3 == tr2::nullopt); + assert (o3 == tr2::optional<int>{}); + assert (!o3); + assert (bool(o3) == false); + + assert (o1 == o2); + assert (o2 == o1); + assert (o1 == o3); + assert (o3 == o1); + assert (o2 == o3); + assert (o3 == o2); +}; + + +TEST(value_ctor) +{ + OracleVal v; + tr2::optional<Oracle> oo1(v); + assert (oo1 != tr2::nullopt); + assert (oo1 != tr2::optional<Oracle>{}); + assert (oo1 == tr2::optional<Oracle>{v}); + assert (!!oo1); + assert (bool(oo1)); + // NA: assert (oo1->s == sValueCopyConstructed); + assert (oo1->s == sMoveConstructed); + assert (v.s == sValueConstructed); + + tr2::optional<Oracle> oo2(std::move(v)); + assert (oo2 != tr2::nullopt); + assert (oo2 != tr2::optional<Oracle>{}); + assert (oo2 == oo1); + assert (!!oo2); + assert (bool(oo2)); + // NA: assert (oo2->s == sValueMoveConstructed); + assert (oo2->s == sMoveConstructed); + assert (v.s == sMovedFrom); + + { + OracleVal v; + tr2::optional<Oracle> oo1{tr2::in_place, v}; + assert (oo1 != tr2::nullopt); + assert (oo1 != tr2::optional<Oracle>{}); + assert (oo1 == tr2::optional<Oracle>{v}); + assert (!!oo1); + assert (bool(oo1)); + assert (oo1->s == sValueCopyConstructed); + assert (v.s == sValueConstructed); + + tr2::optional<Oracle> oo2{tr2::in_place, std::move(v)}; + assert (oo2 != tr2::nullopt); + assert (oo2 != tr2::optional<Oracle>{}); + assert (oo2 == oo1); + assert (!!oo2); + assert (bool(oo2)); + assert (oo2->s == sValueMoveConstructed); + assert (v.s == sMovedFrom); + } +}; + + +TEST(assignment) +{ + tr2::optional<int> oi; + oi = tr2::optional<int>{1}; + assert (*oi == 1); + + oi = tr2::nullopt; + assert (!oi); + + oi = 2; + assert (*oi == 2); + + oi = {}; + assert (!oi); +}; + + +template <class T> +struct MoveAware +{ + T val; + bool moved; + MoveAware(T val) : val(val), moved(false) {} + MoveAware(MoveAware const&) = delete; + MoveAware(MoveAware&& rhs) : val(rhs.val), moved(rhs.moved) { + rhs.moved = true; + } + MoveAware& operator=(MoveAware const&) = delete; + MoveAware& operator=(MoveAware&& rhs) { + val = (rhs.val); + moved = (rhs.moved); + rhs.moved = true; + return *this; + } +}; + +TEST(moved_from_state) +{ + // first, test mock: + MoveAware<int> i{1}, j{2}; + assert (i.val == 1); + assert (!i.moved); + assert (j.val == 2); + assert (!j.moved); + + MoveAware<int> k = std::move(i); + assert (k.val == 1); + assert (!k.moved); + assert (i.val == 1); + assert (i.moved); + + k = std::move(j); + assert (k.val == 2); + assert (!k.moved); + assert (j.val == 2); + assert (j.moved); + + // now, test optional + tr2::optional<MoveAware<int>> oi{1}, oj{2}; + assert (oi); + assert (!oi->moved); + assert (oj); + assert (!oj->moved); + + tr2::optional<MoveAware<int>> ok = std::move(oi); + assert (ok); + assert (!ok->moved); + assert (oi); + assert (oi->moved); + + ok = std::move(oj); + assert (ok); + assert (!ok->moved); + assert (oj); + assert (oj->moved); +}; + + +TEST(copy_move_ctor_optional_int) +{ + tr2::optional<int> oi; + tr2::optional<int> oj = oi; + + assert (!oj); + assert (oj == oi); + assert (oj == tr2::nullopt); + assert (!bool(oj)); + + oi = 1; + tr2::optional<int> ok = oi; + assert (!!ok); + assert (bool(ok)); + assert (ok == oi); + assert (ok != oj); + assert (*ok == 1); + + tr2::optional<int> ol = std::move(oi); + assert (!!ol); + assert (bool(ol)); + assert (ol == oi); + assert (ol != oj); + assert (*ol == 1); +}; + + +TEST(optional_optional) +{ + tr2::optional<tr2::optional<int>> oi1 = tr2::nullopt; + assert (oi1 == tr2::nullopt); + assert (!oi1); + + { + tr2::optional<tr2::optional<int>> oi2 {tr2::in_place}; + assert (oi2 != tr2::nullopt); + assert (bool(oi2)); + assert (*oi2 == tr2::nullopt); + //assert (!(*oi2)); + //std::cout << typeid(**oi2).name() << std::endl; + } + + { + tr2::optional<tr2::optional<int>> oi2 {tr2::in_place, tr2::nullopt}; + assert (oi2 != tr2::nullopt); + assert (bool(oi2)); + assert (*oi2 == tr2::nullopt); + assert (!*oi2); + } + + { + tr2::optional<tr2::optional<int>> oi2 {tr2::optional<int>{}}; + assert (oi2 != tr2::nullopt); + assert (bool(oi2)); + assert (*oi2 == tr2::nullopt); + assert (!*oi2); + } + + tr2::optional<int> oi; + auto ooi = tr2::make_optional(oi); + static_assert( std::is_same<tr2::optional<tr2::optional<int>>, decltype(ooi)>::value, ""); + +}; + +TEST(example_guard) +{ + using namespace tr2; + //FAILS: optional<Guard> ogx(Guard("res1")); + //FAILS: optional<Guard> ogx = "res1"; + //FAILS: optional<Guard> ogx("res1"); + optional<Guard> oga; // Guard is non-copyable (and non-moveable) + optional<Guard> ogb(in_place, "res1"); // initialzes the contained value with "res1" + assert (bool(ogb)); + assert (ogb->val == "res1"); + + optional<Guard> ogc(in_place); // default-constructs the contained value + assert (bool(ogc)); + assert (ogc->val == ""); + + oga.emplace("res1"); // initialzes the contained value with "res1" + assert (bool(oga)); + assert (oga->val == "res1"); + + oga.emplace(); // destroys the contained value and + // default-constructs the new one + assert (bool(oga)); + assert (oga->val == ""); + + oga = nullopt; // OK: make disengaged the optional Guard + assert (!(oga)); + //FAILS: ogb = {}; // ERROR: Guard is not Moveable +}; + + +void process(){} +void process(int ){} +void processNil(){} + + +TEST(example1) +{ + using namespace tr2; + optional<int> oi; // create disengaged object + optional<int> oj = nullopt; // alternative syntax + oi = oj; // assign disengaged object + optional<int> ok = oj; // ok is disengaged + + if (oi) assert(false); // 'if oi is engaged...' + if (!oi) assert(true); // 'if oi is disengaged...' + + if (oi != nullopt) assert(false); // 'if oi is engaged...' + if (oi == nullopt) assert(true); // 'if oi is disengaged...' + + assert(oi == ok); // two disengaged optionals compare equal + + /////////////////////////////////////////////////////////////////////////// + optional<int> ol{1}; // ol is engaged; its contained value is 1 + ok = 2; // ok becomes engaged; its contained value is 2 + oj = ol; // oj becomes engaged; its contained value is 1 + + assert(oi != ol); // disengaged != engaged + assert(ok != ol); // different contained values + assert(oj == ol); // same contained value + assert(oi < ol); // disengaged < engaged + assert(ol < ok); // less by contained value + + ///////////////////////////////////////////////////////////////////////////// + optional<int> om{1}; // om is engaged; its contained value is 1 + optional<int> on = om; // on is engaged; its contained value is 1 + om = 2; // om is engaged; its contained value is 2 + assert (on != om); // on still contains 3. They are not pointers + + ///////////////////////////////////////////////////////////////////////////// + int i = *ol; // i obtains the value contained in ol + assert (i == 1); + *ol = 9; // the object contained in ol becomes 9 + assert(*ol == 9); + assert(ol == make_optional(9)); + + /////////////////////////////////// + int p = 1; + optional<int> op = p; + assert(*op == 1); + p = 2; + assert(*op == 1); // value contained in op is separated from p + + //////////////////////////////// + if (ol) + process(*ol); // use contained value if present + else + process(); // proceed without contained value + + if (!om) + processNil(); + else + process(*om); + + ///////////////////////////////////////// + process(ol.value_or(0)); // use 0 if ol is disengaged + + //////////////////////////////////////////// + ok = nullopt; // if ok was engaged calls T's dtor + oj = {}; // assigns a temporary disengaged optional +}; + + +TEST(example_guard) +{ + using std::experimental::optional; + const optional<int> c = 4; + int i = *c; // i becomes 4 + assert (i == 4); + // FAILS: *c = i; // ERROR: cannot assign to const int& +}; + + +TEST(example_ref) +{ + using namespace std::experimental; + int i = 1; + int j = 2; + optional<int&> ora; // disengaged optional reference to int + optional<int&> orb = i; // contained reference refers to object i + + *orb = 3; // i becomes 3 + // FAILS: ora = j; // ERROR: optional refs do not have assignment from T + // FAILS: ora = {j}; // ERROR: optional refs do not have copy/move assignment + // FAILS: ora = orb; // ERROR: no copy/move assignment + ora.emplace(j); // OK: contained reference refers to object j + ora.emplace(i); // OK: contained reference now refers to object i + + ora = nullopt; // OK: ora becomes disengaged +}; + + +template <typename T> +T getValue( tr2::optional<T> newVal = tr2::nullopt, tr2::optional<T&> storeHere = tr2::nullopt ) +{ + T cached{}; + + if (newVal) { + cached = *newVal; + + if (storeHere) { + *storeHere = *newVal; // LEGAL: assigning T to T + } + } + return cached; +} + +TEST(example_optional_arg) +{ + int iii = 0; + iii = getValue<int>(iii, iii); + iii = getValue<int>(iii); + iii = getValue<int>(); + + { + using namespace std::experimental; + optional<Guard> grd1{in_place, "res1", 1}; // guard 1 initialized + optional<Guard> grd2; + + grd2.emplace("res2", 2); // guard 2 initialized + grd1 = nullopt; // guard 1 released + + } // guard 2 released (in dtor) +}; + + +std::tuple<Date, Date, Date> getStartMidEnd() { return std::tuple<Date, Date, Date>{Date{1}, Date{2}, Date{3}}; } +void run(Date const&, Date const&, Date const&) {} + +TEST(example_date) +{ + using namespace std::experimental; + optional<Date> start, mid, end; // Date doesn't have default ctor (no good default date) + + std::tie(start, mid, end) = getStartMidEnd(); + run(*start, *mid, *end); +}; + + +std::experimental::optional<char> readNextChar(){ return{}; } + +void run(std::experimental::optional<std::string>) {} +void run(std::complex<double>) {} + + +template <class T> +void assign_norebind(tr2::optional<T&>& optref, T& obj) +{ + if (optref) *optref = obj; + else optref.emplace(obj); +} + +template <typename T> void unused(T&&) {} + +TEST(example_conceptual_model) +{ + using namespace std::experimental; + + optional<int> oi = 0; + optional<int> oj = 1; + optional<int> ok = nullopt; + + oi = 1; + oj = nullopt; + ok = 0; + + unused(oi == nullopt); + unused(oj == 0); + unused(ok == 1); +}; + +TEST(example_rationale) +{ + using namespace std::experimental; + if (optional<char> ch = readNextChar()) { + unused(ch); + // ... + } + + ////////////////////////////////// + optional<int> opt1 = nullopt; + optional<int> opt2 = {}; + + opt1 = nullopt; + opt2 = {}; + + if (opt1 == nullopt) {} + if (!opt2) {} + if (opt2 == optional<int>{}) {} + + + + //////////////////////////////// + + run(nullopt); // pick the second overload + // FAILS: run({}); // ambiguous + + if (opt1 == nullopt) {} // fine + // FAILS: if (opt2 == {}) {} // ilegal + + //////////////////////////////// + assert (optional<unsigned>{} < optional<unsigned>{0}); + assert (optional<unsigned>{0} < optional<unsigned>{1}); + assert (!(optional<unsigned>{} < optional<unsigned>{}) ); + assert (!(optional<unsigned>{1} < optional<unsigned>{1})); + + assert (optional<unsigned>{} != optional<unsigned>{0}); + assert (optional<unsigned>{0} != optional<unsigned>{1}); + assert (optional<unsigned>{} == optional<unsigned>{} ); + assert (optional<unsigned>{0} == optional<unsigned>{0}); + + ///////////////////////////////// + optional<int> o; + o = make_optional(1); // copy/move assignment + o = 1; // assignment from T + o.emplace(1); // emplacement + + //////////////////////////////////// + int isas = 0, i = 9; + optional<int&> asas = i; + assign_norebind(asas, isas); + + ///////////////////////////////////// + ////tr2::optional<std::vector<int>> ov2 = {2, 3}; + ////assert (bool(ov2)); + ////assert ((*ov2)[1] == 3); + //// + //////////////////////////////// + ////std::vector<int> v = {1, 2, 4, 8}; + ////optional<std::vector<int>> ov = {1, 2, 4, 8}; + + ////assert (v == *ov); + //// + ////ov = {1, 2, 4, 8}; + + ////std::allocator<int> a; + ////optional<std::vector<int>> ou { in_place, {1, 2, 4, 8}, a }; + + ////assert (ou == ov); + + ////////////////////////////// + // inconvenient syntax: + { + + tr2::optional<std::vector<int>> ov2{tr2::in_place, {2, 3}}; + + assert (bool(ov2)); + assert ((*ov2)[1] == 3); + + //////////////////////////// + + std::vector<int> v = {1, 2, 4, 8}; + optional<std::vector<int>> ov{tr2::in_place, {1, 2, 4, 8}}; + + assert (v == *ov); + + ov.emplace({1, 2, 4, 8}); +/* + std::allocator<int> a; + optional<std::vector<int>> ou { in_place, {1, 2, 4, 8}, a }; + + assert (ou == ov); +*/ + } + + ///////////////////////////////// + { + typedef int T; + optional<optional<T>> ot {in_place}; + optional<optional<T>> ou {in_place, nullopt}; + optional<optional<T>> ov {optional<T>{}}; + + optional<int> oi; + auto ooi = make_optional(oi); + static_assert( std::is_same<optional<optional<int>>, decltype(ooi)>::value, ""); + } +}; + + +bool fun(std::string , std::experimental::optional<int> oi = std::experimental::nullopt) +{ + return bool(oi); +} + +TEST(example_converting_ctor) +{ + using namespace std::experimental; + + assert (true == fun("dog", 2)); + assert (false == fun("dog")); + assert (false == fun("dog", nullopt)); // just to be explicit +}; + + +TEST(bad_comparison) +{ + tr2::optional<int> oi, oj; + int i; + bool b = (oi == oj); + b = (oi >= i); + b = (oi == i); + unused(b); +}; + + +//// NOT APPLICABLE ANYMORE +////TEST(perfect_ctor) +////{ +//// //tr2::optional<std::string> ois = "OS"; +//// assert (*ois == "OS"); +//// +//// // FAILS: tr2::optional<ExplicitStr> oes = "OS"; +//// tr2::optional<ExplicitStr> oes{"OS"}; +//// assert (oes->s == "OS"); +////}; + +TEST(value_or) +{ + tr2::optional<int> oi = 1; + int i = oi.value_or(0); + assert (i == 1); + + oi = tr2::nullopt; + assert (oi.value_or(3) == 3); + + tr2::optional<std::string> os{"AAA"}; + assert (os.value_or("BBB") == "AAA"); + os = {}; + assert (os.value_or("BBB") == "BBB"); +}; + +TEST(mixed_order) +{ + using namespace std::experimental; + + optional<int> oN {nullopt}; + optional<int> o0 {0}; + optional<int> o1 {1}; + + assert ( (oN < 0)); + assert ( (oN < 1)); + assert (!(o0 < 0)); + assert ( (o0 < 1)); + assert (!(o1 < 0)); + assert (!(o1 < 1)); + + assert (!(oN >= 0)); + assert (!(oN >= 1)); + assert ( (o0 >= 0)); + assert (!(o0 >= 1)); + assert ( (o1 >= 0)); + assert ( (o1 >= 1)); + + assert (!(oN > 0)); + assert (!(oN > 1)); + assert (!(o0 > 0)); + assert (!(o0 > 1)); + assert ( (o1 > 0)); + assert (!(o1 > 1)); + + assert ( (oN <= 0)); + assert ( (oN <= 1)); + assert ( (o0 <= 0)); + assert ( (o0 <= 1)); + assert (!(o1 <= 0)); + assert ( (o1 <= 1)); + + assert ( (0 > oN)); + assert ( (1 > oN)); + assert (!(0 > o0)); + assert ( (1 > o0)); + assert (!(0 > o1)); + assert (!(1 > o1)); + + assert (!(0 <= oN)); + assert (!(1 <= oN)); + assert ( (0 <= o0)); + assert (!(1 <= o0)); + assert ( (0 <= o1)); + assert ( (1 <= o1)); + + assert (!(0 < oN)); + assert (!(1 < oN)); + assert (!(0 < o0)); + assert (!(1 < o0)); + assert ( (0 < o1)); + assert (!(1 < o1)); + + assert ( (0 >= oN)); + assert ( (1 >= oN)); + assert ( (0 >= o0)); + assert ( (1 >= o0)); + assert (!(0 >= o1)); + assert ( (1 >= o1)); +}; + +struct BadRelops +{ + int i; +}; + +constexpr bool operator<(BadRelops a, BadRelops b) { return a.i < b.i; } +constexpr bool operator>(BadRelops a, BadRelops b) { return a.i < b.i; } // intentional error! + +TEST(bad_relops) +{ + using namespace std::experimental; + BadRelops a{1}, b{2}; + assert (a < b); + assert (a > b); + + optional<BadRelops> oa = a, ob = b; + assert (oa < ob); + assert (!(oa > ob)); + + assert (oa < b); + assert (oa > b); + + optional<BadRelops&> ra = a, rb = b; + assert (ra < rb); + assert (!(ra > rb)); + + assert (ra < b); + assert (ra > b); +}; + + +TEST(mixed_equality) +{ + using namespace std::experimental; + + assert (make_optional(0) == 0); + assert (make_optional(1) == 1); + assert (make_optional(0) != 1); + assert (make_optional(1) != 0); + + optional<int> oN {nullopt}; + optional<int> o0 {0}; + optional<int> o1 {1}; + + assert (o0 == 0); + assert ( 0 == o0); + assert (o1 == 1); + assert ( 1 == o1); + assert (o1 != 0); + assert ( 0 != o1); + assert (o0 != 1); + assert ( 1 != o0); + + assert ( 1 != oN); + assert ( 0 != oN); + assert (oN != 1); + assert (oN != 0); + assert (!( 1 == oN)); + assert (!( 0 == oN)); + assert (!(oN == 1)); + assert (!(oN == 0)); + + std::string cat{"cat"}, dog{"dog"}; + optional<std::string> oNil{}, oDog{"dog"}, oCat{"cat"}; + + assert (oCat == cat); + assert ( cat == oCat); + assert (oDog == dog); + assert ( dog == oDog); + assert (oDog != cat); + assert ( cat != oDog); + assert (oCat != dog); + assert ( dog != oCat); + + assert ( dog != oNil); + assert ( cat != oNil); + assert (oNil != dog); + assert (oNil != cat); + assert (!( dog == oNil)); + assert (!( cat == oNil)); + assert (!(oNil == dog)); + assert (!(oNil == cat)); +}; + +TEST(const_propagation) +{ + using namespace std::experimental; + + optional<int> mmi{0}; + static_assert(std::is_same<decltype(*mmi), int&>::value, "WTF"); + + const optional<int> cmi{0}; + static_assert(std::is_same<decltype(*cmi), const int&>::value, "WTF"); + + optional<const int> mci{0}; + static_assert(std::is_same<decltype(*mci), const int&>::value, "WTF"); + + optional<const int> cci{0}; + static_assert(std::is_same<decltype(*cci), const int&>::value, "WTF"); +}; + + +static_assert(std::is_base_of<std::logic_error, std::experimental::bad_optional_access>::value, ""); + +TEST(safe_value) +{ + using namespace std::experimental; + + try { + optional<int> ovN{}, ov1{1}; + + int& r1 = ov1.value(); + assert (r1 == 1); + + try { + ovN.value(); + assert (false); + } + catch (bad_optional_access const&) { + } + + { // ref variant + int i1 = 1; + optional<int&> orN{}, or1{i1}; + + int& r2 = or1.value(); + assert (r2 == 1); + + try { + orN.value(); + assert (false); + } + catch (bad_optional_access const&) { + } + } + } + catch(...) { + assert (false); + } +}; + +TEST(optional_ref) +{ + using namespace tr2; + // FAILS: optional<int&&> orr; + // FAILS: optional<nullopt_t&> on; + int i = 8; + optional<int&> ori; + assert (!ori); + ori.emplace(i); + assert (bool(ori)); + assert (*ori == 8); + assert (&*ori == &i); + *ori = 9; + assert (i == 9); + + // FAILS: int& ir = ori.value_or(i); + int ii = ori.value_or(i); + assert (ii == 9); + ii = 7; + assert (*ori == 9); + + int j = 22; + auto&& oj = make_optional(std::ref(j)); + *oj = 23; + assert (&*oj == &j); + assert (j == 23); +}; + +TEST(optional_ref_const_propagation) +{ + using namespace std::experimental; + + int i = 9; + const optional<int&> mi = i; + int& r = *mi; + optional<const int&> ci = i; + static_assert(std::is_same<decltype(*mi), int&>::value, "WTF"); + static_assert(std::is_same<decltype(*ci), const int&>::value, "WTF"); + + unused(r); +}; + +TEST(optional_ref_assign) +{ + using namespace std::experimental; + + int i = 9; + optional<int&> ori = i; + + int j = 1; + ori = optional<int&>{j}; + ori = {j}; + // FAILS: ori = j; + + optional<int&> orx = ori; + ori = orx; + + optional<int&> orj = j; + + assert (ori); + assert (*ori == 1); + assert (ori == orj); + assert (i == 9); + + *ori = 2; + assert (*ori == 2); + assert (ori == 2); + assert (2 == ori); + assert (ori != 3); + + assert (ori == orj); + assert (j == 2); + assert (i == 9); + + ori = {}; + assert (!ori); + assert (ori != orj); + assert (j == 2); + assert (i == 9); +}; + + +TEST(optional_ref_swap) +{ + using namespace std::experimental; + int i = 0; + int j = 1; + optional<int&> oi = i; + optional<int&> oj = j; + + assert (&*oi == &i); + assert (&*oj == &j); + + swap(oi, oj); + assert (&*oi == &j); + assert (&*oj == &i); +}; + +TEST(optional_initialization) +{ + using namespace tr2; + using std::string; + string s = "STR"; + + optional<string> os{s}; + optional<string> ot = s; + optional<string> ou{"STR"}; + optional<string> ov = string{"STR"}; + +}; + +#include <unordered_set> + +TEST(optional_hashing) +{ + using namespace tr2; + using std::string; + + std::hash<int> hi; + std::hash<optional<int>> hoi; + std::hash<string> hs; + std::hash<optional<string>> hos; + + assert (hi(0) == hoi(optional<int>{0})); + assert (hi(1) == hoi(optional<int>{1})); + assert (hi(3198) == hoi(optional<int>{3198})); + + assert (hs("") == hos(optional<string>{""})); + assert (hs("0") == hos(optional<string>{"0"})); + assert (hs("Qa1#") == hos(optional<string>{"Qa1#"})); + + std::unordered_set<optional<string>> set; + assert(set.find({"Qa1#"}) == set.end()); + + set.insert({"0"}); + assert(set.find({"Qa1#"}) == set.end()); + + set.insert({"Qa1#"}); + assert(set.find({"Qa1#"}) != set.end()); +}; + + +// optional_ref_emulation +template <class T> +struct generic +{ + typedef T type; +}; + +template <class U> +struct generic<U&> +{ + typedef std::reference_wrapper<U> type; +}; + +template <class T> +using Generic = typename generic<T>::type; + +template <class X> +bool generic_fun() +{ + std::experimental::optional<Generic<X>> op; + return bool(op); +} + +TEST(optional_ref_emulation) +{ + using namespace std::experimental; + optional<Generic<int>> oi = 1; + assert (*oi == 1); + + int i = 8; + int j = 4; + optional<Generic<int&>> ori {i}; + assert (*ori == 8); + assert ((void*)&*ori != (void*)&i); // !DIFFERENT THAN optional<T&> + + *ori = j; + assert (*ori == 4); +}; + + +# if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 +TEST(moved_on_value_or) +{ + using namespace tr2; + optional<Oracle> oo{in_place}; + + assert (oo); + assert (oo->s == sDefaultConstructed); + + Oracle o = std::move(oo).value_or( Oracle{OracleVal{}} ); + assert (oo); + assert (oo->s == sMovedFrom); + assert (o.s == sMoveConstructed); + + optional<MoveAware<int>> om {in_place, 1}; + assert (om); + assert (om->moved == false); + + /*MoveAware<int> m =*/ std::move(om).value_or( MoveAware<int>{1} ); + assert (om); + assert (om->moved == true); + +# if OPTIONAL_HAS_MOVE_ACCESSORS == 1 + { + Date d = optional<Date>{in_place, 1}.value(); + assert (d.i); // to silence compiler warning + + Date d2 = *optional<Date>{in_place, 1}; + assert (d2.i); // to silence compiler warning + } +# endif +}; +# endif + + +TEST(optional_ref_hashing) +{ + using namespace tr2; + using std::string; + + std::hash<int> hi; + std::hash<optional<int&>> hoi; + std::hash<string> hs; + std::hash<optional<string&>> hos; + + int i0 = 0; + int i1 = 1; + assert (hi(0) == hoi(optional<int&>{i0})); + assert (hi(1) == hoi(optional<int&>{i1})); + + string s{""}; + string s0{"0"}; + string sCAT{"CAT"}; + assert (hs("") == hos(optional<string&>{s})); + assert (hs("0") == hos(optional<string&>{s0})); + assert (hs("CAT") == hos(optional<string&>{sCAT})); + + std::unordered_set<optional<string&>> set; + assert(set.find({sCAT}) == set.end()); + + set.insert({s0}); + assert(set.find({sCAT}) == set.end()); + + set.insert({sCAT}); + assert(set.find({sCAT}) != set.end()); +}; + +struct Combined +{ + int m = 0; + int n = 1; + + constexpr Combined() : m{5}, n{6} {} + constexpr Combined(int m, int n) : m{m}, n{n} {} +}; + +struct Nasty +{ + int m = 0; + int n = 1; + + constexpr Nasty() : m{5}, n{6} {} + constexpr Nasty(int m, int n) : m{m}, n{n} {} + + int operator&() { return n; } + int operator&() const { return n; } +}; + +TEST(arrow_operator) +{ + using namespace std::experimental; + + optional<Combined> oc1{in_place, 1, 2}; + assert (oc1); + assert (oc1->m == 1); + assert (oc1->n == 2); + + optional<Nasty> on{in_place, 1, 2}; + assert (on); + assert (on->m == 1); + assert (on->n == 2); +}; + +TEST(arrow_wit_optional_ref) +{ + using namespace std::experimental; + + Combined c{1, 2}; + optional<Combined&> oc = c; + assert (oc); + assert (oc->m == 1); + assert (oc->n == 2); + + Nasty n{1, 2}; + Nasty m{3, 4}; + Nasty p{5, 6}; + + optional<Nasty&> on{n}; + assert (on); + assert (on->m == 1); + assert (on->n == 2); + + on = {m}; + assert (on); + assert (on->m == 3); + assert (on->n == 4); + + on.emplace(p); + assert (on); + assert (on->m == 5); + assert (on->n == 6); + + optional<Nasty&> om{in_place, n}; + assert (om); + assert (om->m == 1); + assert (om->n == 2); +}; + +TEST(no_dangling_reference_in_value) +{ + // this mostly tests compiler warnings + using namespace std::experimental; + optional<int> oi {2}; + unused (oi.value()); + const optional<int> coi {3}; + unused (coi.value()); +}; + +struct CountedObject +{ + static int _counter; + bool _throw; + CountedObject(bool b) : _throw(b) { ++_counter; } + CountedObject(CountedObject const& rhs) : _throw(rhs._throw) { if (_throw) throw int(); } + ~CountedObject() { --_counter; } +}; + +int CountedObject::_counter = 0; + +TEST(exception_safety) +{ + using namespace std::experimental; + try { + optional<CountedObject> oo(in_place, true); // throw + optional<CountedObject> o1(oo); + } + catch(...) + { + // + } + assert(CountedObject::_counter == 0); + + try { + optional<CountedObject> oo(in_place, true); // throw + optional<CountedObject> o1(std::move(oo)); // now move + } + catch(...) + { + // + } + assert(CountedObject::_counter == 0); +}; + +//// constexpr tests + +// these 4 classes have different noexcept signatures in move operations +struct NothrowBoth { + NothrowBoth(NothrowBoth&&) noexcept(true) {}; + void operator=(NothrowBoth&&) noexcept(true) {}; +}; +struct NothrowCtor { + NothrowCtor(NothrowCtor&&) noexcept(true) {}; + void operator=(NothrowCtor&&) noexcept(false) {}; +}; +struct NothrowAssign { + NothrowAssign(NothrowAssign&&) noexcept(false) {}; + void operator=(NothrowAssign&&) noexcept(true) {}; +}; +struct NothrowNone { + NothrowNone(NothrowNone&&) noexcept(false) {}; + void operator=(NothrowNone&&) noexcept(false) {}; +}; + +void test_noexcept() +{ + { + tr2::optional<NothrowBoth> b1, b2; + static_assert(noexcept(tr2::optional<NothrowBoth>{tr2::constexpr_move(b1)}), "bad noexcept!"); + static_assert(noexcept(b1 = tr2::constexpr_move(b2)), "bad noexcept!"); + } + { + tr2::optional<NothrowCtor> c1, c2; + static_assert(noexcept(tr2::optional<NothrowCtor>{tr2::constexpr_move(c1)}), "bad noexcept!"); + static_assert(!noexcept(c1 = tr2::constexpr_move(c2)), "bad noexcept!"); + } + { + tr2::optional<NothrowAssign> a1, a2; + static_assert(!noexcept(tr2::optional<NothrowAssign>{tr2::constexpr_move(a1)}), "bad noexcept!"); + static_assert(!noexcept(a1 = tr2::constexpr_move(a2)), "bad noexcept!"); + } + { + tr2::optional<NothrowNone> n1, n2; + static_assert(!noexcept(tr2::optional<NothrowNone>{tr2::constexpr_move(n1)}), "bad noexcept!"); + static_assert(!noexcept(n1 = tr2::constexpr_move(n2)), "bad noexcept!"); + } +} + + +void constexpr_test_disengaged() +{ + constexpr tr2::optional<int> g0{}; + constexpr tr2::optional<int> g1{tr2::nullopt}; + static_assert( !g0, "initialized!" ); + static_assert( !g1, "initialized!" ); + + static_assert( bool(g1) == bool(g0), "ne!" ); + + static_assert( g1 == g0, "ne!" ); + static_assert( !(g1 != g0), "ne!" ); + static_assert( g1 >= g0, "ne!" ); + static_assert( !(g1 > g0), "ne!" ); + static_assert( g1 <= g0, "ne!" ); + static_assert( !(g1 <g0), "ne!" ); + + static_assert( g1 == tr2::nullopt, "!" ); + static_assert( !(g1 != tr2::nullopt), "!" ); + static_assert( g1 <= tr2::nullopt, "!" ); + static_assert( !(g1 < tr2::nullopt), "!" ); + static_assert( g1 >= tr2::nullopt, "!" ); + static_assert( !(g1 > tr2::nullopt), "!" ); + + static_assert( (tr2::nullopt == g0), "!" ); + static_assert( !(tr2::nullopt != g0), "!" ); + static_assert( (tr2::nullopt >= g0), "!" ); + static_assert( !(tr2::nullopt > g0), "!" ); + static_assert( (tr2::nullopt <= g0), "!" ); + static_assert( !(tr2::nullopt < g0), "!" ); + + static_assert( (g1 != tr2::optional<int>(1)), "!" ); + static_assert( !(g1 == tr2::optional<int>(1)), "!" ); + static_assert( (g1 < tr2::optional<int>(1)), "!" ); + static_assert( (g1 <= tr2::optional<int>(1)), "!" ); + static_assert( !(g1 > tr2::optional<int>(1)), "!" ); + static_assert( !(g1 > tr2::optional<int>(1)), "!" ); +} + + +constexpr tr2::optional<int> g0{}; +constexpr tr2::optional<int> g2{2}; +static_assert( g2, "not initialized!" ); +static_assert( *g2 == 2, "not 2!" ); +static_assert( g2 == tr2::optional<int>(2), "not 2!" ); +static_assert( g2 != g0, "eq!" ); + +# if OPTIONAL_HAS_MOVE_ACCESSORS == 1 +static_assert( *tr2::optional<int>{3} == 3, "WTF!" ); +static_assert( tr2::optional<int>{3}.value() == 3, "WTF!" ); +static_assert( tr2::optional<int>{3}.value_or(1) == 3, "WTF!" ); +static_assert( tr2::optional<int>{}.value_or(4) == 4, "WTF!" ); +# endif + +constexpr tr2::optional<Combined> gc0{tr2::in_place}; +static_assert(gc0->n == 6, "WTF!"); + +// optional refs +int gi = 0; +constexpr tr2::optional<int&> gori = gi; +constexpr tr2::optional<int&> gorn{}; +constexpr int& gri = *gori; +static_assert(gori, "WTF"); +static_assert(!gorn, "WTF"); +static_assert(gori != tr2::nullopt, "WTF"); +static_assert(gorn == tr2::nullopt, "WTF"); +static_assert(&gri == &*gori, "WTF"); + +constexpr int gci = 1; +constexpr tr2::optional<int const&> gorci = gci; +constexpr tr2::optional<int const&> gorcn{}; + +static_assert(gorcn < gorci, "WTF"); +static_assert(gorcn <= gorci, "WTF"); +static_assert(gorci == gorci, "WTF"); +static_assert(*gorci == 1, "WTF"); +static_assert(gorci == gci, "WTF"); + +namespace constexpr_optional_ref_and_arrow +{ + using namespace std::experimental; + constexpr Combined c{1, 2}; + constexpr optional<Combined const&> oc = c; + static_assert(oc, "WTF!"); + static_assert(oc->m == 1, "WTF!"); + static_assert(oc->n == 2, "WTF!"); +} + +#if OPTIONAL_HAS_CONSTEXPR_INIT_LIST + +namespace InitList +{ + using namespace std::experimental; + + struct ConstInitLister + { + template <typename T> + constexpr ConstInitLister(std::initializer_list<T> il) : len (il.size()) {} + size_t len; + }; + + constexpr ConstInitLister CIL {2, 3, 4}; + static_assert(CIL.len == 3, "WTF!"); + + constexpr optional<ConstInitLister> oil {in_place, {4, 5, 6, 7}}; + static_assert(oil, "WTF!"); + static_assert(oil->len == 4, "WTF!"); +} + +#endif // OPTIONAL_HAS_CONSTEXPR_INIT_LIST + +// end constexpr tests + + +#include <string> + + +struct VEC +{ + std::vector<int> v; + template <typename... X> + VEC( X&&...x) : v(std::forward<X>(x)...) {} + + template <typename U, typename... X> + VEC(std::initializer_list<U> il, X&&...x) : v(il, std::forward<X>(x)...) {} +}; + + + +int main() { + tr2::optional<int> oi = 1; + assert (bool(oi)); + oi.operator=({}); + assert (!oi); + + VEC v = {5, 6}; + + if (OPTIONAL_HAS_THIS_RVALUE_REFS) + std::cout << "Optional has rvalue references for *this" << std::endl; + else + std::cout << "Optional doesn't have rvalue references for *this" << std::endl; + + if (OPTIONAL_HAS_CONSTEXPR_INIT_LIST) + std::cout << "Optional has constexpr initializer_list" << std::endl; + else + std::cout << "Optional doesn't have constexpr initializer_list" << std::endl; + + if (OPTIONAL_HAS_MOVE_ACCESSORS) + std::cout << "Optional has constexpr move accessors" << std::endl; + else + std::cout << "Optional doesn't have constexpr move accessors" << std::endl; +} + |