either.h 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // Originally taken from
  2. // https://github.com/cryfs/cryfs/blob/14ad22570ddacef22d5ff139cdff68a54fc8234d/src/cpp-utils/either.h
  3. #pragma once
  4. #include <c10/macros/Macros.h>
  5. #include <c10/util/C++17.h>
  6. #include <c10/util/Optional.h>
  7. namespace c10 {
  8. /**
  9. * either<A, B> is a tagged union that holds either an object of type A
  10. * or an object of type B.
  11. */
  12. template <class Left, class Right>
  13. class either final {
  14. public:
  15. template <
  16. class Head,
  17. class... Tail,
  18. std::enable_if_t<
  19. std::is_constructible<Left, Head, Tail...>::value &&
  20. !std::is_constructible<Right, Head, Tail...>::value>* = nullptr>
  21. either(Head&& construct_left_head_arg, Tail&&... construct_left_tail_args)
  22. : _side(Side::left) {
  23. _construct_left(
  24. std::forward<Head>(construct_left_head_arg),
  25. std::forward<Tail>(construct_left_tail_args)...);
  26. }
  27. template <
  28. class Head,
  29. class... Tail,
  30. std::enable_if_t<
  31. !std::is_constructible<Left, Head, Tail...>::value &&
  32. std::is_constructible<Right, Head, Tail...>::value>* = nullptr>
  33. either(Head&& construct_right_head_arg, Tail&&... construct_right_tail_args)
  34. : _side(Side::right) {
  35. _construct_right(
  36. std::forward<Head>(construct_right_head_arg),
  37. std::forward<Tail>(construct_right_tail_args)...);
  38. }
  39. either(const either<Left, Right>& rhs) : _side(rhs._side) {
  40. if (_side == Side::left) {
  41. _construct_left(
  42. rhs._left); // NOLINT(cppcoreguidelines-pro-type-union-access)
  43. } else {
  44. _construct_right(
  45. rhs._right); // NOLINT(cppcoreguidelines-pro-type-union-access)
  46. }
  47. }
  48. either(either<Left, Right>&& rhs) noexcept : _side(rhs._side) {
  49. if (_side == Side::left) {
  50. _construct_left(std::move(
  51. rhs._left)); // NOLINT(cppcoreguidelines-pro-type-union-access)
  52. } else {
  53. _construct_right(std::move(
  54. rhs._right)); // NOLINT(cppcoreguidelines-pro-type-union-access)
  55. }
  56. }
  57. ~either() {
  58. _destruct();
  59. }
  60. either<Left, Right>& operator=(const either<Left, Right>& rhs) {
  61. _destruct();
  62. _side = rhs._side;
  63. if (_side == Side::left) {
  64. _construct_left(
  65. rhs._left); // NOLINT(cppcoreguidelines-pro-type-union-access)
  66. } else {
  67. _construct_right(
  68. rhs._right); // NOLINT(cppcoreguidelines-pro-type-union-access)
  69. }
  70. return *this;
  71. }
  72. either<Left, Right>& operator=(either<Left, Right>&& rhs) {
  73. _destruct();
  74. _side = rhs._side;
  75. if (_side == Side::left) {
  76. _construct_left(std::move(
  77. rhs._left)); // NOLINT(cppcoreguidelines-pro-type-union-access)
  78. } else {
  79. _construct_right(std::move(
  80. rhs._right)); // NOLINT(cppcoreguidelines-pro-type-union-access)
  81. }
  82. return *this;
  83. }
  84. bool is_left() const noexcept {
  85. return _side == Side::left;
  86. }
  87. bool is_right() const noexcept {
  88. return _side == Side::right;
  89. }
  90. const Left& left() const& {
  91. if (C10_UNLIKELY(!is_left())) {
  92. throw std::logic_error(
  93. "Tried to get left side of an either which is right.");
  94. }
  95. return _left; // NOLINT(cppcoreguidelines-pro-type-union-access)
  96. }
  97. Left& left() & {
  98. return const_cast<Left&>(
  99. const_cast<const either<Left, Right>*>(this)->left());
  100. }
  101. Left&& left() && {
  102. return std::move(left());
  103. }
  104. const Right& right() const& {
  105. if (C10_UNLIKELY(!is_right())) {
  106. throw std::logic_error(
  107. "Tried to get right side of an either which is left.");
  108. }
  109. return _right; // NOLINT(cppcoreguidelines-pro-type-union-access)
  110. }
  111. Right& right() & {
  112. return const_cast<Right&>(
  113. const_cast<const either<Left, Right>*>(this)->right());
  114. }
  115. Right&& right() && {
  116. return std::move(right());
  117. }
  118. template <class Result, class LeftFoldFunc, class RightFoldFunc>
  119. Result fold(LeftFoldFunc&& leftFoldFunc, RightFoldFunc&& rightFoldFunc)
  120. const {
  121. if (Side::left == _side) {
  122. return std::forward<LeftFoldFunc>(leftFoldFunc)(_left);
  123. } else {
  124. return std::forward<RightFoldFunc>(rightFoldFunc)(_right);
  125. }
  126. }
  127. private:
  128. union {
  129. Left _left;
  130. Right _right;
  131. };
  132. enum class Side : uint8_t { left, right } _side;
  133. explicit either(Side side) noexcept : _side(side) {}
  134. template <typename... Args>
  135. void _construct_left(Args&&... args) {
  136. new (&_left) Left(std::forward<Args>(
  137. args)...); // NOLINT(cppcoreguidelines-pro-type-union-access)
  138. }
  139. template <typename... Args>
  140. void _construct_right(Args&&... args) {
  141. new (&_right) Right(std::forward<Args>(
  142. args)...); // NOLINT(cppcoreguidelines-pro-type-union-access)
  143. }
  144. void _destruct() noexcept {
  145. if (_side == Side::left) {
  146. _left.~Left(); // NOLINT(cppcoreguidelines-pro-type-union-access)
  147. } else {
  148. _right.~Right(); // NOLINT(cppcoreguidelines-pro-type-union-access)
  149. }
  150. }
  151. template <typename Left_, typename Right_, typename... Args>
  152. friend either<Left_, Right_> make_left(Args&&... args);
  153. template <typename Left_, typename Right_, typename... Args>
  154. friend either<Left_, Right_> make_right(Args&&... args);
  155. };
  156. template <class Left, class Right>
  157. inline bool operator==(
  158. const either<Left, Right>& lhs,
  159. const either<Left, Right>& rhs) {
  160. if (lhs.is_left() != rhs.is_left()) {
  161. return false;
  162. }
  163. if (lhs.is_left()) {
  164. return lhs.left() == rhs.left();
  165. } else {
  166. return lhs.right() == rhs.right();
  167. }
  168. }
  169. template <class Left, class Right>
  170. inline bool operator!=(
  171. const either<Left, Right>& lhs,
  172. const either<Left, Right>& rhs) {
  173. return !operator==(lhs, rhs);
  174. }
  175. template <class Left, class Right>
  176. inline std::ostream& operator<<(
  177. std::ostream& stream,
  178. const either<Left, Right>& value) {
  179. if (value.is_left()) {
  180. stream << "Left(" << value.left() << ")";
  181. } else {
  182. stream << "Right(" << value.right() << ")";
  183. }
  184. return stream;
  185. }
  186. template <typename Left, typename Right, typename... Args>
  187. inline either<Left, Right> make_left(Args&&... args) {
  188. either<Left, Right> result(either<Left, Right>::Side::left);
  189. result._construct_left(std::forward<Args>(args)...);
  190. return result;
  191. }
  192. template <typename Left, typename Right, typename... Args>
  193. inline either<Left, Right> make_right(Args&&... args) {
  194. either<Left, Right> result(either<Left, Right>::Side::right);
  195. result._construct_right(std::forward<Args>(args)...);
  196. return result;
  197. }
  198. } // namespace c10