2018-10-27 21:20:40 -07:00
# pragma once
# ifndef MESSMER_CPPUTILS_EITHER_H
# define MESSMER_CPPUTILS_EITHER_H
# include <boost/optional.hpp>
# include <iostream>
# include "assert/assert.h"
namespace cpputils {
template < class Left , class Right >
class either final {
public :
2018-12-24 18:15:08 +01:00
template < class Head , class . . . Tail , std : : enable_if_t < std : : is_constructible < Left , Head , Tail . . . > : : value & & ! std : : is_constructible < Right , Head , Tail . . . > : : value > * = nullptr >
2018-12-24 18:34:42 +01:00
either ( Head & & construct_left_head_arg , Tail & & . . . construct_left_tail_args ) noexcept ( noexcept ( std : : declval < either < Left , Right > > ( ) . _construct_left ( std : : forward < Head > ( construct_left_head_arg ) , std : : forward < Tail > ( construct_left_tail_args ) . . . ) ) )
2018-10-27 21:20:40 -07:00
: _side ( Side : : left ) {
2018-12-24 18:15:08 +01:00
_construct_left ( std : : forward < Head > ( construct_left_head_arg ) , std : : forward < Tail > ( construct_left_tail_args ) . . . ) ;
2018-10-27 21:20:40 -07:00
}
2018-12-24 18:15:08 +01:00
template < class Head , class . . . Tail , std : : enable_if_t < ! std : : is_constructible < Left , Head , Tail . . . > : : value & & std : : is_constructible < Right , Head , Tail . . . > : : value > * = nullptr >
2018-12-24 18:34:42 +01:00
either ( Head & & construct_right_head_arg , Tail & & . . . construct_right_tail_args ) noexcept ( noexcept ( std : : declval < either < Left , Right > > ( ) . _construct_right ( std : : forward < Head > ( construct_right_head_arg ) , std : : forward < Tail > ( construct_right_tail_args ) . . . ) ) )
2018-12-24 18:15:08 +01:00
: _side ( Side : : right ) {
_construct_right ( std : : forward < Head > ( construct_right_head_arg ) , std : : forward < Tail > ( construct_right_tail_args ) . . . ) ;
2018-10-27 21:20:40 -07:00
}
//TODO Try allowing copy-construction when Left/Right types are std::is_convertible
either ( const either < Left , Right > & rhs ) noexcept ( noexcept ( std : : declval < either < Left , Right > > ( ) . _construct_left ( rhs . _left ) ) & & noexcept ( std : : declval < either < Left , Right > > ( ) . _construct_right ( rhs . _right ) ) )
: _side ( rhs . _side ) {
if ( _side = = Side : : left ) {
2018-12-25 19:50:53 +01:00
_construct_left ( rhs . _left ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
} else {
2018-12-25 19:50:53 +01:00
_construct_right ( rhs . _right ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
}
}
2018-12-28 09:47:22 +01:00
either ( either < Left , Right > & & rhs ) noexcept ( noexcept ( std : : declval < either < Left , Right > > ( ) . _construct_left ( std : : move ( rhs . _left ) ) ) & & noexcept ( std : : declval < either < Left , Right > > ( ) . _construct_right ( std : : move ( rhs . _right ) ) ) )
2018-10-27 21:20:40 -07:00
: _side ( rhs . _side ) {
if ( _side = = Side : : left ) {
2018-12-25 19:50:53 +01:00
_construct_left ( std : : move ( rhs . _left ) ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
} else {
2018-12-25 19:50:53 +01:00
_construct_right ( std : : move ( rhs . _right ) ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
}
}
~ either ( ) {
_destruct ( ) ;
}
//TODO Try allowing copy-assignment when Left/Right types are std::is_convertible
2020-07-22 10:32:43 -07:00
// NOLINTNEXTLINE(cert-oop54-cpp)
2018-12-28 09:47:22 +01:00
either < Left , Right > & operator = ( const either < Left , Right > & rhs ) noexcept ( noexcept ( std : : declval < either < Left , Right > > ( ) . _construct_left ( rhs . _left ) ) & & noexcept ( std : : declval < either < Left , Right > > ( ) . _construct_right ( rhs . _right ) ) ) {
2020-07-22 10:32:43 -07:00
if ( this = = & rhs ) {
return * this ;
}
2018-10-27 21:20:40 -07:00
_destruct ( ) ;
_side = rhs . _side ;
if ( _side = = Side : : left ) {
2018-12-25 19:50:53 +01:00
_construct_left ( rhs . _left ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
} else {
2018-12-25 19:50:53 +01:00
_construct_right ( rhs . _right ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
}
return * this ;
}
2018-12-28 09:47:22 +01:00
either < Left , Right > & operator = ( either < Left , Right > & & rhs ) noexcept ( noexcept ( std : : declval < either < Left , Right > > ( ) . _construct_left ( std : : move ( rhs . _left ) ) ) & & noexcept ( std : : declval < either < Left , Right > > ( ) . _construct_right ( std : : move ( rhs . _right ) ) ) ) {
2020-07-22 10:32:43 -07:00
if ( this = = & rhs ) {
return * this ;
}
2018-10-27 21:20:40 -07:00
_destruct ( ) ;
_side = rhs . _side ;
if ( _side = = Side : : left ) {
2018-12-25 19:50:53 +01:00
_construct_left ( std : : move ( rhs . _left ) ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
} else {
2018-12-25 19:50:53 +01:00
_construct_right ( std : : move ( rhs . _right ) ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
}
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 & {
2018-12-25 10:34:40 +01:00
if ( ! is_left ( ) ) {
throw std : : logic_error ( " Tried to get left side of an either which is right. " ) ;
}
2018-12-25 19:50:53 +01:00
return _left ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
}
Left & left ( ) & {
return const_cast < Left & > ( const_cast < const either < Left , Right > * > ( this ) - > left ( ) ) ;
}
Left & & left ( ) & & {
return std : : move ( left ( ) ) ;
}
const Right & right ( ) const & {
2018-12-25 10:34:40 +01:00
if ( ! is_right ( ) ) {
throw std : : logic_error ( " Tried to get right side of an either which is left. " ) ;
}
2018-12-25 19:50:53 +01:00
return _right ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
}
Right & right ( ) & {
return const_cast < Right & > ( const_cast < const either < Left , Right > * > ( this ) - > right ( ) ) ;
}
Right & & right ( ) & & {
return std : : move ( right ( ) ) ;
}
boost : : optional < const Left & > left_opt ( ) const & noexcept {
if ( _side = = Side : : left ) {
2018-12-25 19:50:53 +01:00
return _left ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
} else {
return boost : : none ;
}
}
boost : : optional < Left & > left_opt ( ) & noexcept {
if ( _side = = Side : : left ) {
2018-12-25 19:50:53 +01:00
return _left ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
} else {
return boost : : none ;
}
}
2018-12-25 10:40:46 +01:00
// warning: opposed to the other left_opt variants, this one already moves the content and returns by value.
2018-12-29 11:58:38 +01:00
boost : : optional < Left > left_opt ( ) & & noexcept ( noexcept ( boost : : optional < Left > ( std : : move ( std : : declval < either < Left , Right > > ( ) . _left ) ) ) ) {
2018-12-25 10:40:46 +01:00
if ( _side = = Side : : left ) {
2018-12-29 22:38:54 +01:00
return std : : move ( _left ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-12-25 10:40:46 +01:00
} else {
return boost : : none ;
}
}
2018-10-27 21:20:40 -07:00
boost : : optional < const Right & > right_opt ( ) const & noexcept {
if ( _side = = Side : : right ) {
2018-12-25 19:50:53 +01:00
return _right ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
} else {
return boost : : none ;
}
}
boost : : optional < Right & > right_opt ( ) & noexcept {
if ( _side = = Side : : right ) {
2018-12-25 19:50:53 +01:00
return _right ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
} else {
return boost : : none ;
}
}
2018-12-25 10:40:46 +01:00
// warning: opposed to the other left_opt variants, this one already moves the content and returns by value.
2018-12-29 11:58:38 +01:00
boost : : optional < Right > right_opt ( ) & & noexcept ( noexcept ( boost : : optional < Right > ( std : : move ( std : : declval < either < Left , Right > > ( ) . _right ) ) ) ) {
2018-12-25 10:40:46 +01:00
if ( _side = = Side : : right ) {
2018-12-29 22:38:54 +01:00
return std : : move ( _right ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-12-25 10:40:46 +01:00
} else {
return boost : : none ;
}
}
2018-10-27 21:20:40 -07:00
private :
union {
Left _left ;
Right _right ;
} ;
enum class Side : uint8_t { left , right } _side ;
explicit either ( Side side ) noexcept : _side ( side ) { }
template < typename . . . Args >
void _construct_left ( Args & & . . . args ) noexcept ( noexcept ( new Left ( std : : forward < Args > ( args ) . . . ) ) ) {
2018-12-25 19:50:53 +01:00
new ( & _left ) Left ( std : : forward < Args > ( args ) . . . ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
}
template < typename . . . Args >
void _construct_right ( Args & & . . . args ) noexcept ( noexcept ( new Right ( std : : forward < Args > ( args ) . . . ) ) ) {
2018-12-25 19:50:53 +01:00
new ( & _right ) Right ( std : : forward < Args > ( args ) . . . ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
}
void _destruct ( ) noexcept {
if ( _side = = Side : : left ) {
2018-12-25 19:50:53 +01:00
_left . ~ Left ( ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
} else {
2018-12-25 19:50:53 +01:00
_right . ~ Right ( ) ; // NOLINT(cppcoreguidelines-pro-type-union-access)
2018-10-27 21:20:40 -07:00
}
}
template < typename Left_ , typename Right_ , typename . . . Args >
2018-12-24 18:34:42 +01:00
friend either < Left_ , Right_ > make_left ( Args & & . . . args ) /* TODO noexcept(noexcept(std::declval<either<Left, Right>>()._construct_left(std::forward<Args>(args)...))) */ ;
2018-10-27 21:20:40 -07:00
template < typename Left_ , typename Right_ , typename . . . Args >
2018-12-24 18:34:42 +01:00
friend either < Left_ , Right_ > make_right ( Args & & . . . args ) /* TODO noexcept(noexcept(std::declval<either<Left, Right>>()._construct_right(std::forward<Args>(args)...))) */ ;
2018-10-27 21:20:40 -07:00
} ;
template < class Left , class Right >
2018-12-24 18:15:08 +01:00
inline bool operator = = ( const either < Left , Right > & lhs , const either < Left , Right > & rhs ) noexcept ( noexcept ( std : : declval < Left > ( ) = = std : : declval < Left > ( ) ) & & noexcept ( std : : declval < Right > ( ) = = std : : declval < Right > ( ) ) ) {
2018-10-27 21:20:40 -07:00
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 < class Left , class Right >
2018-12-24 18:15:08 +01:00
inline bool operator ! = ( const either < Left , Right > & lhs , const either < Left , Right > & rhs ) noexcept ( noexcept ( operator = = ( lhs , rhs ) ) ) {
2018-10-27 21:20:40 -07:00
return ! operator = = ( lhs , rhs ) ;
}
template < class Left , class Right >
2018-12-24 18:15:08 +01:00
inline std : : ostream & operator < < ( std : : ostream & stream , const either < Left , Right > & value ) {
2018-10-27 21:20:40 -07:00
if ( value . is_left ( ) ) {
stream < < " Left( " < < value . left ( ) < < " ) " ;
} else {
stream < < " Right( " < < value . right ( ) < < " ) " ;
}
return stream ;
}
template < typename Left , typename Right , typename . . . Args >
2018-12-24 18:15:08 +01:00
inline either < Left , Right > make_left ( Args & & . . . args ) /* TODO noexcept(noexcept(std::declval<either<Left, Right>>()._construct_left(std::forward<Args>(args)...))) */ {
2018-10-27 21:20:40 -07:00
either < Left , Right > result ( either < Left , Right > : : Side : : left ) ;
result . _construct_left ( std : : forward < Args > ( args ) . . . ) ;
return result ;
}
template < typename Left , typename Right , typename . . . Args >
2018-12-24 18:15:08 +01:00
inline either < Left , Right > make_right ( Args & & . . . args ) /* TODO noexcept(noexcept(std::declval<either<Left, Right>>()._construct_right(std::forward<Args>(args)...))) */ {
2018-10-27 21:20:40 -07:00
either < Left , Right > result ( either < Left , Right > : : Side : : right ) ;
result . _construct_right ( std : : forward < Args > ( args ) . . . ) ;
return result ;
}
}
# endif