summaryrefslogtreecommitdiff
path: root/src/compat
diff options
context:
space:
mode:
Diffstat (limited to 'src/compat')
-rw-r--r--src/compat/attr.hpp4
-rw-r--r--src/compat/borrow.hpp111
-rw-r--r--src/compat/borrow.py18
-rw-r--r--src/compat/cast.cpp26
-rw-r--r--src/compat/fwd.hpp15
-rw-r--r--src/compat/iter.cpp26
-rw-r--r--src/compat/memory.cpp26
-rw-r--r--src/compat/nullpo.hpp10
-rw-r--r--src/compat/operators.hpp68
-rw-r--r--src/compat/option.hpp469
-rw-r--r--src/compat/option.py39
-rw-r--r--src/compat/option_test.cpp499
-rw-r--r--src/compat/rawmem.cpp26
-rw-r--r--src/compat/rawmem.hpp21
-rw-r--r--src/compat/result.hpp94
-rw-r--r--src/compat/result_test.cpp79
-rw-r--r--src/compat/time_t.cpp26
-rw-r--r--src/compat/time_t.hpp84
18 files changed, 1504 insertions, 137 deletions
diff --git a/src/compat/attr.hpp b/src/compat/attr.hpp
index 238a5d5..5ebef6d 100644
--- a/src/compat/attr.hpp
+++ b/src/compat/attr.hpp
@@ -1,7 +1,7 @@
#pragma once
// attr.hpp - Attributes.
//
-// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com>
+// Copyright © 2013-2014 Ben Longbons <b.r.longbons@gmail.com>
//
// This file is part of The Mana World (Athena server)
//
@@ -28,4 +28,6 @@ namespace tmwa
#else
# define FALLTHROUGH /* fallthrough */
#endif
+
+#define JOIN(a, b) a##b
} // namespace tmwa
diff --git a/src/compat/borrow.hpp b/src/compat/borrow.hpp
new file mode 100644
index 0000000..0ea6a26
--- /dev/null
+++ b/src/compat/borrow.hpp
@@ -0,0 +1,111 @@
+#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:
+ Borrowed() = delete;
+ 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);
+ }
+
+ friend bool operator == (Borrowed l, Borrowed r)
+ {
+ return l.stupid == r.stupid;
+ }
+ friend bool operator != (Borrowed l, Borrowed r)
+ {
+ return !(l == r);
+ }
+ };
+
+ 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/borrow.py b/src/compat/borrow.py
new file mode 100644
index 0000000..58cd19b
--- /dev/null
+++ b/src/compat/borrow.py
@@ -0,0 +1,18 @@
+class Borrowed(object):
+ __slots__ = ('_value')
+ name = 'tmwa::Borrowed'
+ enabled = True
+
+ def __init__(self, value):
+ self._value = value['stupid']
+
+ def to_string(self):
+ return self._value
+
+ test_extra = '''
+ static int borrow_thingy;
+ '''
+
+ tests = [
+ ('tmwa::borrow(borrow_thingy)', '<borrow_thingy>'),
+ ]
diff --git a/src/compat/cast.cpp b/src/compat/cast.cpp
deleted file mode 100644
index 482529d..0000000
--- a/src/compat/cast.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "cast.hpp"
-// cast.cpp - Change the type of a variable.
-//
-// Copyright © 2011-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/fwd.hpp b/src/compat/fwd.hpp
index 45f3c24..3fa0dd2 100644
--- a/src/compat/fwd.hpp
+++ b/src/compat/fwd.hpp
@@ -20,8 +20,23 @@
#include "../sanity.hpp"
+#include "../ints/fwd.hpp" // rank 1
+#include "../strings/fwd.hpp" // rank 1
+// compat/fwd.hpp is rank 2
+
namespace tmwa
{
+ namespace option
+ {
+ template<class T>
+ class Option;
+ }
+ using option::Option;
+
+ template<class T>
+ class Borrowed;
+
+ struct TimeT;
// meh, add more when I feel like it
} // namespace tmwa
diff --git a/src/compat/iter.cpp b/src/compat/iter.cpp
deleted file mode 100644
index b6d6b63..0000000
--- a/src/compat/iter.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "iter.hpp"
-// iter.cpp - tools for dealing with iterators
-//
-// 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/memory.cpp b/src/compat/memory.cpp
deleted file mode 100644
index f9f2c22..0000000
--- a/src/compat/memory.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "memory.hpp"
-// memory.cpp - I forget ...
-//
-// Copyright © 2013-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/nullpo.hpp b/src/compat/nullpo.hpp
index 5be674a..38c8e92 100644
--- a/src/compat/nullpo.hpp
+++ b/src/compat/nullpo.hpp
@@ -52,13 +52,15 @@ bool nullpo_chk(const char *file, int line, const char *func,
const void *target);
template<class T>
-bool nullpo_chk(const char *file, int line, const char *func, T target)
-{
- return nullpo_chk(file, line, func, target.operator->());
-}
+bool nullpo_chk(const char *, int, const char *, Borrowed<T>) = delete;
template<class T>
bool nullpo_chk(const char *file, int line, const char *func, T *target)
{
return nullpo_chk(file, line, func, static_cast<const void *>(target));
}
+template<class T>
+bool nullpo_chk(const char *file, int line, const char *func, T target)
+{
+ return nullpo_chk(file, line, func, target.operator->());
+}
} // namespace tmwa
diff --git a/src/compat/operators.hpp b/src/compat/operators.hpp
new file mode 100644
index 0000000..bb05765
--- /dev/null
+++ b/src/compat/operators.hpp
@@ -0,0 +1,68 @@
+#pragma once
+// operators.hpp - ADL helper for value wrappers.
+//
+// Copyright © 2013 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"
+
+
+namespace tmwa
+{
+namespace _operators
+{
+ class Comparable {};
+
+ template<class T>
+ bool operator == (T l, T r)
+ {
+ return l.value == r.value;
+ }
+
+ template<class T>
+ bool operator != (T l, T r)
+ {
+ return l.value != r.value;
+ }
+
+ template<class T>
+ bool operator < (T l, T r)
+ {
+ return l.value < r.value;
+ }
+
+ template<class T>
+ bool operator <= (T l, T r)
+ {
+ return l.value <= r.value;
+ }
+
+ template<class T>
+ bool operator > (T l, T r)
+ {
+ return l.value > r.value;
+ }
+
+ template<class T>
+ bool operator >= (T l, T r)
+ {
+ return l.value >= r.value;
+ }
+}
+
+using _operators::Comparable;
+} // namespace tmwa
diff --git a/src/compat/option.hpp b/src/compat/option.hpp
new file mode 100644
index 0000000..b6e7655
--- /dev/null
+++ b/src/compat/option.hpp
@@ -0,0 +1,469 @@
+#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;
+ }
+
+ // TODO all *_or and *_set methods should have a lazy version too
+ 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()
+ {
+ if (repr.is_some())
+ {
+ 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;
+ }
+ bool is_some() const
+ {
+ return repr.is_some();
+ }
+ bool is_none() const
+ {
+ return !is_some();
+ }
+
+ 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;
+ }
+ }
+ // wanting members is *so* common
+ template<class M, class B>
+ Option<M> pmd_get(const M B::*pmd) const
+ {
+ if (repr.is_some())
+ {
+ return Some((*repr.ptr()).*pmd);
+ }
+ else
+ {
+ return None;
+ }
+ }
+ template<class M, class B>
+ void pmd_set(M B::*pmd, M value)
+ {
+ if (repr.is_some())
+ {
+ ((*repr.ptr()).*pmd) = std::move(value);
+ }
+ }
+ template<class M, class B>
+ Option<M> pmd_pget(const M B::*pmd) const
+ {
+ if (repr.is_some())
+ {
+ return Some((**repr.ptr()).*pmd);
+ }
+ else
+ {
+ return None;
+ }
+ }
+ template<class M, class B>
+ void pmd_pset(M B::*pmd, M value)
+ {
+ if (repr.is_some())
+ {
+ ((**repr.ptr()).*pmd) = std::move(value);
+ }
+ }
+ };
+
+ 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.is_none()) falsy; \
+ tmwa::option::option_unwrap(std::move(o)); \
+ }).maybe_ref_fun()
+
+#define OMATCH_BEGIN(expr) \
+ { \
+ auto&& _omatch_var = (expr); \
+ switch (_omatch_var.is_some()) \
+ { \
+ { \
+ { \
+ /*}}}}*/
+#define OMATCH_END() \
+ /*{{{{*/ \
+ } \
+ } \
+ } \
+ (void) _omatch_var; \
+ }
+
+#define OMATCH_BEGIN_SOME(var, expr) \
+ OMATCH_BEGIN (expr) \
+ OMATCH_CASE_SOME (var)
+
+#define OMATCH_CASE_SOME(var) \
+ /*{{{{*/ \
+ } \
+ break; \
+ } \
+ { \
+ case true: \
+ { \
+ auto&& var = *_omatch_var.ptr_or(nullptr); \
+ /*}}}}*/
+#define OMATCH_CASE_NONE() \
+ /*{{{{*/ \
+ } \
+ break; \
+ } \
+ { \
+ case false: \
+ { \
+ /*}}}}*/
+} // namespace option
+
+//using option::Option;
+using option::None;
+using option::Some;
+} // namespace tmwa
diff --git a/src/compat/option.py b/src/compat/option.py
new file mode 100644
index 0000000..7704174
--- /dev/null
+++ b/src/compat/option.py
@@ -0,0 +1,39 @@
+class Option(object):
+ __slots__ = ('_value')
+ name = 'tmwa::option::Option'
+ enabled = True
+
+ def __init__(self, value):
+ self._value = value['repr']
+
+ def to_string(self):
+ value = self._value
+ ty = value.type.template_argument(0)
+ try:
+ some = bool(value['_some'])
+ except gdb.error:
+ stupider = value['stupider']
+ if stupider:
+ return 'Some<%s>(%s)' % (ty, stupider)
+ else:
+ return 'None<%s>' % ty
+ else:
+ if some:
+ data = value['_data']
+ data = data.address.cast(ty.pointer()).dereference()
+ return 'Some<%s>(%s)' % (ty, data)
+ else:
+ return 'None<%s>' % ty
+
+ test_extra = '''
+ #include "../compat/borrow.hpp"
+
+ static int option_borrow_thingy;
+ '''
+
+ tests = [
+ ('tmwa::None<int>()', 'None<int>'),
+ ('tmwa::Some(1)', 'Some<int>(1)'),
+ ('tmwa::Option<tmwa::Borrowed<int>>(tmwa::None)', 'None<tmwa::Borrowed<int>>'),
+ ('tmwa::Some(tmwa::borrow(option_borrow_thingy))', 'Some<tmwa::Borrowed<int>>(<option_borrow_thingy>)'),
+ ]
diff --git a/src/compat/option_test.cpp b/src/compat/option_test.cpp
new file mode 100644
index 0000000..69f3a60
--- /dev/null
+++ b/src/compat/option_test.cpp
@@ -0,0 +1,499 @@
+#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, destruct)
+{
+ struct BugCheck
+ {
+ bool *destroyed;
+
+ BugCheck(bool *d)
+ : destroyed(d)
+ {}
+ BugCheck(BugCheck&& r)
+ : destroyed(r.destroyed)
+ {
+ r.destroyed = nullptr;
+ }
+ BugCheck& operator = (BugCheck&& r)
+ {
+ std::swap(destroyed, r.destroyed);
+ return *this;
+ }
+ ~BugCheck()
+ {
+ if (!destroyed)
+ return;
+ if (*destroyed)
+ abort();
+ *destroyed = true;
+ }
+ };
+
+ bool destroyed = false;
+
+ Option<BugCheck> bug = Some(BugCheck(&destroyed));
+ bug = None;
+}
+
+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);
+ }
+}
+
+TEST(Option, member)
+{
+ struct Foo
+ {
+ int bar = 404;
+ };
+
+ Option<Foo> vng = None;
+ EXPECT_EQ(vng.pmd_get(&Foo::bar).copy_or(42), 42);
+ Option<Foo> vsg = Some(Foo());
+ EXPECT_EQ(vsg.pmd_get(&Foo::bar).copy_or(42), 404);
+
+ Option<Foo> vns = None;
+ vns.pmd_set(&Foo::bar, 42);
+ EXPECT_EQ(vns.copy_or(Foo()).bar, 404);
+ Option<Foo> vss = Some(Foo());
+ vss.pmd_set(&Foo::bar, 42);
+ EXPECT_EQ(vss.copy_or(Foo()).bar, 42);
+
+ Foo foo, alt;
+
+ Option<P<Foo>> png = None;
+ EXPECT_EQ(png.pmd_pget(&Foo::bar).copy_or(42), 42);
+ Option<P<Foo>> psg = Some(borrow(foo));
+ EXPECT_EQ(psg.pmd_pget(&Foo::bar).copy_or(42), 404);
+
+ Option<P<Foo>> pns = None;
+ pns.pmd_pset(&Foo::bar, 42);
+ EXPECT_EQ(pns.copy_or(borrow(alt))->bar, 404);
+ EXPECT_EQ(foo.bar, 404);
+ Option<P<Foo>> pss = Some(borrow(foo));
+ pss.pmd_pset(&Foo::bar, 42);
+ EXPECT_EQ(pss.copy_or(borrow(alt))->bar, 42);
+ EXPECT_EQ(foo.bar, 42);
+ EXPECT_EQ(alt.bar, 404);
+}
+
+#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));
+
+ v = None;
+ OMATCH_BEGIN (v)
+ {
+ OMATCH_CASE_SOME (o)
+ {
+ EXPECT_NE(o, o);
+ }
+ OMATCH_CASE_NONE ()
+ {
+ SUCCEED();
+ }
+ }
+ OMATCH_END ();
+
+ v = Some(1);
+ OMATCH_BEGIN (v)
+ {
+ OMATCH_CASE_SOME (o)
+ {
+ EXPECT_EQ(o, 1);
+ }
+ OMATCH_CASE_NONE ()
+ {
+ FAIL();
+ }
+ }
+ OMATCH_END ();
+}
+
+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
diff --git a/src/compat/rawmem.cpp b/src/compat/rawmem.cpp
deleted file mode 100644
index d322437..0000000
--- a/src/compat/rawmem.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "rawmem.hpp"
-// rawmem.cpp - Ignore poisoning and really frob this memory unsafely.
-//
-// Copyright © 2013-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/rawmem.hpp b/src/compat/rawmem.hpp
index c271a56..66af204 100644
--- a/src/compat/rawmem.hpp
+++ b/src/compat/rawmem.hpp
@@ -22,6 +22,8 @@
#include <cstdint>
#include <cstring>
+#include <type_traits>
+
#include "fwd.hpp"
@@ -49,4 +51,23 @@ void really_memset0(uint8_t *dest, size_t n)
{
memset(dest, '\0', n);
}
+
+template<class T>
+struct is_trivially_copyable
+: std::integral_constant<bool,
+ // come back when GCC actually implements the public traits properly
+ __has_trivial_copy(T)
+ && __has_trivial_assign(T)
+ && __has_trivial_destructor(T)>
+{};
+
+template<class T>
+void really_memzero_this(T *v)
+{
+ static_assert(is_trivially_copyable<T>::value, "only for mostly-pod types");
+ static_assert(std::is_class<T>::value || std::is_union<T>::value, "Only for user-defined structures (for now)");
+ memset(v, '\0', sizeof(*v));
+}
+template<class T, size_t n>
+void really_memzero_this(T (&)[n]) = delete;
} // namespace tmwa
diff --git a/src/compat/result.hpp b/src/compat/result.hpp
new file mode 100644
index 0000000..6adc552
--- /dev/null
+++ b/src/compat/result.hpp
@@ -0,0 +1,94 @@
+#pragma once
+// result.hpp - A possibly failed return value
+//
+// 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 "../strings/rstring.hpp"
+
+#include "option.hpp"
+
+namespace tmwa
+{
+ namespace result
+ {
+ enum ResultMagicFlag { magic_flag };
+
+ template<class T>
+ class Result
+ {
+ Option<T> success;
+ RString failure;
+ public:
+ Result(ResultMagicFlag, T v)
+ : success(Some(std::move(v))), failure()
+ {}
+ Result(ResultMagicFlag, RString msg)
+ : success(None), failure(std::move(msg))
+ {}
+
+ bool is_ok() { return success.is_some(); }
+ bool is_err() { return !is_ok(); }
+
+ Option<T>& get_success() { return success; }
+ RString& get_failure() { return failure; }
+ };
+
+ template<class T>
+ Result<T> Ok(T v)
+ {
+ return Result<T>(magic_flag, std::move(v));
+ }
+
+ struct Err
+ {
+ RString message;
+ Err(RString m) : message(std::move(m)) {}
+
+ template<class T>
+ operator Result<T>()
+ {
+ return Result<T>(magic_flag, message);
+ }
+ template<class T>
+ operator Option<Result<T>>()
+ {
+ return Some(Result<T>(magic_flag, message));
+ }
+ };
+
+ template<class T>
+ bool operator == (const Result<T>& l, const Result<T>& r)
+ {
+ return l.get_success() == r.get_success() && l.get_failure() == r.get_failure();
+ }
+ template<class T>
+ bool operator != (const Result<T>& l, const Result<T>& r)
+ {
+ return !(l == r);
+ }
+ } // namespace result
+ using result::Result;
+ using result::Ok;
+ using result::Err;
+
+#define TRY(r) ({ auto _res = r; TRY_UNWRAP(_res.get_success(), return ::tmwa::Err(_res.get_failure())); })
+ // TODO the existence of this as a separate macro is a bug.
+#define TRY_MOVE(r) ({ auto _res = r; TRY_UNWRAP(std::move(_res.get_success()), return ::tmwa::Err(_res.get_failure())); })
+} // namespace tmwa
diff --git a/src/compat/result_test.cpp b/src/compat/result_test.cpp
new file mode 100644
index 0000000..0fcc181
--- /dev/null
+++ b/src/compat/result_test.cpp
@@ -0,0 +1,79 @@
+#include "result.hpp"
+// result_test.cpp - Testsuite for possibly failing return values
+//
+// 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 "../poison.hpp"
+
+
+namespace tmwa
+{
+TEST(Result, inspect)
+{
+ struct Foo
+ {
+ int val;
+
+ Foo(int v) : val(v) {}
+ Foo(Foo&&) = default;
+ Foo(const Foo&) = delete;
+ Foo& operator = (Foo&&) = default;
+ Foo& operator = (const Foo&) = delete;
+
+ bool operator == (const Foo& other) const
+ {
+ return this->val == other.val;
+ }
+ };
+
+ Result<Foo> foo = Ok(Foo(1));
+ EXPECT_TRUE(foo.is_ok());
+ EXPECT_FALSE(foo.is_err());
+ EXPECT_EQ(foo.get_success(), Some(Foo(1)));
+ EXPECT_EQ(foo.get_failure(), ""_s);
+ foo = Err("oops"_s);
+ EXPECT_FALSE(foo.is_ok());
+ EXPECT_TRUE(foo.is_err());
+ EXPECT_EQ(foo.get_success(), None<Foo>());
+ EXPECT_EQ(foo.get_failure(), "oops"_s);
+}
+
+static
+Result<int> try_you(bool b)
+{
+ return b ? Ok(0) : Err("die"_s);
+}
+
+static
+Result<int> try_me(bool b)
+{
+ return Ok(TRY(try_you(b)) + 1);
+}
+
+TEST(Result, try)
+{
+ Result<int> t = try_me(true);
+ EXPECT_EQ(t.get_success(), Some(1));
+ Result<int> f = try_me(false);
+ EXPECT_EQ(f.get_failure(), "die"_s);
+}
+} // namespace tmwa
diff --git a/src/compat/time_t.cpp b/src/compat/time_t.cpp
deleted file mode 100644
index ee0bbde..0000000
--- a/src/compat/time_t.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "time_t.hpp"
-// time_t.cpp - time_t with a reliable representation
-//
-// Copyright © 2013-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/time_t.hpp b/src/compat/time_t.hpp
index 9e7cf25..e9c97c4 100644
--- a/src/compat/time_t.hpp
+++ b/src/compat/time_t.hpp
@@ -20,10 +20,90 @@
#include "fwd.hpp"
-// TODO fix this ordering violation by promoting TimeT here
-#include "../mmo/utils.hpp"
+#include <ctime>
+
+#include "../ints/little.hpp"
+
+#include "operators.hpp"
namespace tmwa
{
+// Exists in place of time_t, to give it a predictable printf-format.
+// (on x86 and amd64, time_t == long, but not on x32)
+static_assert(sizeof(long long) >= sizeof(time_t), "long long >= time_t");
+struct TimeT : Comparable
+{
+ long long value;
+
+ // conversion
+ TimeT(time_t t=0) : value(t) {}
+ TimeT(struct tm t) : value(timegm(&t)) {}
+ operator time_t() const { return value; }
+ operator struct tm() const { time_t v = value; return *gmtime(&v); }
+
+ explicit operator bool() const { return value; }
+ bool operator !() const { return !value; }
+
+ // prevent surprises
+ template<class T>
+ TimeT(T) = delete;
+ template<class T>
+ operator T() const = delete;
+
+ static
+ TimeT now()
+ {
+ // poisoned, but this is still in header-land
+ return time(nullptr);
+ }
+
+ bool error() const
+ {
+ return value == -1;
+ }
+ bool okay() const
+ {
+ return !error();
+ }
+};
+
+inline
+long long convert_for_printf(TimeT t)
+{
+ return t.value;
+}
+
+// 2038 problem
+inline __attribute__((warn_unused_result))
+bool native_to_network(Little32 *net, TimeT nat)
+{
+ time_t tmp = nat;
+ return native_to_network(net, static_cast<uint32_t>(tmp));
+}
+
+inline __attribute__((warn_unused_result))
+bool network_to_native(TimeT *nat, Little32 net)
+{
+ uint32_t tmp;
+ bool rv = network_to_native(&tmp, net);
+ *nat = static_cast<time_t>(tmp);
+ return rv;
+}
+
+inline __attribute__((warn_unused_result))
+bool native_to_network(Little64 *net, TimeT nat)
+{
+ time_t tmp = nat;
+ return native_to_network(net, static_cast<uint64_t>(tmp));
+}
+
+inline __attribute__((warn_unused_result))
+bool network_to_native(TimeT *nat, Little64 net)
+{
+ uint64_t tmp;
+ bool rv = network_to_native(&tmp, net);
+ *nat = static_cast<time_t>(tmp);
+ return rv;
+}
} // namespace tmwa