MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
units/base.hpp
Go to the documentation of this file.
1/*
2
3Copyright 2025 Matthew Tolman
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16
17*/
18
19#ifndef MTCORE_UNITS_BASE_HPP
20#define MTCORE_UNITS_BASE_HPP
21
22#include "mtcore/math/core.hpp"
24#include "mtcore/meta/base.hpp"
25
26#include <numbers>
27#include <ratio>
28
32
36namespace mtcore::units {
43 template<typename Underlying, typename Units>
44 struct UnitValue;
45
56 template<size_t UnitCategory, typename S = std::ratio<1>, int PiPow = 0, typename Tr = std::ratio<0>, int exp = 1>
57 struct Unit {
58 static constexpr size_t Id = UnitCategory;
59 using Scale = S;
60 static constexpr auto PiPower = PiPow;
61 using Translation = Tr;
62 static constexpr auto EXPONENT = exp;
63 };
64
69 template<typename T>
70 concept IsUnit = std::same_as<T, Unit<T::Id, typename T::Scale, T::PiPower, typename T::Translation, T::EXPONENT>>;
71
79 template<IsUnit... Us>
80 struct Units;
81
82 template<size_t Id, typename S, int PiPow, typename Tr, int Exp, IsUnit... Mappings>
83 struct Units<Unit<Id, S, PiPow, Tr, Exp>, Mappings...> {
85 using Rest = Units<Mappings...>;
86 };
87
88 template<>
89 struct Units<> {
90 using Head = void;
91 using Rest = void;
92 };
93
94 namespace impl {
100 template<typename L, typename R>
101 struct ConcatUnits;
102
103 template<IsUnit... LU, IsUnit... RU>
104 struct ConcatUnits<Units<LU...>, Units<RU...>> {
105 using T = Units<LU..., RU...>;
106 };
107
113 template<size_t CategoryId, typename T>
114 struct GetUnitsImpl;
115
116 template<size_t CategoryId, typename S, int PiPow, int exp, typename Tr, typename... Mappings>
117 struct GetUnitsImpl<CategoryId, Units<Unit<CategoryId, S, PiPow, Tr, exp>, Mappings...>> {
118 using Rest = GetUnitsImpl<CategoryId, Units<Mappings...>>;
119 using T = typename ConcatUnits<Units<Unit<CategoryId, S, PiPow, Tr, exp>>, typename Rest::T>::T;
120 };
121
122 template<size_t CategoryId, size_t CurId, typename S, int PiPow, int exp, typename Tr, typename... Mappings>
123 struct GetUnitsImpl<CategoryId, Units<Unit<CurId, S, PiPow, Tr, exp>, Mappings...>> {
124 using Rest = GetUnitsImpl<CategoryId, Units<Mappings...>>;
125 using T = typename Rest::T;
126 };
127
128 template<size_t CategoryId>
129 struct GetUnitsImpl<CategoryId, Units<>> {
130 using T = Units<>;
131 };
132
138 template<size_t CategoryId, typename U>
139 struct GetUnits : GetUnitsImpl<CategoryId, std::remove_cvref_t<U>> {};
140
145 template<size_t CategoryId, typename T, size_t Indx = 0>
146 struct GetUnitImpl;
147
148 template<size_t CategoryId, typename S, int PiPow, int exp, typename Tr, typename... Mappings, size_t Indx>
149 struct GetUnitImpl<CategoryId, Units<Unit<CategoryId, S, PiPow, Tr, exp>, Mappings...>, Indx> {
150 using Rest = GetUnitImpl<CategoryId, Units<Mappings...>, Indx + 1>;
151 using Scale = typename math::MultRatios<typename math::IntPowRatio<S, exp>::T, typename Rest::Scale>::T;
152 static constexpr auto PiPower = PiPow * exp + Rest::PiPower;
153 using Translation = typename math::AddRatios<typename math::NegateRatio<Tr>::T, typename Rest::Translation>::T;
154 static constexpr int exponent = exp + Rest::exponent;
155 using T = Unit<CategoryId, Scale, PiPower, Translation, exponent>;
156 using UNIT = T;
157 static constexpr auto index = Indx;
158 static constexpr bool present = true;
159 };
160
161 template<size_t CategoryId, size_t CurId, typename S, int PiPow, int exp, typename Tr, typename... Mappings,
162 size_t Indx>
163 struct GetUnitImpl<CategoryId, Units<Unit<CurId, S, PiPow, Tr, exp>, Mappings...>, Indx> {
164 using Rest = GetUnitImpl<CategoryId, Units<Mappings...>, Indx + 1>;
165 using Scale = typename Rest::Scale;
166 static constexpr auto PiPower = Rest::PiPower;
167 using Translation = typename Rest::Translation;
168 using T = typename Rest::T;
169 using UNIT = typename Rest::UNIT;
170 static constexpr int exponent = Rest::exponent;
171 static constexpr auto index = Rest::index;
172 static constexpr bool present = Rest::present;
173 };
174
175 template<size_t CategoryId, size_t Indx>
176 struct GetUnitImpl<CategoryId, Units<>, Indx> {
177 using Scale = std::ratio<1>;
178 static constexpr int PiPower = 0;
179 using Translation = std::ratio<0>;
180 using T = Unit<CategoryId, Scale, PiPower, Translation>;
181 using UNIT = T;
182 static constexpr int exponent = 0;
183 static constexpr auto index = std::numeric_limits<size_t>::max();
184 static constexpr bool present = false;
185 };
186
191 template<size_t CategoryId, typename U>
192 struct GetUnit : GetUnitImpl<CategoryId, std::remove_cvref_t<U>, 0> {};
193
194 static_assert(!GetUnit<4, Units<Unit<1>>>::present);
195 static_assert(GetUnit<4, Units<Unit<4>>>::present);
196 static_assert(GetUnit<4, Units<Unit<5>, Unit<4>>>::present);
197 static_assert(std::same_as<GetUnit<4, Units<Unit<5>, Unit<4>>>::Scale, std::ratio<1>>);
198 static_assert(std::same_as<GetUnit<4, Units<Unit<5>, Unit<4, std::ratio<2>>>>::Scale, std::ratio<2>>);
199 static_assert(std::same_as<GetUnit<4, Units<Unit<5>, Unit<4, std::ratio<2>>, Unit<4, std::ratio<1, 2>>>>::Scale,
200 std::ratio<1>>);
201 static_assert(std::same_as<GetUnit<4, Units<Unit<5>, Unit<4, std::ratio<10>>, Unit<4, std::ratio<1, 2>>>>::Scale,
202 std::ratio<5>>);
203 static_assert(std::same_as<GetUnit<4, Units<Unit<5>, Unit<4, std::ratio<10>>, Unit<4, std::ratio<1, 2>>,
204 Unit<4, std::ratio<1, 5>>>>::Scale,
205 std::ratio<1>>);
206 static_assert(std::same_as<GetUnit<4, Units<Unit<5>, Unit<4, std::ratio<10>>, Unit<4, std::ratio<1, 2>>,
207 Unit<4, std::ratio<1, 10>>>>::Scale,
208 std::ratio<1, 2>>);
209
216 template<int Id, typename L, typename R>
217 struct ExpEq {
218 static constexpr auto value = GetUnit<Id, L>::exponent == GetUnit<Id, R>::exponent;
219 };
220
226 template<bool present, typename... U>
227 struct UniqIdSetImpl;
228
229 template<typename U, typename UNext, typename... Us>
230 struct UniqIdSetImpl<true, Units<U, UNext, Us...>> {
231 using T = typename UniqIdSetImpl<GetUnit<UNext::Id, Units<Us...>>::present, Units<UNext, Us...>>::T;
232 };
233
234 template<typename U, typename UNext, typename... Us>
235 struct UniqIdSetImpl<false, Units<U, UNext, Us...>> {
236 using T = typename meta::ConcatIntList<
237 meta::IntList<U::Id>,
238 typename UniqIdSetImpl<GetUnit<UNext::Id, Units<Us...>>::present, Units<UNext, Us...>>::T>::T;
239 };
240
241 template<typename U>
242 struct UniqIdSetImpl<false, Units<U>> {
243 using T = meta::IntList<U::Id>;
244 };
245
246 template<bool p>
247 struct UniqIdSetImpl<p, Units<>> {
248 using T = meta::IntList<>;
249 };
250
254 template<typename T>
255 struct UniqIdSet;
256
257 template<>
258 struct UniqIdSet<const Units<>> {
259 using T = meta::IntList<>;
260 };
261
262 template<>
263 struct UniqIdSet<Units<>> {
264 using T = meta::IntList<>;
265 };
266
267 template<IsUnit U, IsUnit... Us>
268 struct UniqIdSet<Units<U, Us...>> {
269 using T = typename UniqIdSetImpl<GetUnit<U::Id, Units<Us...>>::present, Units<U, Us...>>::T;
270 };
271
272 template<IsUnit U, IsUnit... Us>
273 struct UniqIdSet<const Units<U, Us...>> {
274 using T = typename UniqIdSetImpl<GetUnit<U::Id, Units<Us...>>::present, Units<U, Us...>>::T;
275 };
276
280 template<typename Ids, typename Left, typename Right>
281 struct AllLeftInRightImpl;
282
283 template<int... Ids, typename Left, typename Right>
284 struct AllLeftInRightImpl<meta::IntList<Ids...>, Left, Right> {
285 static constexpr auto value =
286 meta::And<GetUnit<Ids, Right>::present...>::value && meta::And<ExpEq<Ids, Left, Right>::value...>::value;
287 };
288
292 template<typename Left, typename Right>
293 struct AllLeftInRight : AllLeftInRightImpl<typename UniqIdSet<std::remove_cvref_t<Left>>::T,
294 std::remove_cvref_t<std::remove_cvref_t<Left>>,
295 std::remove_cvref_t<std::remove_cvref_t<Right>>> {};
296
297 static_assert(AllLeftInRight<Units<Unit<1>>, Units<Unit<1>>>::value);
298 static_assert(AllLeftInRight<Units<Unit<1>>, Units<Unit<1>>>::value);
299 static_assert(AllLeftInRight<Units<Unit<1>>, Units<Unit<1>, Unit<2>>>::value);
300 static_assert(AllLeftInRight<Units<Unit<1>, Unit<2>>, Units<Unit<1>, Unit<2>>>::value);
301 static_assert(!AllLeftInRight<Units<Unit<1>, Unit<2>, Unit<3>>, Units<Unit<1>, Unit<2>>>::value);
302 static_assert(!AllLeftInRight<Units<Unit<1>>, Units<Unit<1>, Unit<1>>>::value);
303 static_assert(!AllLeftInRight<Units<Unit<1>, Unit<1>>, Units<Unit<1>>>::value);
304 static_assert(AllLeftInRight<Units<Unit<1>, Unit<1>>, Units<Unit<1>, Unit<1>>>::value);
305
309 template<typename Left, typename Right>
310 struct CompatibleDimensions {
311 static constexpr bool value = AllLeftInRight<Left, Right>::value && AllLeftInRight<Right, Left>::value;
312 };
313
317 template<typename From, typename To>
318 concept Convertible = CompatibleDimensions<From, To>::value;
319
323 template<typename T, typename Us>
324 struct WithoutZeroExponentsImpl;
325
326 template<typename Us>
327 struct WithoutZeroExponentsImpl<meta::IntList<>, Us> {
328 using T = Units<>;
329 };
330
331 template<int Cur, int... Rest, typename Us>
332 struct WithoutZeroExponentsImpl<meta::IntList<Cur, Rest...>, Us> {
333 using Nxt = typename WithoutZeroExponentsImpl<meta::IntList<Rest...>, Us>::T;
334 using T = typename meta::If<GetUnit<Cur, Us>::exponent == 0, Nxt,
335 typename ConcatUnits<typename GetUnits<Cur, Us>::T, Nxt>::T>::T;
336 };
337
341 template<typename T>
342 struct WithoutZeroExponents;
343
344 template<>
345 struct WithoutZeroExponents<Units<>> {
346 using T = Units<>;
347 };
348
349 template<typename U, typename... Us>
350 struct WithoutZeroExponents<Units<U, Us...>> {
351 using T = typename WithoutZeroExponentsImpl<typename UniqIdSet<Units<U, Us...>>::T, Units<U, Us...>>::T;
352 };
353
357 template<IsUnit U>
358 struct FlipUnit {
359 using T = Unit<U::Id, typename U::Scale, U::PiPower, typename U::Translation, -U::EXPONENT>;
360 };
361
362 static_assert(GetUnit<1, Units<Unit<1>, Unit<1>>>::exponent == 2);
363 static_assert(GetUnit<1, Units<Unit<1>, typename FlipUnit<Unit<1>>::T>>::exponent == 0);
364
365 static_assert(GetUnit<1, Units<Unit<1>, typename FlipUnit<Unit<1>>::T>>::present == true);
366 static_assert(
367 GetUnit<1, typename WithoutZeroExponents<Units<Unit<1>, typename FlipUnit<Unit<1>>::T>>::T>::present == false);
368 static_assert(GetUnit<1, typename WithoutZeroExponents<Units<Unit<1>, Unit<1>>>::T>::present == true);
369
373 template<typename Ids, typename From, typename To>
374 struct ConvertImpl;
375
376 template<typename From, typename To>
377 struct ConvertImpl<meta::IntList<>, From, To> {
378 template<bool, typename U>
379 static constexpr U convert(const U &v) {
380 return v;
381 }
382 };
383
384 template<int Id, int... Ids, typename From, typename To>
385 struct ConvertImpl<meta::IntList<Id, Ids...>, From, To> {
386 using FromScale = typename GetUnit<Id, From>::Scale;
387 using FromTranslation = typename GetUnit<Id, From>::Translation;
388 static constexpr auto FromPiScale = GetUnit<Id, From>::PiPower;
389 static constexpr auto FromExponent = GetUnit<Id, From>::exponent;
390
391 using ToScale = typename GetUnit<Id, To>::Scale;
392 using ToTranslation = typename GetUnit<Id, To>::Translation;
393 static constexpr auto ToPiScale = GetUnit<Id, To>::PiPower;
394 static constexpr auto ToExponent = GetUnit<Id, To>::exponent;
395
396 static constexpr auto FSNum = math::RatioParts<FromScale>::Numerator;
397 static constexpr auto FSDen = math::RatioParts<FromScale>::Denominator;
398 static constexpr auto FTNum = math::RatioParts<FromTranslation>::Numerator;
399 static constexpr auto FTDen = math::RatioParts<FromTranslation>::Denominator;
400
401 static constexpr auto TSNum = math::RatioParts<ToScale>::Numerator;
402 static constexpr auto TSDen = math::RatioParts<ToScale>::Denominator;
403 static constexpr auto TTNum = math::RatioParts<ToTranslation>::Numerator;
404 static constexpr auto TTDen = math::RatioParts<ToTranslation>::Denominator;
405
406 static constexpr auto ScaleMult = static_cast<long double>(FSNum) * static_cast<long double>(TSDen);
407 static constexpr auto ScaleDiv = static_cast<long double>(FSDen) * static_cast<long double>(TSNum);
408 static constexpr auto ScaleFactor = static_cast<long double>(ScaleMult) / static_cast<long double>(ScaleDiv);
409 static_assert(ScaleDiv != 0);
410
411 static constexpr auto factor = ScaleFactor;
412
413 template<bool ignoreChecks, typename U>
414 static constexpr U convert(U v) {
415 static_assert(!std::is_integral_v<U> || !(FSNum == TSNum && FSDen == TSDen) || (factor == 1) || ignoreChecks,
416 "Possible precision loss detected!");
417 if constexpr (FTNum != 0) {
418 v -= static_cast<long double>(FTNum) / static_cast<long double>(FTDen);
419 }
420
421 if constexpr (!std::is_integral_v<U> && std::is_convertible_v<U, long double>) {
422 v = static_cast<U>(static_cast<long double>(v) * factor);
423 }
424 else {
425 v = static_cast<U>((static_cast<long double>(v) * ScaleMult) / ScaleDiv);
426 }
427
428 if constexpr (ToPiScale != 0) {
429 v *= std::pow(std::numbers::pi_v<long double>, -ToPiScale);
430 }
431 if constexpr (FromPiScale) {
432 v *= std::pow(std::numbers::pi_v<long double>, FromPiScale);
433 }
434
435 if constexpr (TTNum != 0) {
436 v += static_cast<long double>(TTNum) / static_cast<long double>(TTDen);
437 }
438 return ConvertImpl<meta::IntList<Ids...>, From, To>::template convert<ignoreChecks>(static_cast<U>(v));
439 }
440 };
441
445 template<typename From, Convertible<From> To>
446 struct Convert : ConvertImpl<typename UniqIdSet<std::remove_cvref_t<From>>::T, std::remove_cvref_t<From>,
447 std::remove_cvref_t<To>> {};
448
449 template<typename T, typename Dest>
450 concept ConvertibleTo = AllLeftInRight<T, Dest>::value && AllLeftInRight<Dest, T>::value;
451
452 template<typename T>
453 constexpr bool gt(T l, T r) {
454 return l > r;
455 }
456
457 template<typename From, typename To>
458 struct ConvertMorePrecise {
459 using T = typename meta::If<
460 gt(Convert<From, To>::template convert<true>(1.), Convert<To, From>::template convert<true>(1.)), To, From>::T;
461 };
462 } // namespace impl
463
470 template<typename Underlying>
471 struct UnitValue<Underlying, Units<>> {
472 Underlying val;
473 using UnitsType = Units<>;
474 using ValType = Underlying;
475
477 [[nodiscard]] constexpr operator Underlying() const noexcept { return val; }
479 constexpr UnitValue operator+(const UnitValue &o) const { return {val + o.val}; }
480
482 constexpr UnitValue &operator+=(const UnitValue &o) {
483 *this = static_cast<UnitValue>((*this) + o);
484 return *this;
485 }
486
488 constexpr UnitValue operator-(const UnitValue &o) const { return {val - o.val}; }
489
491 constexpr UnitValue &operator-=(const UnitValue &o) {
492 *this = static_cast<UnitValue>((*this) + o);
493 return *this;
494 }
495
497 constexpr auto operator<=>(const UnitValue &o) const noexcept { return val <=> o.val; }
499 constexpr auto operator==(const UnitValue &o) const noexcept { return val == o.val; }
501 constexpr auto operator<(const UnitValue &o) const noexcept { return val < o.val; }
503 constexpr auto operator<=(const UnitValue &o) const noexcept { return val <= o.val; }
505 constexpr auto operator>(const UnitValue &o) const noexcept { return val > o.val; }
507 constexpr auto operator>=(const UnitValue &o) const noexcept { return val >= o.val; }
509 constexpr auto operator!=(const UnitValue &o) const noexcept { return val != o.val; }
510
512 constexpr auto operator<=>(const Underlying &o) const noexcept { return val <=> o; }
514 constexpr auto operator==(const Underlying &o) const noexcept { return val == o; }
516 constexpr auto operator<(const Underlying &o) const noexcept { return val < o; }
518 constexpr auto operator<=(const Underlying &o) const noexcept { return val <= o; }
520 constexpr auto operator>(const Underlying &o) const noexcept { return val > o; }
522 constexpr auto operator>=(const Underlying &o) const noexcept { return val >= o; }
524 constexpr auto operator!=(const Underlying &o) const noexcept { return val != o; }
525 };
526
534 template<typename Underlying, typename U1, typename... UnitsSub>
535 struct UnitValue<Underlying, Units<U1, UnitsSub...>> {
537 Underlying val;
538 using Us = Units<U1, UnitsSub...>;
540 using UnitsType = Us;
542 using ValType = Underlying;
543
545 template<typename OU, impl::Convertible<Us> OD>
546 [[nodiscard]] constexpr operator UnitValue<OU, OD>() const {
547 if constexpr (std::is_same_v<OD, Us>) {
548 return UnitValue<OU, OD>{static_cast<OU>(val)};
549 }
550 else {
551 using Converter = impl::Convert<Us, OD>;
552 return {Converter::template convert<false>(val)};
553 }
554 }
555
557 template<typename Target, typename U = Underlying, bool ignoreChecks = false>
558 [[nodiscard]] constexpr auto convert() const {
559 using Converter = impl::Convert<Us, Target>;
561 }
562
564 template<typename Target, typename U = Underlying, bool ignoreChecks = false>
565 [[nodiscard]] constexpr auto to(Target) const {
566 using Converter = impl::Convert<Us, Target>;
568 }
569
571 constexpr auto operator+(const UnitValue &o) const {
572 auto r = val + o.val;
573 return UnitValue<decltype(r), Us>{r};
574 }
575
577 constexpr UnitValue &operator+=(const UnitValue &o) {
578 *this = static_cast<UnitValue>((*this) + o);
579 return *this;
580 }
581
583 constexpr auto operator-(const UnitValue &o) const {
584 auto r = val - o.val;
585 return UnitValue<decltype(r), Us>{r};
586 }
587
589 constexpr UnitValue &operator-=(const UnitValue &o) {
590 *this = static_cast<UnitValue>((*this) + o);
591 return *this;
592 }
593
595 template<typename OV, impl::Convertible<Us> OUs, bool ignoreChecks = false>
596 constexpr auto operator+(const UnitValue<OV, OUs> &o) const {
597 using Target =
598 typename meta::If<std::is_integral_v<Underlying>, typename impl::ConvertMorePrecise<Us, OUs>::T, UnitsType>::T;
599 auto r = impl::Convert<Us, Target>::template convert<ignoreChecks>(val) +
600 impl::Convert<OUs, Target>::template convert<ignoreChecks>(o.val);
601 return UnitValue<decltype(r), Target>{r};
602 }
603
605 template<typename OV, impl::Convertible<Us> OUs, bool ignoreChecks = false>
607 *this = static_cast<UnitValue>((*this) + impl::Convert<OUs, Us>::template convert<ignoreChecks>(o.val));
608 return *this;
609 }
610
612 template<typename OV, impl::Convertible<Us> OUs, bool ignoreChecks = false>
613 constexpr auto operator-(const UnitValue<OV, OUs> &o) const {
614 using Target =
615 typename meta::If<std::is_integral_v<Underlying>, typename impl::ConvertMorePrecise<Us, OUs>::T, UnitsType>::T;
616 auto r = impl::Convert<Us, Target>::template convert<ignoreChecks>(val) -
617 impl::Convert<OUs, Target>::template convert<ignoreChecks>(o.val);
618 return UnitValue<decltype(r), Target>{r};
619 }
620
622 template<typename OV, impl::Convertible<Us> OUs, bool ignoreChecks = false>
624 *this = static_cast<UnitValue>((*this) - impl::Convert<OUs, Us>::template convert<ignoreChecks>(o.val));
625 return *this;
626 }
627
629 constexpr auto operator<=>(const UnitValue &o) const noexcept { return val <=> o.val; }
631 constexpr auto operator==(const UnitValue &o) const noexcept { return val == o.val; }
633 constexpr auto operator<(const UnitValue &o) const noexcept { return val < o.val; }
635 constexpr auto operator<=(const UnitValue &o) const noexcept { return val <= o.val; }
637 constexpr auto operator>(const UnitValue &o) const noexcept { return val > o.val; }
639 constexpr auto operator>=(const UnitValue &o) const noexcept { return val >= o.val; }
641 constexpr auto operator!=(const UnitValue &o) const noexcept { return val != o.val; }
642
644 template<typename OV, impl::Convertible<Us> OUs, bool ignoreChecks = false>
645 constexpr auto operator<=>(const UnitValue<OV, OUs> &o) const {
646 using Target = typename impl::ConvertMorePrecise<Us, OUs>::T;
647 return impl::Convert<Us, Target>::template convert<ignoreChecks>(val) <=>
648 impl::Convert<OUs, Target>::template convert<ignoreChecks>(o.val);
649 }
650
652 template<typename OV, impl::Convertible<Us> OUs, bool ignoreChecks = false>
653 constexpr auto operator!=(const UnitValue<OV, OUs> &o) const {
654 using Target = typename impl::ConvertMorePrecise<Us, OUs>::T;
655 return impl::Convert<Us, Target>::template convert<ignoreChecks>(val) !=
656 impl::Convert<OUs, Target>::template convert<ignoreChecks>(o.val);
657 }
658
660 template<typename OV, impl::Convertible<Us> OUs, bool ignoreChecks = false>
661 constexpr auto operator==(const UnitValue<OV, OUs> &o) const {
662 using Target = typename impl::ConvertMorePrecise<Us, OUs>::T;
663 return impl::Convert<Us, Target>::template convert<ignoreChecks>(val) ==
664 impl::Convert<OUs, Target>::template convert<ignoreChecks>(o.val);
665 }
666
668 template<typename OV, impl::Convertible<Us> OUs, bool ignoreChecks = false>
669 constexpr auto operator>=(const UnitValue<OV, OUs> &o) const {
670 using Target = typename impl::ConvertMorePrecise<Us, OUs>::T;
671 return impl::Convert<Us, Target>::template convert<ignoreChecks>(val) >=
672 impl::Convert<OUs, Target>::template convert<ignoreChecks>(o.val);
673 }
674
676 template<typename OV, impl::Convertible<Us> OUs, bool ignoreChecks = false>
677 constexpr auto operator>(const UnitValue<OV, OUs> &o) const {
678 using Target = typename impl::ConvertMorePrecise<Us, OUs>::T;
679 return impl::Convert<Us, Target>::template convert<ignoreChecks>(val) >
680 impl::Convert<OUs, Target>::template convert<ignoreChecks>(o.val);
681 }
682
684 template<typename OV, impl::Convertible<Us> OUs, bool ignoreChecks = false>
685 constexpr auto operator<=(const UnitValue<OV, OUs> &o) const {
686 using Target = typename impl::ConvertMorePrecise<Us, OUs>::T;
687 return impl::Convert<Us, Target>::template convert<ignoreChecks>(val) <=
688 impl::Convert<OUs, Target>::template convert<ignoreChecks>(o.val);
689 }
690
692 template<typename OV, impl::Convertible<Us> OUs, bool ignoreChecks = false>
693 constexpr auto operator<(const UnitValue<OV, OUs> &o) const {
694 using Target = typename impl::ConvertMorePrecise<Us, OUs>::T;
695 return impl::Convert<Us, Target>::template convert<ignoreChecks>(val) <
696 impl::Convert<OUs, Target>::template convert<ignoreChecks>(o.val);
697 }
698 };
699
705 namespace dimensions {
707 constexpr size_t LENGTH = 0;
709 constexpr size_t MASS = 1;
711 constexpr size_t TIME = 2;
713 constexpr size_t ANGLE = 3;
715 constexpr size_t CURRENT = 4;
717 constexpr size_t TEMPERATURE = 5;
719 constexpr size_t SUBSTANCE = 6;
721 constexpr size_t LUMINOSITY = 7;
723 constexpr size_t STORAGE = 8;
724 } // namespace dimensions
725
727 template<std::floating_point Val, typename... Dims>
728 constexpr UnitValue<Val, Units<Dims...>> operator*(Val lhs, const Units<Dims...> &) {
729 return {lhs};
730 }
731
733 template<std::integral Val, typename... Dims>
734 constexpr UnitValue<Val, Units<Dims...>> operator*(Val lhs, const Units<Dims...> &) {
735 return {lhs};
736 }
737
739 template<typename... Dims1, typename... Dims2>
740 constexpr auto operator*(const Units<Dims1...> &, const Units<Dims2...> &) {
741 return typename impl::WithoutZeroExponents<typename impl::ConcatUnits<Units<Dims1...>, Units<Dims2...>>::T>::T{};
742 }
743
744 static_assert(
745 std::same_as<std::remove_cvref_t<decltype(Units<Unit<1>>{} * Units<Unit<1>>{})>, Units<Unit<1>, Unit<1>>>);
746
748 template<typename... Dims1, typename... Dims2>
749 constexpr auto operator/(const Units<Dims1...> &, const Units<Dims2...> &) {
750 return typename impl::WithoutZeroExponents<
751 typename impl::ConcatUnits<Units<Dims1...>, Units<typename impl::FlipUnit<Dims2>::T...>>::T>::T{};
752 }
753
755 template<std::floating_point Val, typename... Dims>
756 constexpr auto operator/(Val lhs, const Units<Dims...> &) {
757 return UnitValue<Val, typename impl::WithoutZeroExponents<Units<typename impl::FlipUnit<Dims...>::T>>::T>{lhs};
758 }
759
761 template<std::integral Val, typename... Dims>
762 constexpr auto operator/(Val lhs, const Units<Dims...> &) {
763 return UnitValue<Val, typename impl::WithoutZeroExponents<Units<typename impl::FlipUnit<Dims...>::T>>::T>{lhs};
764 }
765
766 static_assert(std::same_as<decltype(Units<Unit<1>>{} / Units<Unit<1>>{}), Units<>>);
767
769 template<typename U1, typename Us1, typename U2, typename Us2>
770 constexpr auto operator*(const UnitValue<U1, Us1> &lhs, const UnitValue<U2, Us2> &rhs) {
771 using RetUnits = typename impl::ConcatUnits<std::remove_const_t<typename UnitValue<U1, Us1>::UnitsType>,
772 std::remove_const_t<typename UnitValue<U2, Us2>::UnitsType>>::T;
773 const auto r = lhs.val * rhs.val;
774 return UnitValue<std::remove_cvref_t<decltype(r)>, RetUnits>{r};
775 }
776
778 template<typename U1, typename Us1, typename U2, typename Us2>
779 constexpr auto operator/(const UnitValue<U1, Us1> &lhs, const UnitValue<U2, Us2> &rhs) {
780 using RetUnits = typename impl::ConcatUnits<std::remove_const_t<typename UnitValue<U1, Us1>::UnitsType>,
781 std::remove_const_t<typename UnitValue<U2, Us2>::UnitsType>>::T;
782 const auto r = lhs.val * rhs.val;
783 if constexpr (std::is_same_v<Units<>, RetUnits>) {
784 return r;
785 }
786 return UnitValue<std::remove_cvref_t<decltype(r)>, RetUnits>{r};
787 }
788
790 template<std::floating_point I, typename U, typename Us>
791 constexpr auto operator/(I value, UnitValue<U, Us> lhs) {
792 using RetUnits = typename std::remove_const_t<decltype(1 / std::declval<Us>())>::UnitsType;
793 return UnitValue<U, RetUnits>{static_cast<U>(value) / lhs.val};
794 }
795
797 template<std::floating_point I, typename U, typename Us>
798 constexpr auto operator/(UnitValue<U, Us> lhs, I value) {
799 using RetUnits = std::remove_const_t<Us>;
800 return UnitValue<U, RetUnits>{lhs.val / static_cast<U>(value)};
801 }
802
804 template<std::integral I, typename U, typename Us>
805 constexpr auto operator/(I value, const UnitValue<U, Us> &lhs) {
806 using RetUnits = typename std::remove_const_t<decltype(1 / std::declval<Us>())>::UnitsType;
807 return UnitValue<U, RetUnits>{static_cast<U>(value) / lhs.val};
808 }
809
811 template<std::integral I, typename U, typename Us>
812 constexpr auto operator/(UnitValue<U, Us> lhs, I value) {
813 using RetUnits = typename std::remove_const_t<decltype(1 / std::declval<Us>())>::UnitsType;
814 return UnitValue<U, RetUnits>{lhs.val / value};
815 }
816
818 template<std::floating_point Val, typename U, typename Us>
819 constexpr UnitValue<U, Us> operator*(const UnitValue<U, Us> &lhs, Val rhs) {
820 return {static_cast<U>(lhs) * rhs.val};
821 }
822
824 template<std::floating_point Val, typename U, typename Us>
825 constexpr UnitValue<Val, Us> operator*(Val lhs, const UnitValue<U, Us> &rhs) {
826 return {lhs * static_cast<Val>(rhs.val)};
827 }
828
830 template<std::integral Val, typename U, typename Us>
831 constexpr UnitValue<U, Us> operator*(const UnitValue<U, Us> &lhs, Val rhs) {
832 return {static_cast<U>(lhs) * rhs.val};
833 }
834
836 template<std::integral Val, typename U, typename Us>
837 constexpr UnitValue<Val, Us> operator*(Val lhs, const UnitValue<U, Us> &rhs) {
838 return {lhs * static_cast<Val>(rhs.val)};
839 }
840
842 template<typename U, typename Us, typename... Dims>
843 constexpr auto operator*(UnitValue<U, Us> lhs, const Units<Dims...> &u) {
844 using RetUnits = std::remove_const_t<decltype(std::declval<typename UnitValue<U, Us>::UnitsType>() * u)>;
845 return UnitValue<U, RetUnits>{lhs.val};
846 }
847
849 template<typename U, typename Us, typename... Dims>
850 constexpr auto operator/(UnitValue<U, Us> lhs, const Units<Dims...> &u) {
851 using RetUnits = std::remove_const_t<decltype(std::declval<typename UnitValue<U, Us>::UnitsType>() / u)>;
852 return UnitValue<U, RetUnits>{lhs.val};
853 }
854
856 template<typename T>
857 concept IsUnitValue = requires(const T &t, const typename T::UnitsType UT) {
858 { t.template convert<typename T::UnitsType>() };
859 { t.to(UT) };
860 { t + t };
861 { t - t };
862 { t * t };
863 { t / t };
864 { 1 * t };
865 { 1 / t };
866 { t * 1 };
867 { t / 1 };
868 { 1. * t };
869 { 1. / t };
870 { t * 1. };
871 { t / 1. };
872 };
874
879 template<typename U>
880 struct UnitName;
881
882#define MTCORE_DEF_UNIT_NAME_NO_ABBREV(NS, UNIT) \
883 template<> \
884 struct UnitName<std::remove_cvref_t<decltype(NS::UNIT)>> { \
885 static constexpr std::string_view nameUs = #UNIT; \
886 static constexpr std::string_view name = #UNIT; \
887 static constexpr std::string_view abbrev = #UNIT; \
888 static constexpr std::string_view abbrevUs = #UNIT; \
889 static constexpr std::string_view abbrevAscii = #UNIT; \
890 static constexpr std::string_view symbol = ""; \
891 };
892
893#define MTCORE_DEF_UNIT_NAME_SYM(NS, UNIT, ABBREV, SYM) \
894 template<> \
895 struct UnitName<std::remove_cvref_t<decltype(NS::UNIT)>> { \
896 static constexpr std::string_view nameUs = #UNIT; \
897 static constexpr std::string_view name = #UNIT; \
898 static constexpr std::string_view abbrev = #ABBREV; \
899 static constexpr std::string_view abbrevUs = #ABBREV; \
900 static constexpr std::string_view abbrevAscii = #ABBREV; \
901 static constexpr std::string_view symbol = SYM; \
902 };
903
904#define MTCORE_DEF_UNIT_NAME_STRS(NS, UNIT, NAME, US_NAME, ABBREV, ABBREV_ASCII, ABBREV_US, SYMBOL) \
905 template<> \
906 struct UnitName<std::remove_cvref_t<decltype(NS::UNIT)>> { \
907 static constexpr std::string_view nameUs = US_NAME; \
908 static constexpr std::string_view name = NAME; \
909 static constexpr std::string_view abbrev = ABBREV; \
910 static constexpr std::string_view abbrevUs = ABBREV; \
911 static constexpr std::string_view abbrevAscii = ABBREV_ASCII; \
912 static constexpr std::string_view symbol = SYMBOL; \
913 };
914
915#define MTCORE_DEF_UNIT_NAME(NS, UNIT, ABBREV) \
916 template<> \
917 struct UnitName<std::remove_cvref_t<decltype(NS::UNIT)>> { \
918 static constexpr std::string_view nameUs = #UNIT; \
919 static constexpr std::string_view name = #UNIT; \
920 static constexpr std::string_view abbrev = #ABBREV; \
921 static constexpr std::string_view abbrevUs = #ABBREV; \
922 static constexpr std::string_view abbrevAscii = #ABBREV; \
923 static constexpr std::string_view symbol = ""; \
924 };
925
926#define MTCORE_DEF_UNIT_NAME_DIFF_US(NS, UNIT, US_NAME, ABBREV) \
927 template<> \
928 struct UnitName<std::remove_cvref_t<decltype(NS::UNIT)>> { \
929 static constexpr std::string_view nameUs = #US_NAME; \
930 static constexpr std::string_view name = #UNIT; \
931 static constexpr std::string_view abbrev = #ABBREV; \
932 static constexpr std::string_view abbrevUs = #ABBREV; \
933 static constexpr std::string_view abbrevAscii = #ABBREV; \
934 static constexpr std::string_view symbol = ""; \
935 };
936
937#define MTCORE_DEF_UNIT_NAME_UNIC_ABB(NS, UNIT, US_NAME, ABBREV, UNIC_ABB) \
938 template<> \
939 struct UnitName<std::remove_cvref_t<decltype(NS::UNIT)>> { \
940 static constexpr std::string_view nameUs = #US_NAME; \
941 static constexpr std::string_view name = #UNIT; \
942 static constexpr std::string_view abbrev = #UNIC_ABB; \
943 static constexpr std::string_view abbrevUs = #UNIC_ABB; \
944 static constexpr std::string_view abbrevAscii = #ABBREV; \
945 static constexpr std::string_view symbol = ""; \
946 };
947
948#define MTCORE_DEF_UNIT_NAME_SI_PREFIXES_SMALL(NS, UNIT, US_NAME, ABBREV) \
949 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, deci##UNIT, deci##US_NAME, d##ABBREV) \
950 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, centi##UNIT, centi##US_NAME, c##ABBREV) \
951 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, milli##UNIT, milli##US_NAME, m##ABBREV) \
952 MTCORE_DEF_UNIT_NAME_UNIC_ABB(NS, micro##UNIT, micro##US_NAME, u##ABBREV, μ##ABBREV) \
953 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, nano##UNIT, nano##US_NAME, n##ABBREV) \
954 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, pico##UNIT, pico##US_NAME, p##ABBREV) \
955 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, femto##UNIT, femto##US_NAME, f##ABBREV)
956
957#define MTCORE_DEF_UNIT_NAME_SI_PREFIXES_LARGE(NS, UNIT, US_NAME, ABBREV) \
958 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, deca##UNIT, deca##US_NAME, da##ABBREV) \
959 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, hecto##UNIT, hecto##US_NAME, hm##ABBREV) \
960 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, kilo##UNIT, kilo##US_NAME, km##ABBREV) \
961 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, mega##UNIT, mega##US_NAME, M##ABBREV) \
962 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, giga##UNIT, giga##US_NAME, G##ABBREV) \
963 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, tera##UNIT, tera##US_NAME, T##ABBREV) \
964 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, peta##UNIT, peta##US_NAME, P##ABBREV)
965
966#define MTCORE_DEF_UNIT_NAME_SI(NS, UNIT, US_NAME, ABBREV) \
967 MTCORE_DEF_UNIT_NAME_DIFF_US(NS, UNIT, US_NAME, ABBREV) \
968 MTCORE_DEF_UNIT_NAME_SI_PREFIXES_SMALL(NS, UNIT, US_NAME, ABBREV) \
969 MTCORE_DEF_UNIT_NAME_SI_PREFIXES_LARGE(NS, UNIT, US_NAME, ABBREV)
970
971} // namespace mtcore::units
972
973#endif // MTCORE_UNITS_BASE_HPP
Concept to check something is a unit value.
Concept to check that a type is a unit.
constexpr UnitValue< Val, Units< Dims... > > operator*(Val lhs, const Units< Dims... > &)
Multiply a number with units to get a unit value.
constexpr auto operator/(const Units< Dims1... > &, const Units< Dims2... > &)
Divide units to combine them.
Represents prebuilt dimensions for units.
constexpr size_t LENGTH
For units representing length.
constexpr size_t STORAGE
For units representing storage.
constexpr size_t ANGLE
For units representing angles.
constexpr size_t CURRENT
For units representing current (electricity)
constexpr size_t LUMINOSITY
For units representing luminosity.
constexpr size_t TEMPERATURE
For units representing temperature.
constexpr size_t MASS
For units representing mass.
constexpr size_t SUBSTANCE
For units representing substance.
constexpr size_t TIME
For units representing time.
Base namespace for units.
Definition angle.hpp:24
Switches types based on a condition.
Definition meta/base.hpp:39
Template for storing unit names.
constexpr auto operator!=(const UnitValue &o) const noexcept
Compare with a unit of the same type.
constexpr auto operator<(const UnitValue< OV, OUs > &o) const
Compare with a unit of a convertible type.
constexpr auto operator+(const UnitValue &o) const
Add to a unit of the same type.
constexpr auto operator==(const UnitValue &o) const noexcept
Compare with a unit of the same type.
constexpr auto operator>=(const UnitValue &o) const noexcept
Compare with a unit of the same type.
constexpr auto operator!=(const UnitValue< OV, OUs > &o) const
Compare with a unit of a convertible type.
constexpr auto convert() const
Convert to another unit value type.
constexpr auto to(Target) const
Convert to the specified unit.
constexpr auto operator<=(const UnitValue &o) const noexcept
Compare with a unit of the same type.
constexpr auto operator>(const UnitValue< OV, OUs > &o) const
Compare with a unit of a convertible type.
constexpr auto operator<=>(const UnitValue &o) const noexcept
Compare with a unit of the same type.
constexpr auto operator==(const UnitValue< OV, OUs > &o) const
Compare with a unit of a convertible type.
constexpr UnitValue & operator+=(const UnitValue &o)
Add to a unit of the same type.
constexpr UnitValue & operator-=(const UnitValue< OV, OUs > &o)
Subtract from a unit of a compatible type.
constexpr auto operator-(const UnitValue &o) const
Subtract with a unit of the same type.
constexpr auto operator>=(const UnitValue< OV, OUs > &o) const
Compare with a unit of a convertible type.
constexpr auto operator<(const UnitValue &o) const noexcept
Compare with a unit of the same type.
constexpr auto operator<=(const UnitValue< OV, OUs > &o) const
Compare with a unit of a convertible type.
constexpr auto operator-(const UnitValue< OV, OUs > &o) const
Subtract from a unit of a compatible type.
constexpr UnitValue & operator-=(const UnitValue &o)
Subtract with a unit of the same type.
constexpr auto operator>(const UnitValue &o) const noexcept
Compare with a unit of the same type.
constexpr auto operator+(const UnitValue< OV, OUs > &o) const
Add to a unit of a compatible type.
constexpr UnitValue & operator+=(const UnitValue< OV, OUs > &o)
Add to a unit of a compatible type.
constexpr auto operator<=>(const UnitValue< OV, OUs > &o) const
Compare with a unit of a convertible type.
constexpr auto operator==(const Underlying &o) const noexcept
Compare with a number.
constexpr auto operator<=>(const Underlying &o) const noexcept
Compare with a number.
constexpr UnitValue & operator-=(const UnitValue &o)
Subtract a number.
constexpr auto operator>=(const UnitValue &o) const noexcept
Compare with another unitless value.
constexpr auto operator!=(const UnitValue &o) const noexcept
Compare with another unitless value.
constexpr auto operator>(const Underlying &o) const noexcept
Compare with a number.
constexpr auto operator<=(const Underlying &o) const noexcept
Compare with a number.
constexpr auto operator>=(const Underlying &o) const noexcept
Compare with a number.
constexpr UnitValue operator+(const UnitValue &o) const
Add a number.
constexpr auto operator>(const UnitValue &o) const noexcept
Compare with another unitless value.
constexpr UnitValue & operator+=(const UnitValue &o)
Add a number.
constexpr auto operator==(const UnitValue &o) const noexcept
Compare with another unitless value.
constexpr auto operator<(const UnitValue &o) const noexcept
Compare with another unitless value.
constexpr auto operator<=(const UnitValue &o) const noexcept
Compare with another unitless value.
constexpr UnitValue operator-(const UnitValue &o) const
Subtract a number.
constexpr auto operator<=>(const UnitValue &o) const noexcept
Compare with another unitless value.
constexpr auto operator<(const Underlying &o) const noexcept
Compare with a number.
constexpr auto operator!=(const Underlying &o) const noexcept
Compare with a number.
Represents a value with associated units.
Represents a single unit of measurement.
List of units.