#pragma once #ifndef MESSMER_CPP_UTILS_EITHER_H #define MESSMER_CPP_UTILS_EITHER_H #include namespace cpputils { template class either final { public: //TODO Try allowing construction with any type that std::is_convertible to Left or Right. either(const Left &left): _side(Side::left) { _construct_left(left); } either(Left &&left): _side(Side::left) { _construct_left(std::move(left)); } either(const Right &right): _side(Side::right) { _construct_right(right); } either(Right &&right): _side(Side::right) { _construct_right(std::move(right)); } //TODO Try allowing copy-construction when Left/Right types are std::is_convertible either(const either &rhs): _side(rhs._side) { if(_side == Side::left) { _construct_left(rhs._left); } else { _construct_right(rhs._right); } } either(either &&rhs): _side(rhs._side) { if(_side == Side::left) { _construct_left(std::move(rhs._left)); } else { _construct_right(std::move(rhs._right)); } } ~either() { _destruct(); } //TODO Try allowing copy-assignment when Left/Right types are std::is_convertible either &operator=(const either &rhs) { _destruct(); _side = rhs._side; if (_side == Side::left) { _construct_left(rhs._left); } else { _construct_right(rhs._right); } return *this; } either &operator=(either &&rhs) { _destruct(); _side = rhs._side; if (_side == Side::left) { _construct_left(std::move(rhs._left)); } else { _construct_right(std::move(rhs._right)); } return *this; } //TODO fold, map_left, map_right, left_or_else(val), right_or_else(val), left_or_else(func), right_or_else(func) bool is_left() const { return _side == Side::left; } bool is_right() const { return _side == Side::right; } const Left &left() const& { return _left; } Left &left() & { return const_cast(const_cast*>(this)->left()); } Left &&left() && { return std::move(left()); } const Right &right() const& { return _right; } Right &right() & { return const_cast(const_cast*>(this)->right()); } Right &&right() && { return std::move(right()); } boost::optional left_opt() const& { if (_side == Side::left) { return _left; } else { return boost::none; } } boost::optional left_opt() & { if (_side == Side::left) { return _left; } else { return boost::none; } } boost::optional left_opt() && { if (_side == Side::left) { return std::move(_left); } else { return boost::none; } } boost::optional right_opt() const& { if (_side == Side::right) { return _right; } else { return boost::none; } } boost::optional right_opt() & { if (_side == Side::right) { return _right; } else { return boost::none; } } boost::optional right_opt() && { if (_side == Side::right) { return std::move(_right); } else { return boost::none; } } private: union { Left _left; Right _right; }; enum class Side : unsigned char {left, right} _side; either(Side side): _side(side) {} template void _construct_left(Args&&... args) { new(&_left)Left(std::forward(args)...); } template void _construct_right(Args&&... args) { new(&_right)Right(std::forward(args)...); } void _destruct() { if (_side == Side::left) { _left.~Left(); } else { _right.~Right(); } } template friend either make_left(Args&&... args); template friend either make_right(Args&&... args); }; template bool operator==(const either &lhs, const either &rhs) { if (lhs.is_left() != rhs.is_left()) { return false; } if (lhs.is_left()) { return lhs.left() == rhs.left(); } else { return lhs.right() == rhs.right(); } } template bool operator!=(const either &lhs, const either &rhs) { return !operator==(lhs, rhs); } template std::ostream &operator<<(std::ostream &stream, const either &value) { if (value.is_left()) { stream << "Left(" << value.left() << ")"; } else { stream << "Right(" << value.right() << ")"; } return stream; } template either make_left(Args&&... args) { either result(either::Side::left); result._construct_left(std::forward(args)...); return result; } template either make_right(Args&&... args) { either result(either::Side::right); result._construct_right(std::forward(args)...); return result; } } #endif