summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Longbons <b.r.longbons@gmail.com>2014-09-30 16:20:07 -0700
committerBen Longbons <b.r.longbons@gmail.com>2014-10-04 16:17:30 -0700
commit2b7f56003f27e04868bba2052e5395f68a5ad817 (patch)
treef05ffd6e8d771c7abe85c0a9dadb3836854b6a2d
parent77563ec532c8b51134c3020fb3c70a8bed8457fb (diff)
downloadtmwa-2b7f56003f27e04868bba2052e5395f68a5ad817.tar.gz
tmwa-2b7f56003f27e04868bba2052e5395f68a5ad817.tar.bz2
tmwa-2b7f56003f27e04868bba2052e5395f68a5ad817.tar.xz
tmwa-2b7f56003f27e04868bba2052e5395f68a5ad817.zip
Implement Option<T> and Borrowed<T>
-rw-r--r--src/compat/borrow.cpp26
-rw-r--r--src/compat/borrow.hpp101
-rw-r--r--src/compat/fwd.hpp9
-rw-r--r--src/compat/option.cpp26
-rw-r--r--src/compat/option.hpp376
-rw-r--r--src/compat/option_test.cpp399
6 files changed, 937 insertions, 0 deletions
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 <b.r.longbons@gmail.com>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+#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 <b.r.longbons@gmail.com>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#include <cstdlib>
+
+#include <iterator>
+
+#include "option.hpp"
+
+// unit tests currention in option_test.cpp
+
+namespace tmwa
+{
+ // TODO see if const-by-default is a thing
+ template<class T>
+ 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<class U>
+ Borrowed<U> downcast_to() const
+ {
+ static_assert(std::is_base_of<T, U>::value, "base check");
+ static_assert(!std::is_same<T, U>::value, "same check");
+ return Borrowed<U>(static_cast<U *>(stupid));
+ }
+
+ template<class U>
+ Borrowed<U> upcast_to() const
+ {
+ static_assert(std::is_base_of<U, T>::value, "base check");
+ static_assert(!std::is_same<T, U>::value, "same check");
+ return Borrowed<U>(stupid);
+ }
+ };
+
+ namespace option
+ {
+ template<class T>
+ class OptionRepr<Borrowed<T>>
+ {
+ T *stupider;
+ public:
+ void set_none() { stupider = nullptr; }
+ void post_set_some() {}
+ bool is_some() const { return stupider != nullptr; }
+ Borrowed<T> *ptr() { return reinterpret_cast<Borrowed<T> *>(&stupider); }
+ const Borrowed<T> *ptr() const { return reinterpret_cast<const Borrowed<T> *>(&stupider); }
+ };
+ }
+
+ template<class T>
+ using P = Borrowed<T>;
+
+ template<class T>
+ Borrowed<T> borrow(T& ref)
+ {
+ return Borrowed<T>(&ref);
+ }
+
+ template<class T>
+ T *as_raw_pointer(Option<Borrowed<T>> 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 T>
+ class Option;
+ }
+
+ template<class T>
+ 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 <b.r.longbons@gmail.com>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+#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 <b.r.longbons@gmail.com>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+#include "fwd.hpp"
+
+#include <cassert>
+
+#include <utility>
+
+
+namespace tmwa
+{
+namespace option
+{
+ enum option_hack_type { option_hack_value };
+
+ template<class T>
+ 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<T *>(&_data); }
+ const T *ptr() const { return reinterpret_cast<const T *>(&_data); }
+ };
+ template<class T>
+ class OptionRepr<T&>;
+ template<class T>
+ class OptionRepr<T&&>;
+
+ template<class T>
+ Option<T> None(option_hack_type=option_hack_value)
+ {
+ return None;
+ }
+
+ template<class T>
+ Option<T> Some(T v)
+ {
+ Option<T> rv = None;
+ rv.do_construct(std::move(v));
+ return rv;
+ }
+
+ template<class T>
+ class Option
+ {
+ static_assert(std::is_pod<OptionRepr<T>>::value, "repr should itself be pod, copies are done manually");
+ OptionRepr<T> repr;
+
+ friend Option<T> Some<T>(T);
+
+ void do_init()
+ {
+ repr.set_none();
+ }
+ template<class... U>
+ void do_construct(U&&... u)
+ {
+ new(repr.ptr()) T(std::forward<U>(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<class F>
+ auto move_map(F&& f) -> Option<decltype(std::forward<F>(f)(std::move(*repr.ptr())))>
+ {
+ if (repr.is_some())
+ {
+ auto rv = Some(std::forward<F>(f)(std::move(*repr.ptr())));
+ do_destruct();
+ return rv;
+ }
+ else
+ {
+ return None;
+ }
+ }
+ template<class F>
+ auto map(F&& f) -> Option<decltype(std::forward<F>(f)(*repr.ptr()))>
+ {
+ if (repr.is_some())
+ {
+ return Some(std::forward<F>(f)(*repr.ptr()));
+ }
+ else
+ {
+ return None;
+ }
+ }
+ template<class F>
+ auto map(F&& f) const -> Option<decltype(std::forward<F>(f)(*repr.ptr()))>
+ {
+ if (repr.is_some())
+ {
+ return Some(std::forward<F>(f)(*repr.ptr()));
+ }
+ else
+ {
+ return None;
+ }
+ }
+ // shortcut for flatten(o.map()) that avoids explicit Some's inside
+ template<class B, class F>
+ auto cmap(B&& b, F&& f) const -> Option<decltype(std::forward<F>(f)(*repr.ptr()))>
+ {
+ if (repr.is_some() && std::forward<B>(b)(*repr.ptr()))
+ {
+ return Some(std::forward<F>(f)(*repr.ptr()));
+ }
+ else
+ {
+ return None;
+ }
+ }
+ };
+
+ template<class T>
+ struct most_flattened_type
+ {
+ using type = T;
+
+ static Option<type> flatten(Option<T> o)
+ {
+ return std::move(o);
+ }
+ };
+ template<class T>
+ struct most_flattened_type<Option<T>>
+ {
+ using type = typename most_flattened_type<T>::type;
+
+ static Option<type> flatten(Option<Option<T>> o)
+ {
+ return most_flattened_type<T>::flatten(o.move_or(None));
+ }
+ };
+
+ template<class T>
+ Option<typename most_flattened_type<T>::type> flatten(Option<T> o)
+ {
+ return most_flattened_type<T>::flatten(std::move(o));
+ }
+
+ template<class T>
+ bool operator == (const Option<T>& l, const Option<T>& 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<class T>
+ bool operator != (const Option<T>& l, const Option<T>& r)
+ {
+ return !(l == r);
+ }
+ template<class T>
+ bool operator < (const Option<T>& l, const Option<T>& 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<class T>
+ bool operator > (const Option<T>& l, const Option<T>& r)
+ {
+ return (r < l);
+ }
+ template<class T>
+ bool operator <= (const Option<T>& l, const Option<T>& r)
+ {
+ return !(r < l);
+ }
+ template<class T>
+ bool operator >= (const Option<T>& l, const Option<T>& r)
+ {
+ return !(l < r);
+ }
+
+ // workaround for the fact that most references can't escape
+ template<class T>
+ struct RefWrapper
+ {
+ T maybe_ref;
+
+ T maybe_ref_fun() { return std::forward<T>(maybe_ref); }
+ };
+
+ template<class T>
+ RefWrapper<T> option_unwrap(RefWrapper<Option<T>> o)
+ { return RefWrapper<T>{std::move(*reinterpret_cast<OptionRepr<T>&>(o.maybe_ref).ptr())}; }
+ template<class T>
+ RefWrapper<T&> option_unwrap(RefWrapper<Option<T>&> o)
+ { return RefWrapper<T&>{*reinterpret_cast<OptionRepr<T>&>(o.maybe_ref).ptr()}; }
+ template<class T>
+ RefWrapper<T&&> option_unwrap(RefWrapper<Option<T>&&> o)
+ { return RefWrapper<T&&>{std::move(*reinterpret_cast<OptionRepr<T>&>(o.maybe_ref).ptr())}; }
+ template<class T>
+ RefWrapper<T> option_unwrap(RefWrapper<const Option<T>> o)
+ { return RefWrapper<T>{std::move(*reinterpret_cast<const OptionRepr<T>&>(o.maybe_ref).ptr())}; }
+ template<class T>
+ RefWrapper<const T&> option_unwrap(RefWrapper<const Option<T>&> o)
+ { return RefWrapper<const T&>{*reinterpret_cast<const OptionRepr<T>&>(o.maybe_ref).ptr()}; }
+ template<class T>
+ RefWrapper<const T&&> option_unwrap(RefWrapper<const Option<T>&&> o)
+ { return RefWrapper<const T&&>{std::move(*reinterpret_cast<const OptionRepr<T>&>(o.maybe_ref).ptr())}; }
+
+ // if you think you understand this, you're not trying hard enough.
+#define TRY_UNWRAP(opt, falsy) \
+ ({ \
+ tmwa::option::RefWrapper<decltype((opt))> 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 <b.r.longbons@gmail.com>
+//
+// 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 <http://www.gnu.org/licenses/>.
+
+#include <gtest/gtest.h>
+
+#include "../strings/literal.hpp"
+
+#include "borrow.hpp"
+
+#include "../diagnostics.hpp"
+//#include "../poison.hpp"
+
+
+namespace tmwa
+{
+TEST(Option, somenone)
+{
+ {
+ option::Option<int> opt = option::None;
+ opt = option::None;
+ }
+ {
+ option::Option<int> opt = option::None<int>;
+ opt = option::None<int>;
+ }
+ {
+ option::Option<int> opt = option::None<int>();
+ opt = option::None<int>();
+ }
+ {
+ option::Option<int> opt = option::Some(123);
+ opt = option::Some(123);
+ }
+ {
+ option::Option<int> opt = option::Some<int>(123);
+ opt = option::Some<int>(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<Foo> 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<Foo> opt = option::None<Foo>;
+ opt = option::None<Foo>;
+ }
+#endif
+ {
+ option::Option<Foo> opt = option::None<Foo>();
+ opt = option::None<Foo>();
+ }
+ {
+ option::Option<Foo> opt = option::Some(Foo());
+ opt = option::Some(Foo());
+ }
+ {
+ option::Option<Foo> opt = option::Some<Foo>(Foo());
+ opt = option::Some<Foo>(Foo());
+ }
+}
+TEST(Option, customrepr)
+{
+ int iv = 123;
+ Borrowed<int> i = borrow(iv);
+
+ EXPECT_EQ(&iv, as_raw_pointer(Some(i)));
+
+ {
+ option::Option<Borrowed<int>> opt = option::None;
+ opt = option::None;
+ }
+ {
+ option::Option<Borrowed<int>> opt = option::None<Borrowed<int>>;
+ opt = option::None<Borrowed<int>>;
+ }
+ {
+ option::Option<Borrowed<int>> opt = option::None<Borrowed<int>>();
+ opt = option::None<Borrowed<int>>();
+ }
+ {
+ option::Option<Borrowed<int>> opt = option::Some(i);
+ opt = option::Some(i);
+ }
+ {
+ option::Option<Borrowed<int>> opt = option::Some<Borrowed<int>>(i);
+ opt = option::Some<Borrowed<int>>(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<Tracked> 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<Tracked> 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<Tracked> 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<Tracked *>(&o));
+ }
+ {
+ const option::Option<Tracked> 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<const Tracked *>(&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<Foo> o = option::None;
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.move_map([](Foo){ return 0; });
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.ptr_or(nullptr), nullptr);
+ }
+ {
+ option::Option<Foo> o = option::Some(Foo());
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ option::Option<int> 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<Foo> o = option::None;
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.map([](Foo&){ return 0; });
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.ptr_or(nullptr), nullptr);
+ }
+ {
+ option::Option<Foo> o = option::Some(Foo());
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ option::Option<int> 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<Foo> o = option::None;
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ option::Option<int> i = o.map([](const Foo&){ return 0; });
+ EXPECT_EQ(o.ptr_or(nullptr), nullptr);
+ EXPECT_EQ(i.ptr_or(nullptr), nullptr);
+ }
+ {
+ option::Option<Foo> o = option::Some(Foo());
+ EXPECT_NE(o.ptr_or(nullptr), nullptr);
+ option::Option<int> 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<int> v = Some(1);
+ Option<int>& l = v;
+ Option<int>&& r = std::move(v);
+ const Option<int> cv = v;
+ // significantly, see the mut
+ const Option<int>& cl = v;
+ const Option<int>&& cr = std::move(v);
+
+ auto fv = [&]() -> Option<int> { return v; };
+ auto fl = [&]() -> Option<int>& { return l; };
+ auto fr = [&]() -> Option<int>&& { return std::move(r); };
+ auto fcv = [&]() -> const Option<int> { return v; };
+ auto fcl = [&]() -> const Option<int>& { return l; };
+ auto fcr = [&]() -> const Option<int>&& { 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<t>(x); \
+ static_assert(std::is_same<decltype(out), decltype(cmp)>::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<int> 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