#pragma once #include "StarVariant.hpp" namespace Star { STAR_EXCEPTION(EitherException, StarException); template struct EitherLeftValue { Value value; }; template struct EitherRightValue { Value value; }; template EitherLeftValue makeLeft(Value value); template EitherRightValue makeRight(Value value); // Container that contains exactly one of either Left or Right. template class Either { public: // Constructs Either that contains a default constructed Left value Either(); Either(EitherLeftValue left); Either(EitherRightValue right); template Either(EitherLeftValue left); template Either(EitherRightValue right); Either(Either const& rhs); Either(Either&& rhs); Either& operator=(Either const& rhs); Either& operator=(Either&& rhs); template Either& operator=(EitherLeftValue left); template Either& operator=(EitherRightValue right); bool isLeft() const; bool isRight() const; void setLeft(Left left); void setRight(Right left); // left() and right() throw EitherException on invalid access Left const& left() const; Right const& right() const; Left& left(); Right& right(); Maybe maybeLeft() const; Maybe maybeRight() const; // leftPtr() and rightPtr() do not throw on invalid access Left const* leftPtr() const; Right const* rightPtr() const; Left* leftPtr(); Right* rightPtr(); private: typedef EitherLeftValue LeftType; typedef EitherRightValue RightType; Variant m_value; }; template EitherLeftValue makeLeft(Value value) { return {std::move(value)}; } template EitherRightValue makeRight(Value value) { return {std::move(value)}; } template Either::Either() {} template Either::Either(EitherLeftValue left) : m_value(std::move(left)) {} template Either::Either(EitherRightValue right) : m_value(std::move(right)) {} template template Either::Either(EitherLeftValue left) : Either(LeftType{std::move(left.value)}) {} template template Either::Either(EitherRightValue right) : Either(RightType{std::move(right.value)}) {} template Either::Either(Either const& rhs) : m_value(rhs.m_value) {} template Either::Either(Either&& rhs) : m_value(std::move(rhs.m_value)) {} template Either& Either::operator=(Either const& rhs) { m_value = rhs.m_value; return *this; } template Either& Either::operator=(Either&& rhs) { m_value = std::move(rhs.m_value); return *this; } template template Either& Either::operator=(EitherLeftValue left) { m_value = LeftType{std::move(left.value)}; return *this; } template template Either& Either::operator=(EitherRightValue right) { m_value = RightType{std::move(right.value)}; return *this; } template bool Either::isLeft() const { return m_value.template is(); } template bool Either::isRight() const { return m_value.template is(); } template void Either::setLeft(Left left) { m_value = LeftType{std::move(left)}; } template void Either::setRight(Right right) { m_value = RightType{std::move(right)}; } template Left const& Either::left() const { if (auto l = leftPtr()) return *l; throw EitherException("Improper access of left side of Either"); } template Right const& Either::right() const { if (auto r = rightPtr()) return *r; throw EitherException("Improper access of right side of Either"); } template Left& Either::left() { if (auto l = leftPtr()) return *l; throw EitherException("Improper access of left side of Either"); } template Right& Either::right() { if (auto r = rightPtr()) return *r; throw EitherException("Improper access of right side of Either"); } template Maybe Either::maybeLeft() const { if (auto l = leftPtr()) return *l; return {}; } template Maybe Either::maybeRight() const { if (auto r = rightPtr()) return *r; return {}; } template Left const* Either::leftPtr() const { if (auto l = m_value.template ptr()) return &l->value; return nullptr; } template Right const* Either::rightPtr() const { if (auto r = m_value.template ptr()) return &r->value; return nullptr; } template Left* Either::leftPtr() { if (auto l = m_value.template ptr()) return &l->value; return nullptr; } template Right* Either::rightPtr() { if (auto r = m_value.template ptr()) return &r->value; return nullptr; } }