Marjoram  0.01
Library for Functional Programming in C++
eitherImpl.hpp
1 #pragma once
2 
3 #include <algorithm>
4 #include <cassert>
5 #include <new>
6 
7 namespace ma {
11 struct LeftSide {
12  LeftSide() {}
13 };
17 static const LeftSide Left;
18 
22 struct RightSide {
23  RightSide() {}
24 };
28 static const RightSide Right;
29 
30 namespace detail {
31 
32 enum EitherSide : char { left, right };
33 
37 template <typename Left_t, typename Right_t> class EitherImpl {
38  public:
42  template <typename... Args>
43  EitherImpl(LeftSide /* selects overload */, Args&&... args)
44  : side(EitherSide::left) {
45  new (&storage) Left_t(std::forward<Args>(args)...);
46  }
47 
51  template <typename... Args>
52  EitherImpl(RightSide /* selects overload */, Args&&... args)
53  : side(EitherSide::right) {
54  new (&storage) Right_t(std::forward<Args>(args)...);
55  }
56 
60  ~EitherImpl() { cleanup(); }
61 
65  EitherImpl(const EitherImpl& rhs) : side(rhs.side) {
66  if (side == left) {
67  new (&storage) Left_t(rhs.asLeft());
68  } else {
69  new (&storage) Right_t(rhs.asRight());
70  }
71  }
72 
76  EitherImpl(EitherImpl&& rhs) noexcept : side(rhs.side) {
77  if (side == left) {
78  new (&storage) Left_t(std::move(rhs.asLeft()));
79  } else {
80  new (&storage) Right_t(std::move(rhs.asRight()));
81  }
82  }
83 
88  if (this == &rhs) {
89  return *this;
90  }
91  /* destroy whatever we are currently holding */
92  cleanup();
93 
94  /* copy rhs */
95  side = rhs.side;
96  if (side == left) {
97  new (&storage) Left_t((rhs.asLeft()));
98  } else {
99  new (&storage) Right_t((rhs.asRight()));
100  }
101  return *this;
102  }
103 
107  EitherImpl& operator=(EitherImpl&& rhs) noexcept {
108  if (this == &rhs) {
109  return *this;
110  }
111  /* destroy whatever we are currently holding */
112  cleanup();
113 
114  /* move rhs */
115  side = rhs.side;
116  if (side == left) {
117  new (&storage) Left_t(std::move(rhs.asLeft()));
118  } else {
119  new (&storage) Right_t(std::move(rhs.asRight()));
120  }
121  return *this;
122  }
123 
130  Right_t& asRight() {
131  assert(side == right);
132  return *reinterpret_cast<Right_t*>(&storage);
133  }
134 
141  const Right_t& asRight() const {
142  assert(side == right);
143  return *reinterpret_cast<const Right_t*>(&storage);
144  }
145 
152  Left_t& asLeft() {
153  assert(side == left);
154  return *reinterpret_cast<Left_t*>(&storage);
155  }
156 
163  const Left_t& asLeft() const {
164  assert(side == left);
165  return *reinterpret_cast<const Left_t*>(&storage);
166  }
167 
168  protected:
172  EitherSide side;
173 
174  private:
175  void cleanup() {
176  if (side == left) {
177  asLeft().~Left_t();
178  } else {
179  asRight().~Right_t();
180  }
181  }
182 
183  using storage_t = typename std::aligned_union<1, Left_t, Right_t>::type;
184  storage_t storage;
185 };
186 } // namespace detail
187 } // namespace ma
Tag indicating the left side.
Definition: eitherImpl.hpp:11
EitherImpl & operator=(const EitherImpl &rhs)
Copy assignment operator; Copies underlying value.
Definition: eitherImpl.hpp:87
~EitherImpl()
Destructor, calling destructor of contained type.
Definition: eitherImpl.hpp:60
Tag indicating the right side.
Definition: eitherImpl.hpp:22
EitherSide side
Indicates whether this contains an A or a B.
Definition: eitherImpl.hpp:172
EitherImpl(RightSide, Args &&... args)
Construct containing right type.
Definition: eitherImpl.hpp:52
EitherImpl(LeftSide, Args &&... args)
Construct containing left type.
Definition: eitherImpl.hpp:43
const Left_t & asLeft() const
Returns stored A value.
Definition: eitherImpl.hpp:163
Implementation of (safe) union of two types.
Definition: eitherImpl.hpp:37
EitherImpl(const EitherImpl &rhs)
Copy constructor; Copies underlying value.
Definition: eitherImpl.hpp:65
EitherImpl(EitherImpl &&rhs) noexcept
Move constructor; Moves underlying value.
Definition: eitherImpl.hpp:76
Right_t & asRight()
Returns stored B value.
Definition: eitherImpl.hpp:130
Definition: either.hpp:10
const Right_t & asRight() const
Returns stored B value.
Definition: eitherImpl.hpp:141
Left_t & asLeft()
Returns stored A value.
Definition: eitherImpl.hpp:152
EitherImpl & operator=(EitherImpl &&rhs) noexcept
Move assignment operator; Moves underlying value.
Definition: eitherImpl.hpp:107