Support Class Library
A set of tools providing classes and utility
Either.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <utility>
4 #include <scl/macros.h>
10 #include <scl/utils/Optional.h>
11 
12 namespace scl{
13  namespace utils{
21  template <class Lhs, class Rhs>
22  class Either{
23  public:
28  using left_type = Lhs;
29 
34  using right_type = Rhs;
35 
36  protected:
37  static constexpr bool is_copyable(){
38  return META::is_copyable<Lhs>()
39  && META::is_copyable<Rhs>();
40  }
41 
42  static constexpr bool is_movable(){
43  return META::is_movable<Lhs>()
44  && META::is_movable<Rhs>();
45  }
46 
50  struct payload_t final{
56 
62 
63  payload_t() = default;
64  ~payload_t() = default;
65 
66  template <class = META::enable_if_t<
68  >>
69  explicit payload_t(payload_t&& p) : left{std::move(p.left)}, right{std::move(p.right)} {
70  }
71 
72  template <class = META::enable_if_t<
74  >>
75  explicit payload_t(const payload_t& p) : left{p.left}, right{p.right} {
76  }
77 
78  template <class = META::void_t<
81  >
82  >>
84  this->left = p.left;
85  this->right = p.right;
86  return *this;
87  };
88 
89  template <class = META::void_t<
92  >
93  >>
95  this->left = std::move(p.left);
96  this->right = std::move(p.right);
97  return *this;
98  }
99  };
100 
105  struct lhs_tag final{};
106 
111  struct rhs_tag final{};
112 
117  bool lhs = false;
118 
124 
125 
126  protected:
131  template <class = META::enable_if_t<
132  META::is_movable<Lhs>()
133  >>
134  Either(lhs_tag, Lhs&& lhs) : lhs{true} {
135  this->payload.left = std::move(lhs);
136  }
137 
142  template <class = META::enable_if_t<
143  META::is_copyable<Lhs>()
144  >>
145  Either(lhs_tag, const Lhs& lhs) : lhs{true} {
146  this->payload.left = lhs;
147  }
148 
153  template <class = META::void_t<META::enable_if_t<
154  META::is_movable<Rhs>()
155  >>>
156  Either(rhs_tag, Rhs&& rhs) : lhs{false} {
157  this->payload.right = std::move(rhs);
158  }
159 
164  template <class = META::void_t<META::enable_if_t<
165  META::is_copyable<Rhs>()
166  >>>
167  Either(rhs_tag, const Rhs& rhs) : lhs{false} {
168  this->payload.right = rhs;
169  }
170 
175  static void leftVoidVisitor(const Lhs& lhs){}
176 
181  static void rightVoidVisitor(const Rhs& rhs){}
182 
183  public:
184  Either() = delete;
185 
186  template <class = META::void_t<
187  META::enable_if_t<is_copyable()>
188  >>
189  Either(const Either& other) : payload{}, lhs{other.lhs} {
190  if(lhs)
191  this->payload.left = other.getLeft();
192  else
193  this->payload.right = other.getRight();
194  }
195 
196  template <class = META::void_t<
197  META::enable_if_t<is_copyable()>
198  >>
199  Either& operator=(const Either& other){
200  this->lhs = other.lhs;
201  if(lhs)
202  this->payload.left = other.getLeft();
203  else
204  this->payload.right = other.getRight();
205 
206  return *this;
207  }
208 
209  template <class = META::void_t<
210  META::enable_if_t<is_movable()>
211  >>
212  Either(Either&& other) noexcept : payload{}, lhs{other.lhs} {
213  if(lhs)
214  this->payload.left = std::move(other.payload.left);
215  else
216  this->payload.right = std::move(other.payload.right);
217  }
218 
219  template <class = META::void_t<
220  META::enable_if_t<is_movable()>
221  >>
222  Either& operator=(Either&& other) noexcept{
223  this->lhs = other.lhs;
224  if(lhs)
225  this->payload.left = std::move(other.payload.left);
226  else
227  this->payload.right = std::move(other.payload.right);
228 
229  return *this;
230  };
231 
237  template <class L>
238  static Either left(L&& lhs){
239  return Either(lhs_tag{}, std::forward<L>(lhs));
240  }
241 
245  template <class L>
246  static Either Left(L&& lhs){
247  return left(std::forward<L>(lhs));
248  }
249 
256  template <class... Args>
257  static Either emplaceLeft(Args&&... args){
258  return left(Lhs{std::forward<Args>(args)...});
259  }
260 
267  template <class... Args>
268  static Either emplaceRight(Args&&... args){
269  return right(Rhs{std::forward<Args>(args)...});
270  }
271 
277  template <class R>
278  static Either right(R&& rhs){
279  return Either(rhs_tag{}, std::forward<R>(rhs));
280  }
281 
285  template <class R>
286  static Either Right(R&& rhs){
287  return right(std::forward<R>(rhs));
288  }
289 
294  bool hasLeft() const{ return this->lhs; }
295 
300  bool hasRight() const{ return !this->hasLeft(); }
301 
307  const Lhs& getLeft() const{
308  if(!this->hasLeft())
310 
311  return this->payload.left.get();
312  }
313 
319  const Rhs& getRight() const{
320  if(!this->hasRight())
322 
323  return this->payload.right.get();
324  }
325 
334  template <class LeftVisitor, class RightVisitor>
335  const Either& visit(LeftVisitor visitLeft, RightVisitor visitRight) const{
336  if(this->hasLeft()) {
337  const Lhs& left = this->getLeft();
338  visitLeft(left);
339  }else {
340  const Rhs& right = this->getRight();
341  visitRight(right);
342  }
343 
344  return *this;
345  }
346 
353  template <class LeftVisitor>
354  const Either& doIfLeft(LeftVisitor visitLeft) const{
355  return this->visit(visitLeft, Either::rightVoidVisitor);
356  }
357 
364  template <class RightVisitor>
365  const Either& doIfRight(RightVisitor visitRight) const{
366  return this->visit(Either::leftVoidVisitor, visitRight);
367  }
368 
376  template <class NewLhs, class Mapper>
377  Either<NewLhs, Rhs> mapLeftTo(Mapper mapper) const{
378  if(this->hasLeft()){
379  const Lhs& left = this->getLeft();
380  return Either<NewLhs, Rhs>::Left(mapper(left));
381  }
382 
383  return Either<NewLhs, Rhs>::Right(this->getRight());
384  }
385 
393  template <class NewRhs, class Mapper>
394  Either<Lhs, NewRhs> mapRightTo(Mapper mapper) const{
395  if(this->hasRight()){
396  const Rhs& right = this->getRight();
397  return Either<Lhs, NewRhs>::Right(mapper(right));
398  }
399 
400  return Either<Lhs, NewRhs>::Left(this->getLeft());
401  }
402 
413  template <class NewLhs, class NewRhs, class MapperLeft, class MapperRight>
414  Either<NewLhs, NewRhs> mapTo(MapperLeft mapLeft, MapperRight mapRight) const{
415  if(this->hasLeft()){
416  const Lhs& left = this->getLeft();
417  return Either<NewLhs, NewRhs>::Left(mapLeft(left));
418  }else{
419  //this->hasRight()
420  const Rhs& right = this->getRight();
421  return Either<NewLhs, NewRhs>::Right(mapRight(right));
422  }
423  }
424 
430  Lhs leftOr(const Lhs& defaultValue) const{
431  return this->payload.left.orElse(defaultValue);
432  }
433 
439  Rhs rightOr(const Rhs& defaultValue) const{
440  return this->payload.right.orElse(defaultValue);
441  }
442  };
443  }
444 }
Either(lhs_tag, Lhs &&lhs)
Construct the LHS alternative (rvalue)
Definition: Either.h:134
void void_t
The must have.
Definition: void_t.h:10
value_type left_type
The type of the left alternative.
Definition: Either.h:28
payload_t & operator=(const payload_t &p)
Definition: Either.h:83
error_type right_type
The type of the right alternative.
Definition: Either.h:34
static void rightVoidVisitor(const Rhs &rhs)
Handy visitor that does nothing on the RHS.
Definition: Either.h:181
Tag used to construct the LHS member (in case both are convertible types)
Definition: Either.h:105
const Either & doIfRight(RightVisitor visitRight) const
Execute a callback if RHS is the active alternative.
Definition: Either.h:365
Global namespace of the SCL.
Definition: alias.hpp:3
static constexpr bool is_copyable()
Definition: Either.h:37
bool hasLeft() const
Determines whether or not LHS is the active alternative.
Definition: Either.h:294
static Either emplaceLeft(Args &&... args)
Constructs a Lhs in place.
Definition: Either.h:257
Lhs leftOr(const Lhs &defaultValue) const
Tries to get the LHS value and fallback to default if not.
Definition: Either.h:430
payload_t(payload_t &&p)
Definition: Either.h:69
Either(const Either &other)
Definition: Either.h:189
Rhs rightOr(const Rhs &defaultValue) const
Tries to get the RHS value and fallback to default if not.
Definition: Either.h:439
static Either left(L &&lhs)
Construct the LHS.
Definition: Either.h:238
payload_t payload
Stores the actual alternative payload.
Definition: Either.h:123
payload_t & operator=(payload_t &&p)
Definition: Either.h:94
static InvalidEitherAccess leftWhenRight()
Helper function that creates an InvaidEitherAccess when trying to access Lhs when Rhs is active...
payload_t(const payload_t &p)
Definition: Either.h:75
bool lhs
Determines whether or ot the LHS member is the current alternative.
Definition: Either.h:117
Optional< Lhs > left
The holder of the left alternative.
Definition: Either.h:55
Alternative type that stores one or the other.
Definition: Either.h:22
Either(Either &&other) noexcept
Definition: Either.h:212
Either(rhs_tag, const Rhs &rhs)
Construct the RHS alternative (lvalue)
Definition: Either.h:167
static Either Right(R &&rhs)
Alias for Either::right.
Definition: Either.h:286
static Either right(R &&rhs)
Construct the RHS.
Definition: Either.h:278
Either & operator=(const Either &other)
Definition: Either.h:199
const Either & visit(LeftVisitor visitLeft, RightVisitor visitRight) const
Visit this Either.
Definition: Either.h:335
Either & operator=(Either &&other) noexcept
Definition: Either.h:222
const Rhs & getRight() const
Attempts to get the RHS value.
Definition: Either.h:319
Either< Lhs, NewRhs > mapRightTo(Mapper mapper) const
Maps the RHS if there&#39;s any.
Definition: Either.h:394
static Either Left(L &&lhs)
Alias for Either::left.
Definition: Either.h:246
Either< NewLhs, Rhs > mapLeftTo(Mapper mapper) const
Maps the LHS if there&#39;s any.
Definition: Either.h:377
Either< NewLhs, NewRhs > mapTo(MapperLeft mapLeft, MapperRight mapRight) const
Maps both alternatives.
Definition: Either.h:414
Either(rhs_tag, Rhs &&rhs)
Construct the RHS alternative (rvalue)
Definition: Either.h:156
const Either & doIfLeft(LeftVisitor visitLeft) const
Execute a callback if LHS is the active alternative.
Definition: Either.h:354
static InvalidEitherAccess rightWhenLeft()
Helper function that creates an InvaidEitherAccess when trying to access Rhs when Lhs is active...
const Lhs & getLeft() const
Attempts to get the LHS value.
Definition: Either.h:307
Tag used to construct the RHS member (in case both are convertible types)
Definition: Either.h:111
static void leftVoidVisitor(const Lhs &lhs)
Handy visitor that does nothing on the LHS.
Definition: Either.h:175
static constexpr bool is_movable()
Definition: Either.h:42
static Either emplaceRight(Args &&... args)
Constructs a Rhs in place.
Definition: Either.h:268
bool hasRight() const
Determines whether or not RHS is the active alternative.
Definition: Either.h:300
Either(lhs_tag, const Lhs &lhs)
Construct the LHS alternative (lvalue)
Definition: Either.h:145
Optional< Rhs > right
The holder of the right alternative.
Definition: Either.h:61
typename std::enable_if< B, T >::type enable_if_t
An handy type alias for the result of std::enable_if.
Definition: enable_if.h:12
Payload type that encapsulates both alternatives.
Definition: Either.h:50