summaryrefslogtreecommitdiff
path: root/src/compat/option.hpp
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 /src/compat/option.hpp
parent77563ec532c8b51134c3020fb3c70a8bed8457fb (diff)
downloadtmwa-2b7f56003f27e04868bba2052e5395f68a5ad817.tar.gz
tmwa-2b7f56003f27e04868bba2052e5395f68a5ad817.tar.bz2
tmwa-2b7f56003f27e04868bba2052e5395f68a5ad817.tar.xz
tmwa-2b7f56003f27e04868bba2052e5395f68a5ad817.zip
Implement Option<T> and Borrowed<T>
Diffstat (limited to 'src/compat/option.hpp')
-rw-r--r--src/compat/option.hpp376
1 files changed, 376 insertions, 0 deletions
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