diff options
Diffstat (limited to 'src/compat')
-rw-r--r-- | src/compat/attr.hpp | 4 | ||||
-rw-r--r-- | src/compat/borrow.hpp | 111 | ||||
-rw-r--r-- | src/compat/borrow.py | 18 | ||||
-rw-r--r-- | src/compat/cast.cpp | 26 | ||||
-rw-r--r-- | src/compat/fwd.hpp | 15 | ||||
-rw-r--r-- | src/compat/iter.cpp | 26 | ||||
-rw-r--r-- | src/compat/memory.cpp | 26 | ||||
-rw-r--r-- | src/compat/nullpo.hpp | 10 | ||||
-rw-r--r-- | src/compat/operators.hpp | 68 | ||||
-rw-r--r-- | src/compat/option.hpp | 469 | ||||
-rw-r--r-- | src/compat/option.py | 39 | ||||
-rw-r--r-- | src/compat/option_test.cpp | 499 | ||||
-rw-r--r-- | src/compat/rawmem.cpp | 26 | ||||
-rw-r--r-- | src/compat/rawmem.hpp | 21 | ||||
-rw-r--r-- | src/compat/result.hpp | 94 | ||||
-rw-r--r-- | src/compat/result_test.cpp | 79 | ||||
-rw-r--r-- | src/compat/time_t.cpp | 26 | ||||
-rw-r--r-- | src/compat/time_t.hpp | 84 |
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 |