#ifndef NEITHER_MAYBE_HPP #define NEITHER_MAYBE_HPP #include #include #include "traits.hpp" namespace neither { template struct Maybe; template <> struct Maybe {}; template struct Maybe { union { T value; }; bool const hasValue = 0; constexpr Maybe() : hasValue{0} {} constexpr Maybe(T const& value) : value{value}, hasValue{1} {} constexpr Maybe(T&& value) : value{std::move(value)}, hasValue{1} {} constexpr Maybe(Maybe) : hasValue{0} {} constexpr Maybe(Maybe const &o) : hasValue{o.hasValue} { if (o.hasValue) { new (&value)T(o.value); } } ~Maybe() { if (hasValue) { value.~T(); } } constexpr T get(T defaultValue) { return hasValue ? value : defaultValue; } constexpr T unsafeGet() { assert(hasValue && "unsafeGet must not be called on an empty Maybe"); return value; } template constexpr auto map(F const &f) const& -> Maybe { using ReturnType = decltype(f(value)); if (!hasValue) { return Maybe(); } return Maybe(f(value)); } template auto map(F const& f)&& -> Maybe { using ReturnType = decltype(f(std::move(value))); if (!hasValue) { return Maybe(); } return Maybe(f(std::move(value))); } template constexpr auto flatMap(F const& f) const& -> decltype(ensureMaybe(f(value))) { using ReturnType = decltype(f(value)); if (!hasValue) { return ReturnType(); } return f(value); } template constexpr auto flatMap(F const& f)&& -> decltype(ensureMaybe(f(std::move(value)))) { using ReturnType = decltype(f(std::move(value))); if (!hasValue) { return ReturnType(); } return f(std::move(value)); } constexpr operator bool() const { return hasValue; } }; template auto maybe(T value) -> Maybe { return {value}; } template auto maybe() -> Maybe { return {}; } template bool operator == (Maybe const& a, Maybe const& b) { if (a.hasValue) { return b.hasValue && a.value == b.value; } return !b.hasValue; } template bool operator != (Maybe const& a, Maybe const& b) { return !(a == b); } } #endif