From 4de6f1d8d94e05d0b1648b1cac17ec9e45d898e6 Mon Sep 17 00:00:00 2001 From: Sebastian Messmer Date: Mon, 24 Dec 2018 18:54:22 +0100 Subject: [PATCH] Re-enable left_opt() and right_opt() for rvalue references --- src/cpp-utils/either.h | 388 +++++++++++++++++++++-------------------- 1 file changed, 201 insertions(+), 187 deletions(-) diff --git a/src/cpp-utils/either.h b/src/cpp-utils/either.h index 88778498..98cc13f3 100644 --- a/src/cpp-utils/either.h +++ b/src/cpp-utils/either.h @@ -8,203 +8,217 @@ namespace cpputils { - template - class either final { - public: - template::value && !std::is_constructible::value>* = nullptr> - either(Head&& construct_left_head_arg, Tail&&... construct_left_tail_args) noexcept(noexcept(std::declval>()._construct_left(std::forward(construct_left_head_arg), std::forward(construct_left_tail_args)...))) - : _side(Side::left) { - _construct_left(std::forward(construct_left_head_arg), std::forward(construct_left_tail_args)...); - } +template +class either final { +public: + template::value && !std::is_constructible::value>* = nullptr> + either(Head&& construct_left_head_arg, Tail&&... construct_left_tail_args) noexcept(noexcept(std::declval>()._construct_left(std::forward(construct_left_head_arg), std::forward(construct_left_tail_args)...))) + : _side(Side::left) { + _construct_left(std::forward(construct_left_head_arg), std::forward(construct_left_tail_args)...); + } - template::value && std::is_constructible::value>* = nullptr> - either(Head&& construct_right_head_arg, Tail&&... construct_right_tail_args) noexcept(noexcept(std::declval>()._construct_right(std::forward(construct_right_head_arg), std::forward(construct_right_tail_args)...))) - : _side(Side::right) { - _construct_right(std::forward(construct_right_head_arg), std::forward(construct_right_tail_args)...); - } + template::value && std::is_constructible::value>* = nullptr> + either(Head&& construct_right_head_arg, Tail&&... construct_right_tail_args) noexcept(noexcept(std::declval>()._construct_right(std::forward(construct_right_head_arg), std::forward(construct_right_tail_args)...))) + : _side(Side::right) { + _construct_right(std::forward(construct_right_head_arg), std::forward(construct_right_tail_args)...); + } - //TODO Try allowing copy-construction when Left/Right types are std::is_convertible - either(const either &rhs) noexcept(noexcept(std::declval>()._construct_left(rhs._left)) && noexcept(std::declval>()._construct_right(rhs._right))) - : _side(rhs._side) { - if(_side == Side::left) { - _construct_left(rhs._left); - } else { - _construct_right(rhs._right); - } - } - - either(either &&rhs) noexcept(noexcept(_construct_left(std::move(rhs._left))) && noexcept(_construct_right(std::move(rhs._right)))) - : _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) noexcept(noexcept(_construct_left(rhs._left)) && noexcept(_construct_right(rhs._right))) { - _destruct(); - _side = rhs._side; - if (_side == Side::left) { - _construct_left(rhs._left); - } else { - _construct_right(rhs._right); - } - return *this; - } - - either &operator=(either &&rhs) noexcept(noexcept(_construct_left(std::move(rhs._left))) && noexcept(_construct_right(std::move(rhs._right)))) { - _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 noexcept { - return _side == Side::left; - } - - bool is_right() const noexcept { - return _side == Side::right; - } - - const Left &left() const& { - ASSERT(is_left(), "Tried to get left side of an either which is right."); - return _left; - } - Left &left() & { - return const_cast(const_cast*>(this)->left()); - } - Left &&left() && { - return std::move(left()); - } - - const Right &right() const& { - ASSERT(is_right(), "Tried to get right side of an either which is left."); - return _right; - } - Right &right() & { - return const_cast(const_cast*>(this)->right()); - } - Right &&right() && { - return std::move(right()); - } - - boost::optional left_opt() const& noexcept { - if (_side == Side::left) { - return _left; - } else { - return boost::none; - } - } - boost::optional left_opt() & noexcept { - if (_side == Side::left) { - return _left; - } else { - return boost::none; - } - } - // left_opt()&& not offered because optional doesn't work - - boost::optional right_opt() const& noexcept { - if (_side == Side::right) { - return _right; - } else { - return boost::none; - } - } - boost::optional right_opt() & noexcept { - if (_side == Side::right) { - return _right; - } else { - return boost::none; - } - } - // right_opt()&& not offered because optional doesn't work - - private: - union { - Left _left; - Right _right; - }; - enum class Side : uint8_t {left, right} _side; - - explicit either(Side side) noexcept : _side(side) {} - - template - void _construct_left(Args&&... args) noexcept(noexcept(new Left(std::forward(args)...))) { - new(&_left)Left(std::forward(args)...); - } - template - void _construct_right(Args&&... args) noexcept(noexcept(new Right(std::forward(args)...))) { - new(&_right)Right(std::forward(args)...); - } - void _destruct() noexcept { - if (_side == Side::left) { - _left.~Left(); - } else { - _right.~Right(); - } - } - - template - friend either make_left(Args&&... args) /* TODO noexcept(noexcept(std::declval>()._construct_left(std::forward(args)...))) */; - - template - friend either make_right(Args&&... args) /* TODO noexcept(noexcept(std::declval>()._construct_right(std::forward(args)...))) */; - }; - - template - inline bool operator==(const either &lhs, const either &rhs) noexcept(noexcept(std::declval() == std::declval()) && noexcept(std::declval() == std::declval())) { - if (lhs.is_left() != rhs.is_left()) { - return false; - } - if (lhs.is_left()) { - return lhs.left() == rhs.left(); - } else { - return lhs.right() == rhs.right(); - } + //TODO Try allowing copy-construction when Left/Right types are std::is_convertible + either(const either &rhs) noexcept(noexcept(std::declval>()._construct_left(rhs._left)) && noexcept(std::declval>()._construct_right(rhs._right))) + : _side(rhs._side) { + if(_side == Side::left) { + _construct_left(rhs._left); + } else { + _construct_right(rhs._right); } + } - template - inline bool operator!=(const either &lhs, const either &rhs) noexcept(noexcept(operator==(lhs, rhs))) { - return !operator==(lhs, rhs); + either(either &&rhs) noexcept(noexcept(_construct_left(std::move(rhs._left))) && noexcept(_construct_right(std::move(rhs._right)))) + : _side(rhs._side) { + if(_side == Side::left) { + _construct_left(std::move(rhs._left)); + } else { + _construct_right(std::move(rhs._right)); } + } - template - inline std::ostream &operator<<(std::ostream &stream, const either &value) { - if (value.is_left()) { - stream << "Left(" << value.left() << ")"; - } else { - stream << "Right(" << value.right() << ")"; - } - return stream; - } + ~either() { + _destruct(); + } - template - inline either make_left(Args&&... args) /* TODO noexcept(noexcept(std::declval>()._construct_left(std::forward(args)...))) */ { - either result(either::Side::left); - result._construct_left(std::forward(args)...); - return result; + //TODO Try allowing copy-assignment when Left/Right types are std::is_convertible + either &operator=(const either &rhs) noexcept(noexcept(_construct_left(rhs._left)) && noexcept(_construct_right(rhs._right))) { + _destruct(); + _side = rhs._side; + if (_side == Side::left) { + _construct_left(rhs._left); + } else { + _construct_right(rhs._right); } + return *this; + } - template - inline either make_right(Args&&... args) /* TODO noexcept(noexcept(std::declval>()._construct_right(std::forward(args)...))) */ { - either result(either::Side::right); - result._construct_right(std::forward(args)...); - return result; + either &operator=(either &&rhs) noexcept(noexcept(_construct_left(std::move(rhs._left))) && noexcept(_construct_right(std::move(rhs._right)))) { + _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 noexcept { + return _side == Side::left; + } + + bool is_right() const noexcept { + return _side == Side::right; + } + + const Left &left() const& { + ASSERT(is_left(), "Tried to get left side of an either which is right."); + return _left; + } + Left &left() & { + return const_cast(const_cast*>(this)->left()); + } + Left &&left() && { + return std::move(left()); + } + + const Right &right() const& { + ASSERT(is_right(), "Tried to get right side of an either which is left."); + return _right; + } + Right &right() & { + return const_cast(const_cast*>(this)->right()); + } + Right &&right() && { + return std::move(right()); + } + + boost::optional left_opt() const& noexcept { + if (_side == Side::left) { + return _left; + } else { + return boost::none; + } + } + boost::optional left_opt() & noexcept { + if (_side == Side::left) { + return _left; + } else { + return boost::none; + } + } + // warning: opposed to the other left_opt variants, this one already moves the content and returns by value. + boost::optional left_opt() && noexcept(noexcept(boost::optional(std::move(_left)))) { + if (_side == Side::left) { + return std::move(_left); + } else { + return boost::none; + } + } + + boost::optional right_opt() const& noexcept { + if (_side == Side::right) { + return _right; + } else { + return boost::none; + } + } + boost::optional right_opt() & noexcept { + if (_side == Side::right) { + return _right; + } else { + return boost::none; + } + } + // warning: opposed to the other left_opt variants, this one already moves the content and returns by value. + boost::optional right_opt() && noexcept(noexcept(boost::optional(std::move(_right)))) { + if (_side == Side::right) { + return std::move(_right); + } else { + return boost::none; + } + } + +private: + union { + Left _left; + Right _right; + }; + enum class Side : uint8_t {left, right} _side; + + explicit either(Side side) noexcept : _side(side) {} + + template + void _construct_left(Args&&... args) noexcept(noexcept(new Left(std::forward(args)...))) { + new(&_left)Left(std::forward(args)...); + } + template + void _construct_right(Args&&... args) noexcept(noexcept(new Right(std::forward(args)...))) { + new(&_right)Right(std::forward(args)...); + } + void _destruct() noexcept { + if (_side == Side::left) { + _left.~Left(); + } else { + _right.~Right(); + } + } + + template + friend either make_left(Args&&... args) /* TODO noexcept(noexcept(std::declval>()._construct_left(std::forward(args)...))) */; + + template + friend either make_right(Args&&... args) /* TODO noexcept(noexcept(std::declval>()._construct_right(std::forward(args)...))) */; +}; + +template +inline bool operator==(const either &lhs, const either &rhs) noexcept(noexcept(std::declval() == std::declval()) && noexcept(std::declval() == std::declval())) { + 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 +inline bool operator!=(const either &lhs, const either &rhs) noexcept(noexcept(operator==(lhs, rhs))) { + return !operator==(lhs, rhs); +} + +template +inline 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 +inline either make_left(Args&&... args) /* TODO noexcept(noexcept(std::declval>()._construct_left(std::forward(args)...))) */ { + either result(either::Side::left); + result._construct_left(std::forward(args)...); + return result; +} + +template +inline either make_right(Args&&... args) /* TODO noexcept(noexcept(std::declval>()._construct_right(std::forward(args)...))) */ { + either result(either::Side::right); + result._construct_right(std::forward(args)...); + return result; +} }