SI  2.5.4
A header only c++ library that provides type safety and user defined literals for handling physical values defined in the International System of Units.
unit.h
Go to the documentation of this file.
1 
12 #pragma once
13 
14 #ifdef SI_DISABLE_IMPLICIT_RATIO_CONVERSION
15 #define SI_ENABLE_IMPLICIT_RATIO_CONVERSION false
16 #else
17 #define SI_ENABLE_IMPLICIT_RATIO_CONVERSION true
18 #endif
19 
20 #include "detail.h"
21 #include "eps_equal.h"
22 #include "unit_cast.h"
23 
24 #include <ratio>
25 #include <type_traits>
26 
28 namespace SI::detail {
29 
30 template <typename _unit_lhs, typename _unit_rhs> struct unit_with_common_ratio;
31 
37 
49 template <char _symbol, typename _exponent, typename _type,
50  typename _ratio = std::ratio<1>>
51 struct unit_t {
52  static_assert(std::is_arithmetic_v<_type>, "Type is an arithmetic value");
53  static_assert(detail::is_ratio_v<_exponent>, "_exponent is a ratio type");
54  static_assert(detail::is_ratio_v<_ratio>, "_ratio is a std::ratio");
55  using ratio = _ratio;
56  using internal_type = _type;
57  using exponent = _exponent;
58  using symbol = std::integral_constant<char, _symbol>;
59 
61  explicit constexpr unit_t(_type v) : value_{v} {}
62  constexpr unit_t() = default;
63  constexpr unit_t(const unit_t &) = default;
64  constexpr unit_t(unit_t &&) = default;
65 
67  template <typename _type_rhs>
69  : value_(rhs.value()) {
70  static_assert(std::is_convertible<_type_rhs, _type>::value,
71  "Internal representation is convertible");
72  }
73 
74  ~unit_t() = default;
75 
76  template <typename _rhs_type, typename _rhs_ratio>
78  : value_{
79  unit_cast<unit_t<_symbol, _exponent, _type, _ratio>>(rhs).value()} {
80  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
81  static_assert(
83  std::ratio_equal_v<ratio, _rhs_ratio>,
84  "Implicit ratio conversion disabled, convert before assigning");
85  }
86 
87  template <typename _rhs_ratio>
89  : value_{
90  std::move(unit_cast<unit_t<_symbol, _exponent, _type, _ratio>>(rhs)
91  .value())} {
92  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
93  static_assert(
95  std::ratio_equal_v<ratio, _rhs_ratio>,
96  "Implicit ratio conversion disabled, convert before assigning");
97  }
98 
100  constexpr _type value() const { return value_; }
101 
104  template <typename _unit_rhs> constexpr _unit_rhs as() const {
105  static_assert(is_unit_t_v<_unit_rhs>, "only supported for SI::unit_t");
106  static_assert(std::ratio_equal_v<typename _unit_rhs::exponent, _exponent>,
107  "Exponents must match");
108  static_assert(_unit_rhs::symbol::value == _symbol,
109  "target unit must be of the same type must match");
110 
111  return unit_cast<_unit_rhs>(*this);
112  }
113 
116  template <template <typename _type_rhs> typename _unit_rhs>
117  constexpr _unit_rhs<_type> as() const {
118  static_assert(is_unit_t_v<_unit_rhs<_type>>,
119  "only supported for SI::unit_t");
120  static_assert(
121  std::ratio_equal_v<typename _unit_rhs<_type>::exponent, _exponent>,
122  "Exponents must match");
123  static_assert(_unit_rhs<_type>::symbol::value == _symbol,
124  "target unit must be of the same type must match");
125 
126  return unit_cast<_unit_rhs<_type>>(*this);
127  }
128 
130  void setValue(_type v) { value_ = v; }
131 
133  constexpr unit_t &operator=(const unit_t &rhs) = default;
134 
136  constexpr unit_t &operator=(unit_t &&rhs) = default;
137 
139  template <
140  typename _rhs_ratio,
141  std::enable_if_t<!std::ratio_equal_v<_rhs_ratio, _ratio>> * = nullptr>
142  constexpr unit_t &
144 
145  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
146  static_assert(
148  std::ratio_equal_v<ratio, _rhs_ratio>,
149  "Implicit ratio conversion disabled, convert before assigning");
150 
151  *this = unit_cast<unit_t<_symbol, _exponent, _type, _ratio>>(rhs);
152  return *this;
153  }
154 
156  template <
157  typename _rhs_ratio,
158  std::enable_if_t<!std::ratio_equal_v<_rhs_ratio, _ratio>> * = nullptr>
159  constexpr unit_t &
161 
162  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
163  static_assert(
165  std::ratio_equal_v<ratio, _rhs_ratio>,
166  "Implicit ratio conversion disabled, convert before assigning");
167 
168  *this =
170  return *this;
171  }
172 
175  template <typename _rhs_type, typename _rhs_ratio>
176  constexpr bool operator==(
178 
179  static_assert(
181  std::ratio_equal_v<ratio, _rhs_ratio>,
182  "Implicit ratio conversion disabled, convert before comparing");
183 
184  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
185  static_assert(std::is_integral_v<_type> || std::is_floating_point_v<_type>,
186  "Is integral or floating point");
187  using gcd_unit = typename unit_with_common_ratio<
188  typename std::remove_reference<decltype(rhs)>::type,
189  typename std::remove_reference<decltype(*this)>::type>::type;
190 
191  if constexpr (std::is_integral_v<_type>) {
192 
193  return unit_cast<gcd_unit>(rhs).value() ==
194  unit_cast<gcd_unit>(*this).value();
195  } else {
196  return detail::eps_equals(unit_cast<gcd_unit>(rhs).value(),
197  unit_cast<gcd_unit>(*this).value());
198  }
199  }
200 
202  template <typename _rhs_type, typename _rhs_ratio>
203  constexpr bool operator!=(
205  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
206  return !(*this == rhs);
207  }
208 
209  template <typename _rhs_type, typename _rhs_ratio>
210  constexpr bool operator<(
212  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
213  static_assert(
215  std::ratio_equal_v<ratio, _rhs_ratio>,
216  "Implicit ratio conversion disabled, convert before comparing");
217 
218  using gcd_unit = typename unit_with_common_ratio<
219  typename std::remove_reference<decltype(rhs)>::type,
220  typename std::remove_reference<decltype(*this)>::type>::type;
221  return unit_cast<gcd_unit>(*this).value() <
222  unit_cast<gcd_unit>(rhs).value();
223  }
224 
225  template <typename _rhs_type, typename _rhs_ratio>
226  constexpr bool operator<=(
228  return !(*this > rhs);
229  }
230 
231  template <typename _rhs_type, typename _rhs_ratio>
232  constexpr bool operator>(
234  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
235  static_assert(
237  std::ratio_equal_v<ratio, _rhs_ratio>,
238  "Implicit ratio conversion disabled, convert before comparing");
239 
240  using gcd_unit = typename unit_with_common_ratio<
241  typename std::remove_reference<decltype(rhs)>::type,
242  typename std::remove_reference<decltype(*this)>::type>::type;
243 
244  return unit_cast<gcd_unit>(*this).value() >
245  unit_cast<gcd_unit>(rhs).value();
246  }
247 
248  template <typename _rhs_type, typename _rhs_ratio>
249  constexpr bool operator>=(
251  return !(*this < rhs);
252  }
253 
255  constexpr unit_t operator*(const _type f) const { return unit_t{value_ * f}; }
256 
258  template <typename _rhs_exponent, typename _rhs_type>
259  constexpr auto operator*(
261 
262  static_assert(detail::is_ratio_v<_rhs_exponent>,
263  "rhs exponent is a ratio type");
265  std::ratio_multiply<ratio, _ratio>>{value() * rhs.value()};
266  }
267 
272  template <typename _rhs_exponent, typename _rhs_ratio, typename _rhs_type>
273  constexpr auto operator*(
275  static_assert(detail::is_ratio_v<_rhs_exponent>,
276  "rhs exponent is a ratio type");
277  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
278 
279  static_assert(
281  std::ratio_equal_v<ratio, _rhs_ratio>,
282  "Implicit ratio conversion disabled, convert before comparing");
283 
285  std::ratio_multiply<ratio, _rhs_ratio>>{value_ * rhs.value()};
286  }
287 
289  constexpr unit_t &operator*=(const _type scalar) {
290  value_ *= scalar;
291  return *this;
292  }
293 
295  constexpr unit_t operator/(const _type f) const { return unit_t{value_ / f}; }
296 
299  template <typename _rhs_exponent, typename _rhs_type,
300  std::enable_if_t<std::ratio_not_equal_v<_rhs_exponent, _exponent>>
301  * = nullptr>
302  constexpr auto operator/(
304  static_assert(detail::is_ratio_v<_rhs_exponent>,
305  "rhs exponent is a ratio type");
306  using rhs_t = typename std::remove_reference<decltype(rhs)>::type;
307 
308  return unit_t<_symbol,
309  std::ratio_subtract<_exponent, typename rhs_t::exponent>,
310  _type, std::ratio_divide<ratio, _ratio>>{value_ /
311  rhs.value()};
312  }
313 
317  template <typename _rhs_exponent, typename _rhs_type, typename _rhs_ratio,
318  std::enable_if_t<std::ratio_not_equal_v<_rhs_exponent, _exponent>>
319  * = nullptr>
320  constexpr auto operator/(
322  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
323  static_assert(detail::is_ratio_v<_rhs_exponent>,
324  "rhs exponent is a ratio type");
325  static_assert(
327  std::ratio_equal_v<ratio, _rhs_ratio>,
328  "Implicit ratio conversion disabled, convert before dividing");
329 
331  std::ratio_divide<ratio, _rhs_ratio>>{value_ / rhs.value()};
332  }
333 
335  template <typename _rhs_type>
336  constexpr _type
338  return value() / rhs.value();
339  }
340 
343  template <
344  typename _rhs_exponent, typename _rhs_type, typename _rhs_ratio,
345  std::enable_if_t<std::ratio_equal_v<_rhs_exponent, exponent>> * = nullptr>
346  constexpr _type operator/(
348  static_assert(SI_ENABLE_IMPLICIT_RATIO_CONVERSION ||
349  std::ratio_equal_v<_rhs_ratio, _ratio>,
350  "Implicit ratio conversion disabled, convert to same ratio "
351  "before dividing");
352 
353  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
354 
355  static_assert(detail::is_ratio_v<_rhs_exponent>,
356  "rhs exponent is a ratio type");
357 
358  using gcd_unit = typename unit_with_common_ratio<
359  typename std::remove_reference<decltype(*this)>::type,
360  typename std::remove_reference<decltype(rhs)>::type>::type;
361 
362  return unit_cast<gcd_unit>(*this) / unit_cast<gcd_unit>(rhs);
363  }
364 
366  constexpr unit_t &operator/=(const _type scalar) {
367  value_ /= scalar;
368  return *this;
369  }
370 
372  template <typename _rhs_type, typename _rhs_ratio>
373  constexpr unit_t operator+(
375 
376  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
377  static_assert(
379  std::ratio_equal_v<ratio, _rhs_ratio>,
380  "Implicit ratio conversion disabled, convert before adding values");
381 
382  return unit_t{
383  value() +
384  unit_cast<unit_t<_symbol, _exponent, _type, _ratio>>(rhs).value()};
385  }
386 
388  constexpr unit_t &operator+=(const unit_t &rhs) {
389  value_ += rhs.value();
390  return *this;
391  }
392 
394  template <
395  typename _rhs_type, typename _rhs_ratio,
396  std::enable_if_t<!std::ratio_equal_v<_rhs_ratio, _ratio>> * = nullptr>
397  constexpr unit_t &
399 
400  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
401  static_assert(
403  std::ratio_equal_v<ratio, _rhs_ratio>,
404  "Implicit ratio conversion disabled, convert before adding values");
405 
406  value_ += unit_cast<unit_t<_symbol, _exponent, _type, _ratio>>(rhs).value();
407 
408  return *this;
409  }
410 
412  template <typename _rhs_type, typename _rhs_ratio>
413  constexpr unit_t operator-(
415 
416  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
417  static_assert(
419  std::ratio_equal_v<ratio, _rhs_ratio>,
420  "Implicit ratio conversion disabled, convert before subtracting");
421 
422  return unit_t{
423  value() +
424  -unit_cast<unit_t<_symbol, _exponent, _type, _ratio>>(rhs).value()};
425  }
426 
428  constexpr unit_t &operator-=(const unit_t &rhs) {
429  value_ -= rhs.value();
430  return *this;
431  }
432 
434  template <typename _rhs_type, typename _rhs_ratio,
435  std::enable_if<!std::ratio_equal_v<_rhs_ratio, _ratio>> * = nullptr>
436  constexpr unit_t &
438 
439  static_assert(detail::is_ratio_v<_rhs_ratio>, "_rhs_ratio is a std::ratio");
440  static_assert(
442  std::ratio_equal_v<ratio, _rhs_ratio>,
443  "Implicit ratio conversion disabled, convert before adding values");
444 
445  value_ -= unit_cast<unit_t<_symbol, _exponent, _type, _ratio>>(rhs).value();
446 
447  return *this;
448  }
449 
451  constexpr unit_t operator-() const { return unit_t{-value_}; }
452 
455  ++value_;
456  return *this;
457  }
460  auto ret_val(*this);
461  ++(*this);
462 
463  return ret_val;
464  }
465 
468  --value_;
469  return *this;
470  }
471 
474  auto ret_val(*this);
475  --(*this);
476 
477  return ret_val;
478  }
479 
480 private:
481  _type value_;
482 };
483 
487 template <typename _type, char _symbol, typename _exponent, typename _rhs_type,
488  typename _ratio,
489  std::enable_if_t<std::is_integral_v<_type>> * = nullptr>
490 constexpr auto
491 operator/(const _type &lhs,
493  static_assert(SI_ENABLE_IMPLICIT_RATIO_CONVERSION ||
494  std::ratio_equal<std::ratio<1>, _ratio>::value,
495  "Implicit ratio conversion disabled, convert to ratio<1> "
496  "before dividing");
497  static_assert(detail::is_ratio_v<_exponent>, "Exponent is a ratio type");
498  return unit_t<_symbol, std::ratio_multiply<std::ratio<-1>, _exponent>, _type,
499  _ratio>{lhs / rhs.value()};
500 }
501 
506 template <typename _type, char _symbol, typename _exponent, typename _rhs_type,
507  typename _ratio,
508  std::enable_if_t<std::is_floating_point_v<_type>> * = nullptr>
509 constexpr auto
510 operator/(const _type &lhs,
511  const unit_t<_symbol, _exponent, _rhs_type, _ratio> &rhs) {
512  static_assert(SI_ENABLE_IMPLICIT_RATIO_CONVERSION ||
513  std::ratio_equal_v<_ratio, std::ratio<1>>,
514  "Implicit ratio conversion disabled, convert to ratio<1> "
515  "before dividing");
516  static_assert(detail::is_ratio_v<_exponent>, "Exponent is a ratio type");
517  return unit_t<_symbol, std::ratio_multiply<std::ratio<-1>, _exponent>, _type,
518  _ratio>{lhs / rhs.value()};
519 }
520 
521 } // namespace SI::detail
Namespace containing implementation details for SI.
Definition: acceleration.h:34
constexpr auto unit_cast(const _rhs_T &rhs)
function to cast between two units of the same type
Definition: unit_cast.h:22
constexpr bool is_unit_t_v
Definition: detail.h:65
constexpr bool eps_equals(const T &lhs, const T &rhs)
Definition: eps_equal.h:22
constexpr auto operator/(const _type &lhs, const unit_t< _symbol, _exponent, _rhs_type, _ratio > &rhs)
Definition: unit.h:491
base template class for holding values of type _type to be multiplied with a ratio _ratio
Definition: unit.h:51
constexpr unit_t & operator-=(const unit_t &rhs)
Subtract-assign value of the same unit.
Definition: unit.h:428
constexpr bool operator>(const unit_t< _symbol, _exponent, _rhs_type, _rhs_ratio > &rhs) const
Definition: unit.h:232
_ratio ratio
Definition: unit.h:55
constexpr unit_t()=default
constexpr unit_t operator*(const _type f) const
multiply with a non-unit scalar
Definition: unit.h:255
constexpr unit_t & operator+=(const unit_t &rhs)
add-assign value of the same unit
Definition: unit.h:388
constexpr bool operator>=(const unit_t< _symbol, _exponent, _rhs_type, _rhs_ratio > &rhs) const
Definition: unit.h:249
void setValue(_type v)
Definition: unit.h:130
constexpr auto operator/(const unit_t< _symbol, _rhs_exponent, _rhs_type, _ratio > &rhs) const
Definition: unit.h:302
constexpr unit_t & operator-=(const unit_t< _symbol, _exponent, _type, _rhs_ratio > &rhs)
subtract value of the same type but possibly different ratio
Definition: unit.h:437
unit_t & operator--()
decrement by prefix –
Definition: unit.h:467
constexpr unit_t & operator=(unit_t &&rhs)=default
Move assignment for same ratio.
constexpr bool operator<=(const unit_t< _symbol, _exponent, _rhs_type, _rhs_ratio > &rhs) const
Definition: unit.h:226
unit_t operator++(int)
increment by postfix ++
Definition: unit.h:459
constexpr unit_t & operator=(const unit_t< _symbol, _exponent, _type, _rhs_ratio > &rhs)
Assignment of same unit but different ratio.
Definition: unit.h:143
constexpr _type operator/(const unit_t< _symbol, _exponent, _rhs_type, _ratio > &rhs)
divide whit same unit result is a scalar
Definition: unit.h:337
constexpr unit_t & operator=(const unit_t &rhs)=default
Assignment for same ratio.
_type internal_type
Definition: unit.h:56
constexpr _type operator/(const unit_t< _symbol, _rhs_exponent, _rhs_type, _rhs_ratio > &rhs) const
Definition: unit.h:346
constexpr unit_t(const unit_t< _symbol, _exponent, _type_rhs, _ratio > &rhs)
construct from other unit with implicitly convertible type
Definition: unit.h:68
constexpr auto operator*(const unit_t< _symbol, _rhs_exponent, _rhs_type, _rhs_ratio > &rhs) const
Definition: unit.h:273
constexpr unit_t(unit_t &&)=default
constexpr unit_t & operator/=(const _type scalar)
divide with a non-unit scalar
Definition: unit.h:366
_exponent exponent
Definition: unit.h:57
constexpr unit_t operator-(const unit_t< _symbol, _exponent, _rhs_type, _rhs_ratio > &rhs) const
subtracts two values, returning type is type of lhs
Definition: unit.h:413
constexpr bool operator<(const unit_t< _symbol, _exponent, _rhs_type, _rhs_ratio > &rhs) const
Definition: unit.h:210
constexpr unit_t operator+(const unit_t< _symbol, _exponent, _rhs_type, _rhs_ratio > &rhs) const
adds two values, returning type is type of lhs
Definition: unit.h:373
constexpr unit_t(const unit_t &)=default
constexpr unit_t operator-() const
negate operation
Definition: unit.h:451
unit_t & operator++()
increment by prefix ++
Definition: unit.h:454
constexpr bool operator!=(const unit_t< _symbol, _exponent, _rhs_type, _rhs_ratio > &rhs) const
compares two values, considers different ratios.
Definition: unit.h:203
unit_t operator--(int)
decrement by postfix –
Definition: unit.h:473
constexpr bool operator==(const unit_t< _symbol, _exponent, _rhs_type, _rhs_ratio > &rhs) const
Definition: unit.h:176
constexpr unit_t & operator*=(const _type scalar)
multiply with a non-unit scalar
Definition: unit.h:289
constexpr auto operator/(const unit_t< _symbol, _rhs_exponent, _rhs_type, _rhs_ratio > &rhs) const
Definition: unit.h:320
constexpr _unit_rhs as() const
Definition: unit.h:104
constexpr unit_t operator/(const _type f) const
divide by a non-unit scalar
Definition: unit.h:295
std::integral_constant< char, _symbol > symbol
Definition: unit.h:58
constexpr unit_t(_type v)
Construct with value v.
Definition: unit.h:61
constexpr unit_t(unit_t< _symbol, _exponent, _type, _rhs_ratio > &&rhs)
Definition: unit.h:88
constexpr _type value() const
returns the stored value as raw type
Definition: unit.h:100
constexpr unit_t & operator=(unit_t< _symbol, _exponent, _type, _rhs_ratio > &&rhs)
Move assignment of same unit but different ratio.
Definition: unit.h:160
constexpr unit_t & operator+=(const unit_t< _symbol, _exponent, _rhs_type, _rhs_ratio > &rhs)
add value of the same type but possibly different ratio
Definition: unit.h:398
constexpr _unit_rhs< _type > as() const
Definition: unit.h:117
constexpr unit_t(const unit_t< _symbol, _exponent, _rhs_type, _rhs_ratio > &rhs)
Definition: unit.h:77
constexpr auto operator*(const unit_t< _symbol, _rhs_exponent, _rhs_type, _ratio > &rhs) const
multiply with an unit of the same ratio
Definition: unit.h:259
Definition: unit_cast.h:49
#define SI_ENABLE_IMPLICIT_RATIO_CONVERSION
Definition: unit.h:17