#ifndef STAR_MAYBE_HPP #define STAR_MAYBE_HPP #include "StarException.hpp" #include "StarHash.hpp" namespace Star { STAR_EXCEPTION(InvalidMaybeAccessException, StarException); template class Maybe { public: typedef T* PointerType; typedef T const* PointerConstType; typedef T& RefType; typedef T const& RefConstType; Maybe(); Maybe(T const& t); Maybe(T&& t); Maybe(Maybe const& rhs); Maybe(Maybe&& rhs); template Maybe(Maybe const& rhs); ~Maybe(); Maybe& operator=(Maybe const& rhs); Maybe& operator=(Maybe&& rhs); template Maybe& operator=(Maybe const& rhs); bool isValid() const; bool isNothing() const; explicit operator bool() const; PointerConstType ptr() const; PointerType ptr(); PointerConstType operator->() const; PointerType operator->(); RefConstType operator*() const; RefType operator*(); bool operator==(Maybe const& rhs) const; bool operator!=(Maybe const& rhs) const; bool operator<(Maybe const& rhs) const; RefConstType get() const; RefType get(); // Get either the contents of this Maybe or the given default. T value(T def = T()) const; // Get either this value, or if this value is none the given value. Maybe orMaybe(Maybe const& other) const; // Takes the value out of this Maybe, leaving it Nothing. T take(); // If this Maybe is set, assigns it to t and leaves this Maybe as Nothing. bool put(T& t); void set(T const& t); void set(T&& t); template void emplace(Args&&... t); void reset(); // Apply a function to the contained value if it is not Nothing. template void exec(Function&& function); // Functor map operator. If this maybe is not Nothing, then applies the // given function to it and returns the result, otherwise returns Nothing (of // the type the function would normally return). template auto apply(Function&& function) const -> Maybe()))>::type>; // Monadic bind operator. Given function should return another Maybe. template auto sequence(Function function) const -> decltype(function(std::declval())); private: union { T m_data; }; bool m_initialized; }; template std::ostream& operator<<(std::ostream& os, Maybe const& v); template struct hash> { size_t operator()(Maybe const& m) const; hash hasher; }; template Maybe::Maybe() : m_initialized(false) {} template Maybe::Maybe(T const& t) : Maybe() { new (&m_data) T(t); m_initialized = true; } template Maybe::Maybe(T&& t) : Maybe() { new (&m_data) T(std::forward(t)); m_initialized = true; } template Maybe::Maybe(Maybe const& rhs) : Maybe() { if (rhs.m_initialized) { new (&m_data) T(rhs.m_data); m_initialized = true; } } template Maybe::Maybe(Maybe&& rhs) : Maybe() { if (rhs.m_initialized) { new (&m_data) T(std::move(rhs.m_data)); m_initialized = true; rhs.reset(); } } template template Maybe::Maybe(Maybe const& rhs) : Maybe() { if (rhs) { new (&m_data) T(*rhs); m_initialized = true; } } template Maybe::~Maybe() { reset(); } template Maybe& Maybe::operator=(Maybe const& rhs) { if (&rhs == this) return *this; if (rhs) emplace(*rhs); else reset(); return *this; } template template Maybe& Maybe::operator=(Maybe const& rhs) { if (rhs) emplace(*rhs); else reset(); return *this; } template Maybe& Maybe::operator=(Maybe&& rhs) { if (&rhs == this) return *this; if (rhs) emplace(rhs.take()); else reset(); return *this; } template bool Maybe::isValid() const { return m_initialized; } template bool Maybe::isNothing() const { return !m_initialized; } template Maybe::operator bool() const { return m_initialized; } template auto Maybe::ptr() const -> PointerConstType { if (m_initialized) return &m_data; return nullptr; } template auto Maybe::ptr() -> PointerType { if (m_initialized) return &m_data; return nullptr; } template auto Maybe::operator-> () const -> PointerConstType { if (!m_initialized) throw InvalidMaybeAccessException(); return &m_data; } template auto Maybe::operator->() -> PointerType { if (!m_initialized) throw InvalidMaybeAccessException(); return &m_data; } template auto Maybe::operator*() const -> RefConstType { return get(); } template auto Maybe::operator*() -> RefType { return get(); } template bool Maybe::operator==(Maybe const& rhs) const { if (!m_initialized && !rhs.m_initialized) return true; if (m_initialized && rhs.m_initialized) return get() == rhs.get(); return false; } template bool Maybe::operator!=(Maybe const& rhs) const { return !operator==(rhs); } template bool Maybe::operator<(Maybe const& rhs) const { if (m_initialized && rhs.m_initialized) return get() < rhs.get(); if (!m_initialized && rhs.m_initialized) return true; return false; } template auto Maybe::get() const -> RefConstType { if (!m_initialized) throw InvalidMaybeAccessException(); return m_data; } template auto Maybe::get() -> RefType { if (!m_initialized) throw InvalidMaybeAccessException(); return m_data; } template T Maybe::value(T def) const { if (m_initialized) return m_data; else return def; } template Maybe Maybe::orMaybe(Maybe const& other) const { if (m_initialized) return *this; else return other; } template T Maybe::take() { if (!m_initialized) throw InvalidMaybeAccessException(); T val(std::move(m_data)); reset(); return val; } template bool Maybe::put(T& t) { if (m_initialized) { t = std::move(m_data); reset(); return true; } else { return false; } } template void Maybe::set(T const& t) { emplace(t); } template void Maybe::set(T&& t) { emplace(std::forward(t)); } template template void Maybe::emplace(Args&&... t) { reset(); new (&m_data) T(std::forward(t)...); m_initialized = true; } template void Maybe::reset() { if (m_initialized) { m_initialized = false; m_data.~T(); } } template template auto Maybe::apply(Function&& function) const -> Maybe()))>::type> { if (!isValid()) return {}; return function(get()); } template template void Maybe::exec(Function&& function) { if (isValid()) function(get()); } template template auto Maybe::sequence(Function function) const -> decltype(function(std::declval())) { if (!isValid()) return {}; return function(get()); } template std::ostream& operator<<(std::ostream& os, Maybe const& v) { if (v) return os << "Just (" << *v << ")"; else return os << "Nothing"; } template size_t hash>::operator()(Maybe const& m) const { if (!m) return 0; else return hasher(*m); } } template struct fmt::formatter> : ostream_formatter {}; #endif