From 2b7f56003f27e04868bba2052e5395f68a5ad817 Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Tue, 30 Sep 2014 16:20:07 -0700 Subject: Implement Option and Borrowed --- src/compat/borrow.cpp | 26 +++ src/compat/borrow.hpp | 101 ++++++++++++ src/compat/fwd.hpp | 9 + src/compat/option.cpp | 26 +++ src/compat/option.hpp | 376 ++++++++++++++++++++++++++++++++++++++++++ src/compat/option_test.cpp | 399 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 937 insertions(+) create mode 100644 src/compat/borrow.cpp create mode 100644 src/compat/borrow.hpp create mode 100644 src/compat/option.cpp create mode 100644 src/compat/option.hpp create mode 100644 src/compat/option_test.cpp (limited to 'src') diff --git a/src/compat/borrow.cpp b/src/compat/borrow.cpp new file mode 100644 index 0000000..aac1c0c --- /dev/null +++ b/src/compat/borrow.cpp @@ -0,0 +1,26 @@ +#include "borrow.hpp" +// borrow.cpp - a non-null, unowned, pointer +// +// Copyright © 2012-2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../poison.hpp" + + +namespace tmwa +{ +} // namespace tmwa diff --git a/src/compat/borrow.hpp b/src/compat/borrow.hpp new file mode 100644 index 0000000..8b23d7f --- /dev/null +++ b/src/compat/borrow.hpp @@ -0,0 +1,101 @@ +#pragma once +// borrow.hpp - a non-null, unowned, pointer +// +// Copyright © 2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "fwd.hpp" + +#include + +#include + +#include "option.hpp" + +// unit tests currention in option_test.cpp + +namespace tmwa +{ + // TODO see if const-by-default is a thing + template + class Borrowed + { + T *stupid; + public: + explicit + Borrowed(T *p) : stupid(p) + { + if (!p) abort(); + } + + T& operator *() const + { + return *stupid; + } + + T *operator ->() const + { + return stupid; + } + + template + Borrowed downcast_to() const + { + static_assert(std::is_base_of::value, "base check"); + static_assert(!std::is_same::value, "same check"); + return Borrowed(static_cast(stupid)); + } + + template + Borrowed upcast_to() const + { + static_assert(std::is_base_of::value, "base check"); + static_assert(!std::is_same::value, "same check"); + return Borrowed(stupid); + } + }; + + namespace option + { + template + class OptionRepr> + { + T *stupider; + public: + void set_none() { stupider = nullptr; } + void post_set_some() {} + bool is_some() const { return stupider != nullptr; } + Borrowed *ptr() { return reinterpret_cast *>(&stupider); } + const Borrowed *ptr() const { return reinterpret_cast *>(&stupider); } + }; + } + + template + using P = Borrowed; + + template + Borrowed borrow(T& ref) + { + return Borrowed(&ref); + } + + template + T *as_raw_pointer(Option> ptr) + { + return &*TRY_UNWRAP(ptr, return nullptr); + } +} // namespace tmwa diff --git a/src/compat/fwd.hpp b/src/compat/fwd.hpp index 45f3c24..7e2be28 100644 --- a/src/compat/fwd.hpp +++ b/src/compat/fwd.hpp @@ -23,5 +23,14 @@ namespace tmwa { + namespace option + { + template + class Option; + } + + template + class Borrowed; + // meh, add more when I feel like it } // namespace tmwa diff --git a/src/compat/option.cpp b/src/compat/option.cpp new file mode 100644 index 0000000..ef9e31c --- /dev/null +++ b/src/compat/option.cpp @@ -0,0 +1,26 @@ +#include "option.hpp" +// option.cpp - a data type that may or may not exist +// +// Copyright © 2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../poison.hpp" + + +namespace tmwa +{ +} // namespace tmwa diff --git a/src/compat/option.hpp b/src/compat/option.hpp new file mode 100644 index 0000000..25c23eb --- /dev/null +++ b/src/compat/option.hpp @@ -0,0 +1,376 @@ +#pragma once +// option.hpp - a data type that may or may not exist +// +// Copyright © 2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "fwd.hpp" + +#include + +#include + + +namespace tmwa +{ +namespace option +{ + enum option_hack_type { option_hack_value }; + + template + class OptionRepr + { + __attribute__((aligned(alignof(T)))) + char _data[sizeof(T)]; + bool _some; + public: + void set_none() { _some = false; } + // maybe add pre_set_some if it is useful for other specializations + void post_set_some() { _some = true; } + bool is_some() const { return _some; } + T *ptr() { return reinterpret_cast(&_data); } + const T *ptr() const { return reinterpret_cast(&_data); } + }; + template + class OptionRepr; + template + class OptionRepr; + + template + Option None(option_hack_type=option_hack_value) + { + return None; + } + + template + Option Some(T v) + { + Option rv = None; + rv.do_construct(std::move(v)); + return rv; + } + + template + class Option + { + static_assert(std::is_pod>::value, "repr should itself be pod, copies are done manually"); + OptionRepr repr; + + friend Option Some(T); + + void do_init() + { + repr.set_none(); + } + template + void do_construct(U&&... u) + { + new(repr.ptr()) T(std::forward(u)...); + repr.post_set_some(); + } + void do_move_construct(Option&& r) + { + if (r.repr.is_some()) + { + do_construct(std::move(*r.repr.ptr())); + r.do_destruct(); + } + } + void do_copy_construct(const Option& r) + { + if (r.repr.is_some()) + { + do_construct(*r.repr.ptr()); + } + } + void do_move_assign(Option&& r) + { + if (repr.is_some()) + { + if (r.repr.is_some()) + { + *repr.ptr() = std::move(*r.repr.ptr()); + } + else + { + do_destruct(); + } + return; + } + else + { + do_move_construct(std::move(r)); + } + } + void do_copy_assign(const Option& r) + { + if (repr.is_some()) + { + if (r.repr.is_some()) + { + *repr.ptr() = *r.repr.ptr(); + } + else + { + do_destruct(); + } + return; + } + else + { + do_copy_construct(r); + } + } + void do_destruct() + { + repr.ptr()->~T(); + repr.set_none(); + } + public: + Option() = delete; + Option(Option(*)(option_hack_type)) + { + do_init(); + } + Option(Option&& r) + { + do_init(); + do_move_construct(std::move(r)); + } + Option(const Option& r) + { + do_init(); + do_copy_construct(r); + } + Option& operator = (Option&& r) + { + do_move_assign(std::move(r)); + return *this; + } + Option& operator = (const Option& r) + { + do_copy_assign(r); + return *this; + } + ~Option() + { + do_destruct(); + } + + T move_or(T def) + { + if (repr.is_some()) + { + def = std::move(*repr.ptr()); + do_destruct(); + } + return def; + } + T copy_or(T def) const + { + if (repr.is_some()) + { + def = *repr.ptr(); + } + return def; + } + T& ref_or(T& def) + { + return repr.is_some() ? *repr.ptr() : def; + } + const T& ref_or(const T& def) const + { + return repr.is_some() ? *repr.ptr() : def; + } + T *ptr_or(T *def) + { + return repr.is_some() ? repr.ptr() : def; + } + const T *ptr_or(const T *def) const + { + return repr.is_some() ? repr.ptr() : def; + } + + template + auto move_map(F&& f) -> Option(f)(std::move(*repr.ptr())))> + { + if (repr.is_some()) + { + auto rv = Some(std::forward(f)(std::move(*repr.ptr()))); + do_destruct(); + return rv; + } + else + { + return None; + } + } + template + auto map(F&& f) -> Option(f)(*repr.ptr()))> + { + if (repr.is_some()) + { + return Some(std::forward(f)(*repr.ptr())); + } + else + { + return None; + } + } + template + auto map(F&& f) const -> Option(f)(*repr.ptr()))> + { + if (repr.is_some()) + { + return Some(std::forward(f)(*repr.ptr())); + } + else + { + return None; + } + } + // shortcut for flatten(o.map()) that avoids explicit Some's inside + template + auto cmap(B&& b, F&& f) const -> Option(f)(*repr.ptr()))> + { + if (repr.is_some() && std::forward(b)(*repr.ptr())) + { + return Some(std::forward(f)(*repr.ptr())); + } + else + { + return None; + } + } + }; + + template + struct most_flattened_type + { + using type = T; + + static Option flatten(Option o) + { + return std::move(o); + } + }; + template + struct most_flattened_type> + { + using type = typename most_flattened_type::type; + + static Option flatten(Option> o) + { + return most_flattened_type::flatten(o.move_or(None)); + } + }; + + template + Option::type> flatten(Option o) + { + return most_flattened_type::flatten(std::move(o)); + } + + template + bool operator == (const Option& l, const Option& r) + { + const T *l2 = l.ptr_or(nullptr); + const T *r2 = r.ptr_or(nullptr); + if (!l2 && !r2) + return true; + if (l2 && r2) + { + return *l2 == *r2; + } + return false; + } + template + bool operator != (const Option& l, const Option& r) + { + return !(l == r); + } + template + bool operator < (const Option& l, const Option& r) + { + const T *l2 = l.ptr_or(nullptr); + const T *r2 = r.ptr_or(nullptr); + + if (!l2 && r2) + return true; + if (l2 && r2) + { + return *l2 < *r2; + } + return false; + } + template + bool operator > (const Option& l, const Option& r) + { + return (r < l); + } + template + bool operator <= (const Option& l, const Option& r) + { + return !(r < l); + } + template + bool operator >= (const Option& l, const Option& r) + { + return !(l < r); + } + + // workaround for the fact that most references can't escape + template + struct RefWrapper + { + T maybe_ref; + + T maybe_ref_fun() { return std::forward(maybe_ref); } + }; + + template + RefWrapper option_unwrap(RefWrapper> o) + { return RefWrapper{std::move(*reinterpret_cast&>(o.maybe_ref).ptr())}; } + template + RefWrapper option_unwrap(RefWrapper&> o) + { return RefWrapper{*reinterpret_cast&>(o.maybe_ref).ptr()}; } + template + RefWrapper option_unwrap(RefWrapper&&> o) + { return RefWrapper{std::move(*reinterpret_cast&>(o.maybe_ref).ptr())}; } + template + RefWrapper option_unwrap(RefWrapper> o) + { return RefWrapper{std::move(*reinterpret_cast&>(o.maybe_ref).ptr())}; } + template + RefWrapper option_unwrap(RefWrapper&> o) + { return RefWrapper{*reinterpret_cast&>(o.maybe_ref).ptr()}; } + template + RefWrapper option_unwrap(RefWrapper&&> o) + { return RefWrapper{std::move(*reinterpret_cast&>(o.maybe_ref).ptr())}; } + + // if you think you understand this, you're not trying hard enough. +#define TRY_UNWRAP(opt, falsy) \ + ({ \ + tmwa::option::RefWrapper o = {(opt)}; \ + if (!o.maybe_ref.ptr_or(nullptr)) falsy; \ + tmwa::option::option_unwrap(std::move(o)); \ + }).maybe_ref_fun() +} // namespace option + +using option::Option; +using option::None; +using option::Some; +} // namespace tmwa diff --git a/src/compat/option_test.cpp b/src/compat/option_test.cpp new file mode 100644 index 0000000..e4be147 --- /dev/null +++ b/src/compat/option_test.cpp @@ -0,0 +1,399 @@ +#include "option.hpp" +// option_test.cpp - Testsuite for a type that may or may not exist +// +// Copyright © 2014 Ben Longbons +// +// This file is part of The Mana World (Athena server) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "../strings/literal.hpp" + +#include "borrow.hpp" + +#include "../diagnostics.hpp" +//#include "../poison.hpp" + + +namespace tmwa +{ +TEST(Option, somenone) +{ + { + option::Option opt = option::None; + opt = option::None; + } + { + option::Option opt = option::None; + opt = option::None; + } + { + option::Option opt = option::None(); + opt = option::None(); + } + { + option::Option opt = option::Some(123); + opt = option::Some(123); + } + { + option::Option opt = option::Some(123); + opt = option::Some(123); + } +} +TEST(Option, somenonenocopy) +{ + struct Foo + { + Foo() = default; + Foo(Foo&&) = default; + Foo(const Foo&) = delete; + Foo& operator = (Foo&&) = default; + Foo& operator = (const Foo&) = delete; + }; + { + option::Option opt = option::None; + opt = option::None; + } + // clang <= 3.4 is buggy + // since clang doesn't version, there is no way to restrict it to clang 3.5+ +#ifndef __clang__ + { + option::Option opt = option::None; + opt = option::None; + } +#endif + { + option::Option opt = option::None(); + opt = option::None(); + } + { + option::Option opt = option::Some(Foo()); + opt = option::Some(Foo()); + } + { + option::Option opt = option::Some(Foo()); + opt = option::Some(Foo()); + } +} +TEST(Option, customrepr) +{ + int iv = 123; + Borrowed i = borrow(iv); + + EXPECT_EQ(&iv, as_raw_pointer(Some(i))); + + { + option::Option> opt = option::None; + opt = option::None; + } + { + option::Option> opt = option::None>; + opt = option::None>; + } + { + option::Option> opt = option::None>(); + opt = option::None>(); + } + { + option::Option> opt = option::Some(i); + opt = option::Some(i); + } + { + option::Option> opt = option::Some>(i); + opt = option::Some>(i); + } +} + +TEST(Option, def) +{ + struct Tracked + { + int id; + int gen; + + Tracked(int i, int g=0) : id(i), gen(g) {} + Tracked(Tracked&&) = default; + Tracked(const Tracked& r) : id(r.id), gen(r.gen + 1) {} + Tracked& operator = (Tracked&&) = default; + Tracked& operator = (const Tracked& r) { id = r.id; gen = r.gen + 1; return *this; } + + bool operator == (const Tracked& r) const + { + return this->id == r.id && this->gen == r.gen; + } + bool operator != (const Tracked& r) const + { + return !(*this == r); + } + }; + + { + option::Option o = option::None; + EXPECT_EQ(o.move_or(Tracked(1)), Tracked(1)); + EXPECT_EQ(o.copy_or(Tracked(2)), Tracked(2)); + Tracked t3(3); + Tracked& r3 = o.ref_or(t3); + EXPECT_EQ(&r3, &t3); + Tracked t4(4); + Tracked *r4 = o.ptr_or(&t4); + EXPECT_EQ(r4, &t4); + EXPECT_EQ(o.ptr_or(nullptr), nullptr); + } + { + const option::Option o = option::None; + EXPECT_EQ(o.copy_or(Tracked(2)), Tracked(2)); + Tracked t3(3); + const Tracked& r3 = o.ref_or(t3); + EXPECT_EQ(&r3, &t3); + Tracked t4(4); + const Tracked *r4 = o.ptr_or(&t4); + EXPECT_EQ(r4, &t4); + EXPECT_EQ(o.ptr_or(nullptr), nullptr); + } + { + option::Option o = option::Some(Tracked(0)); + EXPECT_EQ(o.move_or(Tracked(1)), Tracked(0)); + EXPECT_EQ(o.ptr_or(nullptr), nullptr); + o = option::Some(Tracked(0)); + EXPECT_EQ(o.copy_or(Tracked(2)), Tracked(0, 1)); + Tracked t3(3); + Tracked& r3 = o.ref_or(t3); + EXPECT_NE(&r3, &t3); + Tracked t4(4); + Tracked *r4 = o.ptr_or(&t4); + EXPECT_NE(r4, &t4); + EXPECT_NE(o.ptr_or(nullptr), nullptr); + EXPECT_EQ(&r3, r4); + EXPECT_EQ(r4, reinterpret_cast(&o)); + } + { + const option::Option o = option::Some(Tracked(0)); + EXPECT_EQ(o.copy_or(Tracked(2)), Tracked(0, 1)); + Tracked t3(3); + const Tracked& r3 = o.ref_or(t3); + EXPECT_NE(&r3, &t3); + Tracked t4(4); + const Tracked *r4 = o.ptr_or(&t4); + EXPECT_NE(r4, &t4); + EXPECT_NE(o.ptr_or(nullptr), nullptr); + EXPECT_EQ(&r3, r4); + EXPECT_EQ(r4, reinterpret_cast(&o)); + } +} + +TEST(Option, map) +{ + struct Foo + { + Foo() = default; + Foo(Foo&&) = default; + Foo(const Foo&) = delete; + Foo& operator = (Foo&&) = default; + Foo& operator = (const Foo&) = delete; + }; + + // move + { + option::Option o = option::None; + EXPECT_EQ(o.ptr_or(nullptr), nullptr); + option::Option i = o.move_map([](Foo){ return 0; }); + EXPECT_EQ(o.ptr_or(nullptr), nullptr); + EXPECT_EQ(i.ptr_or(nullptr), nullptr); + } + { + option::Option o = option::Some(Foo()); + EXPECT_NE(o.ptr_or(nullptr), nullptr); + option::Option i = o.move_map([](Foo){ return 1; }); + EXPECT_EQ(o.ptr_or(nullptr), nullptr); + EXPECT_NE(i.ptr_or(nullptr), nullptr); + EXPECT_EQ(i.copy_or(0), 1); + } + // mut ref + { + option::Option o = option::None; + EXPECT_EQ(o.ptr_or(nullptr), nullptr); + option::Option i = o.map([](Foo&){ return 0; }); + EXPECT_EQ(o.ptr_or(nullptr), nullptr); + EXPECT_EQ(i.ptr_or(nullptr), nullptr); + } + { + option::Option o = option::Some(Foo()); + EXPECT_NE(o.ptr_or(nullptr), nullptr); + option::Option i = o.map([](Foo&){ return 1; }); + EXPECT_NE(o.ptr_or(nullptr), nullptr); + EXPECT_NE(i.ptr_or(nullptr), nullptr); + EXPECT_EQ(i.copy_or(0), 1); + } + // const ref + { + option::Option o = option::None; + EXPECT_EQ(o.ptr_or(nullptr), nullptr); + option::Option i = o.map([](const Foo&){ return 0; }); + EXPECT_EQ(o.ptr_or(nullptr), nullptr); + EXPECT_EQ(i.ptr_or(nullptr), nullptr); + } + { + option::Option o = option::Some(Foo()); + EXPECT_NE(o.ptr_or(nullptr), nullptr); + option::Option i = o.map([](const Foo&){ return 1; }); + EXPECT_NE(o.ptr_or(nullptr), nullptr); + EXPECT_NE(i.ptr_or(nullptr), nullptr); + EXPECT_EQ(i.copy_or(0), 1); + } +} + +#if __cplusplus >= 201300 // c++14 as given by gcc 4.9 +# define DECLTYPE_AUTO decltype(auto) +#else +# define DECLTYPE_AUTO auto&& +#endif + +TEST(Option, unwrap) +{ + int x; + + Option v = Some(1); + Option& l = v; + Option&& r = std::move(v); + const Option cv = v; + // significantly, see the mut + const Option& cl = v; + const Option&& cr = std::move(v); + + auto fv = [&]() -> Option { return v; }; + auto fl = [&]() -> Option& { return l; }; + auto fr = [&]() -> Option&& { return std::move(r); }; + auto fcv = [&]() -> const Option { return v; }; + auto fcl = [&]() -> const Option& { return l; }; + auto fcr = [&]() -> const Option&& { return std::move(r); }; + + DIAG_PUSH(); + DIAG_I(useless_cast); + +#define CHECK(v, t) \ + { \ + DECLTYPE_AUTO out = TRY_UNWRAP(v, abort() ); \ + DECLTYPE_AUTO cmp = static_cast(x); \ + static_assert(std::is_same::value, #v); \ + } + + CHECK(v, int&); + CHECK(cv, const int&); + CHECK(l, int&); + CHECK(cl, const int&); + CHECK(r, int&); + CHECK(cr, const int&); + // repeat the same forcing expressions, since that matters with decltype + CHECK((v), int&); + CHECK((cv), const int&); + CHECK((l), int&); + CHECK((cl), const int&); + CHECK((r), int&); + CHECK((cr), const int&); + + CHECK(fv(), int); + CHECK(fcv(), int); + CHECK(fl(), int&); + CHECK(fcl(), const int&); + CHECK(fr(), int&&); + CHECK(fcr(), const int&&); + + DIAG_POP(); +#undef CHECK + + v = None; TRY_UNWRAP(v, v = Some(1)); + v = None; TRY_UNWRAP(l, v = Some(1)); + v = None; TRY_UNWRAP(cl, v = Some(1)); + v = None; TRY_UNWRAP(r, v = Some(1)); + v = None; TRY_UNWRAP(cr, v = Some(1)); + + v = None; TRY_UNWRAP(fl(), v = Some(1)); + v = None; TRY_UNWRAP(fcl(), v = Some(1)); + v = None; TRY_UNWRAP(fr(), v = Some(1)); + v = None; TRY_UNWRAP(fcr(), v = Some(1)); +} + +TEST(Option, flatten) +{ + using option::Option; + using option::Some; + using option::None; + + struct Foo + { + int x; + }; + auto f1 = Some(Foo{42}); + auto f2 = Some(f1); + auto f3 = Some(f2); + EXPECT_EQ(flatten(f1).copy_or(Foo{404}).x, 42); + EXPECT_EQ(flatten(f2).copy_or(Foo{404}).x, 42); + EXPECT_EQ(flatten(f3).copy_or(Foo{404}).x, 42); + + decltype(f1) n1 = None; + decltype(f2) n2a = None; + decltype(f2) n2b = Some(n1); + decltype(f3) n3a = None; + decltype(f3) n3b = Some(n2a); + decltype(f3) n3c = Some(n2b); + EXPECT_EQ(flatten(n1).copy_or(Foo{404}).x, 404); + EXPECT_EQ(flatten(n2a).copy_or(Foo{404}).x, 404); + EXPECT_EQ(flatten(n2b).copy_or(Foo{404}).x, 404); + EXPECT_EQ(flatten(n3a).copy_or(Foo{404}).x, 404); + EXPECT_EQ(flatten(n3b).copy_or(Foo{404}).x, 404); + EXPECT_EQ(flatten(n3c).copy_or(Foo{404}).x, 404); +} + +#define EQ(a, b) ({ EXPECT_TRUE(a == b); EXPECT_FALSE(a != b); EXPECT_FALSE(a < b); EXPECT_TRUE(a <= b); EXPECT_FALSE(a > b); EXPECT_TRUE(a >= b); }) +#define LT(a, b) ({ EXPECT_FALSE(a == b); EXPECT_TRUE(a != b); EXPECT_TRUE(a < b); EXPECT_TRUE(a <= b); EXPECT_FALSE(a > b); EXPECT_FALSE(a >= b); }) +#define GT(a, b) ({ EXPECT_FALSE(a == b); EXPECT_TRUE(a != b); EXPECT_FALSE(a < b); EXPECT_FALSE(a <= b); EXPECT_TRUE(a > b); EXPECT_TRUE(a >= b); }) + +TEST(Option, cmp) +{ + using option::Option; + using option::Some; + using option::None; + + Option none = None; + + EQ(none, none); + EQ(none, None); + LT(none, Some(-1)); + LT(none, Some(0)); + LT(none, Some(1)); + EQ((None), none); + // EQ((None), None); // actually a function template + LT((None), Some(-1)); + LT((None), Some(0)); + LT((None), Some(1)); + GT(Some(-1), none); + GT(Some(-1), None); + EQ(Some(-1), Some(-1)); + LT(Some(-1), Some(0)); + LT(Some(-1), Some(1)); + GT(Some(0), none); + GT(Some(0), None); + GT(Some(0), Some(-1)); + EQ(Some(0), Some(0)); + LT(Some(0), Some(1)); + GT(Some(1), none); + GT(Some(1), None); + GT(Some(1), Some(-1)); + GT(Some(1), Some(0)); + EQ(Some(1), Some(1)); +} + +} // namespace tmwa -- cgit v1.2.3-70-g09d2