diff options
Diffstat (limited to 'source/core/StarEither.hpp')
-rw-r--r-- | source/core/StarEither.hpp | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/source/core/StarEither.hpp b/source/core/StarEither.hpp new file mode 100644 index 0000000..287e4bc --- /dev/null +++ b/source/core/StarEither.hpp @@ -0,0 +1,243 @@ +#ifndef STAR_EITHER_HPP +#define STAR_EITHER_HPP + +#include "StarVariant.hpp" + +namespace Star { + +STAR_EXCEPTION(EitherException, StarException); + +template <typename Value> +struct EitherLeftValue { + Value value; +}; + +template <typename Value> +struct EitherRightValue { + Value value; +}; + +template <typename Value> +EitherLeftValue<Value> makeLeft(Value value); + +template <typename Value> +EitherRightValue<Value> makeRight(Value value); + +// Container that contains exactly one of either Left or Right. +template <typename Left, typename Right> +class Either { +public: + // Constructs Either that contains a default constructed Left value + Either(); + + Either(EitherLeftValue<Left> left); + Either(EitherRightValue<Right> right); + + template <typename T> + Either(EitherLeftValue<T> left); + + template <typename T> + Either(EitherRightValue<T> right); + + Either(Either const& rhs); + Either(Either&& rhs); + + Either& operator=(Either const& rhs); + Either& operator=(Either&& rhs); + + template <typename T> + Either& operator=(EitherLeftValue<T> left); + + template <typename T> + Either& operator=(EitherRightValue<T> 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<Left> maybeLeft() const; + Maybe<Right> 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<Left> LeftType; + typedef EitherRightValue<Right> RightType; + + Variant<LeftType, RightType> m_value; +}; + +template <typename Value> +EitherLeftValue<Value> makeLeft(Value value) { + return {move(value)}; +} + +template <typename Value> +EitherRightValue<Value> makeRight(Value value) { + return {move(value)}; +} + +template <typename Left, typename Right> +Either<Left, Right>::Either() {} + +template <typename Left, typename Right> +Either<Left, Right>::Either(EitherLeftValue<Left> left) + : m_value(move(left)) {} + +template <typename Left, typename Right> +Either<Left, Right>::Either(EitherRightValue<Right> right) + : m_value(move(right)) {} + +template <typename Left, typename Right> +template <typename T> +Either<Left, Right>::Either(EitherLeftValue<T> left) + : Either(LeftType{move(left.value)}) {} + +template <typename Left, typename Right> +template <typename T> +Either<Left, Right>::Either(EitherRightValue<T> right) + : Either(RightType{move(right.value)}) {} + +template <typename Left, typename Right> +Either<Left, Right>::Either(Either const& rhs) + : m_value(rhs.m_value) {} + +template <typename Left, typename Right> +Either<Left, Right>::Either(Either&& rhs) + : m_value(move(rhs.m_value)) {} + +template <typename Left, typename Right> +Either<Left, Right>& Either<Left, Right>::operator=(Either const& rhs) { + m_value = rhs.m_value; + return *this; +} + +template <typename Left, typename Right> +Either<Left, Right>& Either<Left, Right>::operator=(Either&& rhs) { + m_value = move(rhs.m_value); + return *this; +} + +template <typename Left, typename Right> +template <typename T> +Either<Left, Right>& Either<Left, Right>::operator=(EitherLeftValue<T> left) { + m_value = LeftType{move(left.value)}; + return *this; +} + +template <typename Left, typename Right> +template <typename T> +Either<Left, Right>& Either<Left, Right>::operator=(EitherRightValue<T> right) { + m_value = RightType{move(right.value)}; + return *this; +} + +template <typename Left, typename Right> +bool Either<Left, Right>::isLeft() const { + return m_value.template is<LeftType>(); +} + +template <typename Left, typename Right> +bool Either<Left, Right>::isRight() const { + return m_value.template is<RightType>(); +} + +template <typename Left, typename Right> +void Either<Left, Right>::setLeft(Left left) { + m_value = LeftType{move(left)}; +} + +template <typename Left, typename Right> +void Either<Left, Right>::setRight(Right right) { + m_value = RightType{move(right)}; +} + +template <typename Left, typename Right> +Left const& Either<Left, Right>::left() const { + if (auto l = leftPtr()) + return *l; + throw EitherException("Improper access of left side of Either"); +} + +template <typename Left, typename Right> +Right const& Either<Left, Right>::right() const { + if (auto r = rightPtr()) + return *r; + throw EitherException("Improper access of right side of Either"); +} + +template <typename Left, typename Right> +Left& Either<Left, Right>::left() { + if (auto l = leftPtr()) + return *l; + throw EitherException("Improper access of left side of Either"); +} + +template <typename Left, typename Right> +Right& Either<Left, Right>::right() { + if (auto r = rightPtr()) + return *r; + throw EitherException("Improper access of right side of Either"); +} + +template <typename Left, typename Right> +Maybe<Left> Either<Left, Right>::maybeLeft() const { + if (auto l = leftPtr()) + return *l; + return {}; +} + +template <typename Left, typename Right> +Maybe<Right> Either<Left, Right>::maybeRight() const { + if (auto r = rightPtr()) + return *r; + return {}; +} + +template <typename Left, typename Right> +Left const* Either<Left, Right>::leftPtr() const { + if (auto l = m_value.template ptr<LeftType>()) + return &l->value; + return nullptr; +} + +template <typename Left, typename Right> +Right const* Either<Left, Right>::rightPtr() const { + if (auto r = m_value.template ptr<RightType>()) + return &r->value; + return nullptr; +} + +template <typename Left, typename Right> +Left* Either<Left, Right>::leftPtr() { + if (auto l = m_value.template ptr<LeftType>()) + return &l->value; + return nullptr; +} + +template <typename Left, typename Right> +Right* Either<Left, Right>::rightPtr() { + if (auto r = m_value.template ptr<RightType>()) + return &r->value; + return nullptr; +} + +} + +#endif |