libcaramel
strong_type.hpp
1 #pragma once
2 
3 #include <concepts>
4 #include <type_traits>
5 #include <utility>
6 
7 namespace caramel
8 {
9  // clang-format off
10  template <typename Any, typename parameter, template <typename> class... utils_>
11  class strong_type final : public utils_<strong_type<Any, parameter, utils_...>>...
12  {
13  public:
14  using value_type = Any;
15 
16  public:
17  constexpr strong_type()
18  noexcept(std::is_nothrow_default_constructible_v<value_type>)
19  requires std::default_initializable<value_type> = default;
20 
21  constexpr explicit strong_type(const value_type& value)
22  noexcept(std::is_nothrow_copy_constructible_v<value_type>)
23  requires std::copy_constructible<value_type>
24  : m_value{value}
25  {}
26 
27  constexpr explicit strong_type(value_type&& value)
28  noexcept(std::is_nothrow_move_assignable_v<value_type>)
29  requires std::move_constructible<value_type>
30  : m_value{std::move(value)}
31  {}
32 
33  constexpr auto value() noexcept -> value_type&
34  {
35  return m_value;
36  }
37  constexpr auto value() const noexcept -> const value_type&
38  {
39  return m_value;
40  }
41 
42  constexpr operator strong_type<value_type&, parameter>()
43  {
45  }
46 
47  struct argument
48  {
49  constexpr argument() = default;
50  constexpr argument(argument const&) = delete;
51  constexpr argument(argument&&) = delete;
52  constexpr ~argument() = default;
53 
54  constexpr auto operator=(argument const&) -> argument& = delete;
55  constexpr auto operator=(argument&&) -> argument& = delete;
56 
57  constexpr auto operator=(value_type&& value) const -> strong_type // NOLINT
58  {
59  return strong_type{std::forward<value_type>(value)};
60  }
61 
62  constexpr auto operator=(auto&& value) const -> strong_type // NOLINT
63  {
64  return strong_type{std::forward<decltype(value)>(value)};
65  }
66  };
67 
68  private:
69  value_type m_value;
70  };
71  // clang-format on
72 
73  namespace detail
74  {
75  template <typename Any, template <typename> class crtp_type_>
76  struct crtp
77  {
78  constexpr auto underlying() -> Any& { return static_cast<Any&>(*this); }
79  constexpr auto underlying() const -> Any const& { return static_cast<Any const&>(*this); }
80  };
81  } // namespace detail
82 
83  template <typename Any>
84  struct pre_incrementable : detail::crtp<Any, pre_incrementable>
85  {
86  constexpr auto operator++() -> Any&
87  {
88  ++this->underlying().value();
89  return this->underlying();
90  }
91  };
92 
93  template <typename Any>
94  struct post_incrementable : detail::crtp<Any, post_incrementable>
95  {
96  constexpr auto operator++(int) -> Any { return this->underlying().value()++; }
97  };
98 
99  template <typename Any>
101  {
104  };
105 
106  template <typename Any>
107  struct pre_decrementable : detail::crtp<Any, pre_decrementable>
108  {
109  constexpr auto operator--() -> Any&
110  {
111  --this->underlying().value();
112  return this->underlying();
113  }
114  };
115 
116  template <typename Any>
117  struct post_decrementable : detail::crtp<Any, post_decrementable>
118  {
119  constexpr auto operator--(int) -> Any { return this->underlying().value()--; }
120  };
121 
122  template <typename Any>
124  {
127  };
128 
129  template <typename Any>
130  struct binary_addable : detail::crtp<Any, binary_addable>
131  {
132  constexpr auto operator+(const Any& rhs) const
133  {
134  return Any{this->underlying().value() + rhs.value()};
135  }
136 
137  constexpr auto operator+=(const Any& rhs)
138  {
139  this->underlying().vale() += rhs.value();
140  return this->underlying();
141  }
142  };
143 
144  template <typename Any>
145  struct unary_addable : detail::crtp<Any, unary_addable>
146  {
147  constexpr auto operator+() const { return Any{+this->undelying().value()}; }
148  };
149 
150  template <typename Any>
152  {
155  };
156 
157  template <typename Any>
158  struct binary_subtractable : detail::crtp<Any, binary_subtractable>
159  {
160  constexpr auto operator-(const Any& rhs) const
161  {
162  return Any{this->underlying().value() - rhs.value()};
163  }
164 
165  constexpr auto operator-=(const Any& rhs)
166  {
167  this->underlying().value() -= rhs.value();
168  return this->underlying();
169  }
170  };
171 
172  template <typename Any>
173  struct unary_subtractable : detail::crtp<Any, unary_subtractable>
174  {
175  constexpr auto operator-() const { return Any{-this->undelying().value()}; }
176  };
177 
178  template <typename Any>
180  {
183  };
184 
185  template <typename Any>
186  struct multiplicable : detail::crtp<Any, multiplicable>
187  {
188  constexpr auto operator*(const Any& rhs) const -> Any
189  {
190  return Any{this->underlying().value() * rhs.value()};
191  }
192  constexpr auto operator*=(const Any& rhs) -> Any&
193  {
194  this->underlying().value() *= rhs.value();
195  return this->underlying();
196  }
197  };
198 
199  template <typename Any>
200  struct divisible : detail::crtp<Any, divisible>
201  {
202  constexpr auto operator/(const Any& other) const -> Any
203  {
204  return Any{this->underlying().value() / other.value()};
205  }
206  constexpr auto operator/=(const Any& rhs) -> Any&
207  {
208  this->underlying().value() /= rhs.value();
209  return this->underlying();
210  }
211  };
212 
213  template <typename Any>
214  struct modulable : detail::crtp<Any, modulable>
215  {
216  constexpr auto operator%(const Any& rhs) const -> Any
217  {
218  return Any{this->underlying().value() % rhs.value()};
219  }
220  constexpr auto operator%=(const Any& other) -> Any&
221  {
222  this->underlying().value() %= other.value();
223  return this->underlying();
224  }
225  };
226 
227  template <typename Any>
228  struct equatable : detail::crtp<Any, equatable>
229  {
230  constexpr friend auto operator==(const Any& lhs, const Any& rhs) -> bool
231  {
232  return lhs.value() == rhs.value();
233  }
234  };
235 
236  template <typename Any>
237  struct comparable : detail::crtp<Any, comparable>
238  {
239  constexpr friend auto operator<=>(const Any& lhs, const Any& rhs)
240  {
241  return std::compare_three_way{}(lhs, rhs);
242  }
243  };
244 
245  template <typename Any>
246  struct arithmetic :
247  incrementable<Any>,
248  decrementable<Any>,
249  subtractable<Any>,
250  addable<Any>,
251  multiplicable<Any>,
252  divisible<Any>,
253  modulable<Any>,
254  comparable<Any>
255  {
256  };
257 } // namespace caramel
Definition: strong_type.hpp:12
Definition: strong_type.hpp:152
Definition: strong_type.hpp:255
Definition: strong_type.hpp:131
Definition: strong_type.hpp:159
Definition: strong_type.hpp:238
Definition: strong_type.hpp:124
Definition: strong_type.hpp:77
Definition: strong_type.hpp:201
Definition: strong_type.hpp:229
Definition: strong_type.hpp:101
Definition: strong_type.hpp:215
Definition: strong_type.hpp:187
Definition: strong_type.hpp:118
Definition: strong_type.hpp:95
Definition: strong_type.hpp:108
Definition: strong_type.hpp:85
Definition: strong_type.hpp:48
Definition: strong_type.hpp:180
Definition: strong_type.hpp:146
Definition: strong_type.hpp:174