electro 0.3.0
Type-safe electrical units library modeled after std::chrono
Loading...
Searching...
No Matches
electro.hpp
Go to the documentation of this file.
1#pragma once
2
8#include <chrono>
9#include <compare>
10#include <concepts>
11#include <cstdint>
12#include <limits>
13#include <ratio>
14#include <string>
15#ifndef CONFIG_ELECTRO_STD_FORMAT
16#if __has_include(<format>) && defined(__cpp_lib_format)
17#define CONFIG_ELECTRO_STD_FORMAT 1
18#else
19#define CONFIG_ELECTRO_STD_FORMAT 0
20#endif
21#endif
22
23#if CONFIG_ELECTRO_STD_FORMAT
24#include <format>
25#endif
26#include <assert.h>
27
89namespace electro {
90
91template<typename Unit, typename Rep, typename Precision = std::ratio<1>>
92class quantity;
93
98template<typename T>
99struct is_quantity : std::false_type {};
100
101template<typename Unit, typename Rep, typename Precision>
102struct is_quantity<quantity<Unit, Rep, Precision>> : std::true_type {};
103
104template<typename T>
105inline constexpr bool is_quantity_v = is_quantity<T>::value;
106
108template<typename Rep>
110
120template<typename T>
121concept duration_like = requires(T t) {
122 { t.count() };
123 typename T::period;
124 typename T::rep;
125} && !is_quantity_v<T>;
132template<typename Rep>
135 static constexpr Rep zero() noexcept { return Rep(0); }
136
138 static constexpr Rep max() noexcept { return std::numeric_limits<Rep>::max(); }
139
141 static constexpr Rep min() noexcept { return std::numeric_limits<Rep>::lowest(); }
142};
143
144template<typename T>
145struct _is_ratio : std::false_type {};
146
147template<std::intmax_t Num, std::intmax_t Denom>
148struct _is_ratio<std::ratio<Num, Denom>> : std::true_type {};
149
158template<typename T>
159struct treat_as_inexact : std::bool_constant<std::floating_point<T>> {};
160
161template<typename T>
163
165consteval intmax_t _gcd(intmax_t m, intmax_t n) noexcept {
166 while (n != 0) {
167 intmax_t rem = m % n;
168 m = n;
169 n = rem;
170 }
171 return m;
172}
173
174// Runtime GCD for integer types (using Euclidean algorithm)
175template<typename T>
176constexpr T _runtime_gcd(T m, T n) noexcept {
177 if (m < 0) {
178 m = -m;
179 }
180 if (n < 0) {
181 n = -n;
182 }
183 while (n != 0) {
184 T rem = m % n;
185 m = n;
186 n = rem;
187 }
188 return m;
189}
190
191template<typename R1, typename R2>
192inline constexpr intmax_t _safe_ratio_divide_den = [] {
193 constexpr intmax_t g1 = _gcd(R1::num, R2::num);
194 constexpr intmax_t g2 = _gcd(R1::den, R2::den);
195 return (R1::den / g2) * (R2::num / g1);
196}();
197
198template<typename From, typename To>
202// ============================================================================
203// Units
204// ============================================================================
205
215struct volt_unit {
216 static constexpr const char* symbol = "V";
217};
218
221 static constexpr const char* symbol = "A";
222};
223
225struct ohm_unit {
226 static constexpr const char* symbol = "Ω";
227};
228
230struct watt_unit {
231 static constexpr const char* symbol = "W";
232};
233
236 static constexpr const char* symbol = "C";
237};
238
241 static constexpr const char* symbol = "J";
242};
243
246 static constexpr const char* symbol = "F";
247};
248
251 static constexpr const char* symbol = "H";
252};
253
// end of Units group
255
285template<typename Unit, typename Rep, typename Precision>
286class quantity {
287 static_assert(!is_quantity_v<Rep>, "rep cannot be an electro::quantity");
288 static_assert(_is_ratio<Precision>::value, "precision must be a specialization of std::ratio");
289 static_assert(Precision::num > 0, "precision must be positive");
290
291public:
293 using unit = Unit;
295 using rep = Rep;
297 using precision = typename Precision::type;
298
300 constexpr quantity() = default;
301 quantity(const quantity&) = default;
302
312 template<typename Rep2>
313 requires std::convertible_to<const Rep2&, rep> && (treat_as_inexact_v<rep> || !treat_as_inexact_v<Rep2>)
314 constexpr explicit quantity(const Rep2& r)
315 : _r(static_cast<rep>(r)) {}
316
328 template<typename Rep2, typename Precision2>
329 requires std::convertible_to<const Rep2&, rep> &&
333
341 template<typename Rep2, typename Precision2>
342 requires(!std::is_same_v<quantity, quantity<Unit, Rep2, Precision2>>) && (!treat_as_inexact_v<rep>) &&
344 constexpr explicit quantity(const quantity<Unit, Rep2, Precision2>& q)
346
347 ~quantity() = default;
348 quantity& operator=(const quantity&) = default;
349
351 constexpr rep count() const { return _r; }
352
356
360
361 constexpr quantity& operator++() {
362 ++_r;
363 return *this;
364 }
365
366 constexpr quantity operator++(int) { return quantity(_r++); }
367
368 constexpr quantity& operator--() {
369 --_r;
370 return *this;
371 }
372
373 constexpr quantity operator--(int) { return quantity(_r--); }
374
375 constexpr quantity& operator+=(const quantity& q) {
376 _r += q.count();
377 return *this;
378 }
379
380 constexpr quantity& operator-=(const quantity& q) {
381 _r -= q.count();
382 return *this;
383 }
384
385 constexpr quantity& operator*=(const rep& r) {
386 _r *= r;
387 return *this;
388 }
389
390 constexpr quantity& operator/=(const rep& r) {
391 _r /= r;
392 return *this;
393 }
394
395 constexpr quantity& operator%=(const rep& r)
396 requires(!treat_as_inexact_v<rep>)
397 {
398 _r %= r;
399 return *this;
400 }
401
402 constexpr quantity& operator%=(const quantity& q)
403 requires(!treat_as_inexact_v<rep>)
404 {
405 _r %= q.count();
406 return *this;
407 }
408
410 static constexpr quantity zero() noexcept { return quantity(quantity_values<rep>::zero()); }
411
413 static constexpr quantity min() noexcept { return quantity(quantity_values<rep>::min()); }
414
416 static constexpr quantity max() noexcept { return quantity(quantity_values<rep>::max()); }
417
418private:
419 rep _r{};
420};
421
437template<typename ToQuantity, typename Unit, typename Rep, typename Precision>
439 static_assert(
440 std::is_same_v<typename ToQuantity::unit, Unit>, "quantity_cast cannot convert between different units"
441 );
442 if constexpr (std::is_same_v<ToQuantity, quantity<Unit, Rep, Precision>>) {
443 return q;
444 } else {
445 using to_rep = typename ToQuantity::rep;
446 using to_precision = typename ToQuantity::precision;
447 using cf = std::ratio_divide<Precision, to_precision>;
448
449 // Use wider intermediate type for integer-to-integer conversions
450 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
451#ifdef __SIZEOF_INT128__
452 using cr = std::common_type_t<to_rep, Rep, intmax_t>;
453 using wider_t = std::conditional_t<std::is_signed_v<cr>, __int128, unsigned __int128>;
454#else
455 using wider_t = intmax_t;
456#endif
457
458 if constexpr (cf::den == 1 && cf::num == 1) {
459 return ToQuantity(static_cast<to_rep>(q.count()));
460 } else if constexpr (cf::den == 1) {
461 wider_t result = static_cast<wider_t>(q.count()) * static_cast<wider_t>(cf::num);
462 return ToQuantity(static_cast<to_rep>(result));
463 } else if constexpr (cf::num == 1) {
464 wider_t result = static_cast<wider_t>(q.count()) / static_cast<wider_t>(cf::den);
465 return ToQuantity(static_cast<to_rep>(result));
466 } else {
467 // Use GCD to reduce operands: count * num / den
468 wider_t count = static_cast<wider_t>(q.count());
469 wider_t den = static_cast<wider_t>(cf::den);
470 wider_t g = _runtime_gcd(count, den);
471 wider_t reduced_count = count / g;
473 wider_t result = reduced_count * static_cast<wider_t>(cf::num) / reduced_den;
474 return ToQuantity(static_cast<to_rep>(result));
475 }
476 } else {
477 // Floating-point path
478 using cr = std::common_type_t<to_rep, Rep, intmax_t>;
479 if constexpr (cf::den == 1 && cf::num == 1) {
480 return ToQuantity(static_cast<to_rep>(q.count()));
481 } else if constexpr (cf::den == 1) {
482 return ToQuantity(static_cast<to_rep>(static_cast<cr>(q.count()) * static_cast<cr>(cf::num)));
483 } else if constexpr (cf::num == 1) {
484 return ToQuantity(static_cast<to_rep>(static_cast<cr>(q.count()) / static_cast<cr>(cf::den)));
485 } else {
486 return ToQuantity(
487 static_cast<to_rep>(
488 static_cast<cr>(q.count()) * static_cast<cr>(cf::num) / static_cast<cr>(cf::den)
489 )
490 );
491 }
492 }
493 }
494}
495
513template<typename ToQuantity, typename Unit, typename Rep, typename Precision>
515 using to_rep = typename ToQuantity::rep;
517
518 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
519 if (result > q) {
520 return ToQuantity(result.count() - to_rep(1));
521 }
522 }
523
524 return result;
525}
526
544template<typename ToQuantity, typename Unit, typename Rep, typename Precision>
546 using to_rep = typename ToQuantity::rep;
548
549 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
550 if (result < q) {
551 return ToQuantity(result.count() + to_rep(1));
552 }
553 }
554
555 return result;
556}
557
575template<typename ToQuantity, typename Unit, typename Rep, typename Precision>
577 using to_rep = typename ToQuantity::rep;
578
579 if constexpr (std::is_integral_v<Rep> && std::is_integral_v<to_rep>) {
582
583 auto diff_lower = q - lower;
584 auto diff_upper = upper - q;
585
586 if (diff_lower < diff_upper) {
587 return lower;
588 } else if (diff_lower > diff_upper) {
589 return upper;
590 } else {
591 // Tie: round to even
592 return (lower.count() % to_rep(2) == to_rep(0)) ? lower : upper;
593 }
594 } else {
596 }
597}
598
609template<typename Unit, typename Rep, typename Precision>
613
614// ============================================================================
615// Same-unit arithmetic
616// ============================================================================
617
619template<typename Unit, typename Rep1, typename Precision1, typename Rep2, typename Precision2>
621 -> std::common_type_t<quantity<Unit, Rep1, Precision1>, quantity<Unit, Rep2, Precision2>> {
622 using cq = std::common_type_t<quantity<Unit, Rep1, Precision1>, quantity<Unit, Rep2, Precision2>>;
623 return cq(cq(lhs).count() + cq(rhs).count());
624}
625
627template<typename Unit, typename Rep1, typename Precision1, typename Rep2, typename Precision2>
629 -> std::common_type_t<quantity<Unit, Rep1, Precision1>, quantity<Unit, Rep2, Precision2>> {
630 using cq = std::common_type_t<quantity<Unit, Rep1, Precision1>, quantity<Unit, Rep2, Precision2>>;
631 return cq(cq(lhs).count() - cq(rhs).count());
632}
633
635template<typename Unit, typename Rep1, typename Precision, typename Rep2>
636 requires not_quantity<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
642
644template<typename Unit, typename Rep1, typename Rep2, typename Precision>
645 requires not_quantity<Rep1> && std::convertible_to<const Rep1&, std::common_type_t<Rep1, Rep2>>
650
652template<typename Unit, typename Rep1, typename Precision, typename Rep2>
653 requires not_quantity<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>>
659
661template<typename Unit, typename Rep1, typename Precision1, typename Rep2, typename Precision2>
663 -> std::common_type_t<Rep1, Rep2> {
664 using cq = std::common_type_t<quantity<Unit, Rep1, Precision1>, quantity<Unit, Rep2, Precision2>>;
665 return cq(lhs).count() / cq(rhs).count();
666}
667
669template<typename Unit, typename Rep1, typename Precision, typename Rep2>
670 requires not_quantity<Rep2> && std::convertible_to<const Rep2&, std::common_type_t<Rep1, Rep2>> &&
672constexpr auto operator%(const quantity<Unit, Rep1, Precision>& q, const Rep2& s)
675 return cq(cq(q).count() % s);
676}
677
679template<typename Unit, typename Rep1, typename Precision1, typename Rep2, typename Precision2>
682 -> std::common_type_t<quantity<Unit, Rep1, Precision1>, quantity<Unit, Rep2, Precision2>> {
683 using cq = std::common_type_t<quantity<Unit, Rep1, Precision1>, quantity<Unit, Rep2, Precision2>>;
684 return cq(cq(lhs).count() % cq(rhs).count());
685}
686
687template<typename Unit, typename Rep1, typename Precision1, typename Rep2, typename Precision2>
689 using ct = std::common_type_t<quantity<Unit, Rep1, Precision1>, quantity<Unit, Rep2, Precision2>>;
690 return ct(lhs).count() == ct(rhs).count();
691}
692
693template<typename Unit, typename Rep1, typename Precision1, typename Rep2, typename Precision2>
694 requires std::three_way_comparable<std::common_type_t<Rep1, Rep2>>
696 using ct = std::common_type_t<quantity<Unit, Rep1, Precision1>, quantity<Unit, Rep2, Precision2>>;
697 return ct(lhs).count() <=> ct(rhs).count();
698}
699
700// ============================================================================
701// Cross-unit arithmetic
702// ============================================================================
703
705// Product of two quantities, in the given result unit. Counts multiply and
706// precisions multiply, so no conversion factor is needed and the result is
707// exact for integer representations.
708template<typename ToUnit, typename U1, typename R1, typename P1, typename U2, typename R2, typename P2>
709constexpr auto _product(const quantity<U1, R1, P1>& lhs, const quantity<U2, R2, P2>& rhs) {
710 using cr = std::common_type_t<R1, R2>;
712 return result_t(static_cast<cr>(lhs.count()) * static_cast<cr>(rhs.count()));
713}
714
715// Quotient of two quantities, in the given result unit. Counts divide and
716// precisions divide; for integer representations the count division truncates.
717template<typename ToUnit, typename U1, typename R1, typename P1, typename U2, typename R2, typename P2>
718constexpr auto _quotient(const quantity<U1, R1, P1>& lhs, const quantity<U2, R2, P2>& rhs) {
719 using cr = std::common_type_t<R1, R2>;
721 return result_t(static_cast<cr>(lhs.count()) / static_cast<cr>(rhs.count()));
722}
723
724// Product of a quantity and a duration, in the given result unit.
725template<typename ToUnit, typename U1, typename R1, typename P1, duration_like D>
726constexpr auto _duration_product(const quantity<U1, R1, P1>& lhs, const D& rhs) {
727 using cr = std::common_type_t<R1, typename D::rep>;
729 return result_t(static_cast<cr>(lhs.count()) * static_cast<cr>(rhs.count()));
730}
731
732// Quotient of a quantity and a duration, in the given result unit.
733template<typename ToUnit, typename U1, typename R1, typename P1, duration_like D>
734constexpr auto _duration_quotient(const quantity<U1, R1, P1>& lhs, const D& rhs) {
735 using cr = std::common_type_t<R1, typename D::rep>;
737 return result_t(static_cast<cr>(lhs.count()) / static_cast<cr>(rhs.count()));
738}
739
740// Quotient of two quantities yielding a std::chrono::duration.
741template<typename U1, typename R1, typename P1, typename U2, typename R2, typename P2>
743 using cr = std::common_type_t<R1, R2>;
744 using result_t = std::chrono::duration<cr, std::ratio_divide<P1, P2>>;
745 return result_t(static_cast<cr>(lhs.count()) / static_cast<cr>(rhs.count()));
746}
747
748// Product of two quantities yielding a std::chrono::duration.
749template<typename U1, typename R1, typename P1, typename U2, typename R2, typename P2>
751 using cr = std::common_type_t<R1, R2>;
752 using result_t = std::chrono::duration<cr, std::ratio_multiply<P1, P2>>;
753 return result_t(static_cast<cr>(lhs.count()) * static_cast<cr>(rhs.count()));
754}
769template<typename R1, typename P1, typename R2, typename P2>
773
775template<typename R1, typename P1, typename R2, typename P2>
779
781template<typename R1, typename P1, typename R2, typename P2>
785
787template<typename R1, typename P1, typename R2, typename P2>
791
793template<typename R1, typename P1, typename R2, typename P2>
797
799template<typename R1, typename P1, typename R2, typename P2>
803
805template<typename R1, typename P1, typename R2, typename P2>
809
811template<typename R1, typename P1, typename R2, typename P2>
815
823template<typename R1, typename P1, duration_like D>
824constexpr auto operator*(const quantity<ampere_unit, R1, P1>& i, const D& t) {
826}
827
829template<duration_like D, typename R1, typename P1>
830constexpr auto operator*(const D& t, const quantity<ampere_unit, R1, P1>& i) {
832}
833
835template<typename R1, typename P1, duration_like D>
836constexpr auto operator/(const quantity<coulomb_unit, R1, P1>& q, const D& t) {
838}
839
847template<typename R1, typename P1, typename R2, typename P2>
851
859template<typename R1, typename P1, duration_like D>
860constexpr auto operator*(const quantity<watt_unit, R1, P1>& p, const D& t) {
862}
863
865template<duration_like D, typename R1, typename P1>
866constexpr auto operator*(const D& t, const quantity<watt_unit, R1, P1>& p) {
868}
869
871template<typename R1, typename P1, duration_like D>
872constexpr auto operator/(const quantity<joule_unit, R1, P1>& e, const D& t) {
874}
875
877template<typename R1, typename P1, typename R2, typename P2>
879 return _quotient_duration(e, p);
880}
881
889template<typename R1, typename P1, typename R2, typename P2>
893
895template<typename R1, typename P1, typename R2, typename P2>
899
901template<typename R1, typename P1, typename R2, typename P2>
905
907template<typename R1, typename P1, typename R2, typename P2>
911
913template<typename R1, typename P1, typename R2, typename P2>
917
919template<typename R1, typename P1, typename R2, typename P2>
923
931template<typename R1, typename P1, typename R2, typename P2>
933 return _product_duration(r, c);
934}
935
937template<typename R1, typename P1, typename R2, typename P2>
939 return _product_duration(c, r);
940}
941
949template<typename R1, typename P1, typename R2, typename P2>
951 return _quotient_duration(l, r);
952}
953
// end of CrossUnit group
955
956// ============================================================================
957// Type aliases
958// ============================================================================
959
970template<typename Rep, typename Precision = std::ratio<1>>
972
974template<typename Rep, typename Precision = std::ratio<1>>
976
978template<typename Rep, typename Precision = std::ratio<1>>
980
982template<typename Rep, typename Precision = std::ratio<1>>
984
986template<typename Rep, typename Precision = std::ratio<1>>
988
990template<typename Rep, typename Precision = std::ratio<1>>
992
994template<typename Rep, typename Precision = std::ratio<1>>
996
998template<typename Rep, typename Precision = std::ratio<1>>
1000
1009
1022
1033
1046
1057
1070
1081
1090
// end of QuantityTypes group
1092
1093// ============================================================================
1094// String conversion and formatting
1095// ============================================================================
1096
1103template<typename Precision>
1105
1107template<>
1108struct si_prefix<std::femto> {
1109 static constexpr const char* symbol = "f";
1110};
1111
1112template<>
1113struct si_prefix<std::pico> {
1114 static constexpr const char* symbol = "p";
1115};
1116
1117template<>
1118struct si_prefix<std::nano> {
1119 static constexpr const char* symbol = "n";
1120};
1121
1122template<>
1123struct si_prefix<std::micro> {
1124 static constexpr const char* symbol = "µ";
1125};
1126
1127template<>
1128struct si_prefix<std::milli> {
1129 static constexpr const char* symbol = "m";
1130};
1131
1132template<>
1133struct si_prefix<std::ratio<1>> {
1134 static constexpr const char* symbol = "";
1135};
1136
1137template<>
1138struct si_prefix<std::kilo> {
1139 static constexpr const char* symbol = "k";
1140};
1141
1142template<>
1143struct si_prefix<std::mega> {
1144 static constexpr const char* symbol = "M";
1145};
1146
1147template<>
1148struct si_prefix<std::giga> {
1149 static constexpr const char* symbol = "G";
1150};
1151
1152template<>
1153struct si_prefix<std::tera> {
1154 static constexpr const char* symbol = "T";
1155};
1169template<typename Unit, typename Precision>
1172 static std::string value()
1173 requires requires { si_prefix<Precision>::symbol; }
1174 {
1175 return std::string(si_prefix<Precision>::symbol) + Unit::symbol;
1176 }
1177};
1178
1180template<>
1181struct quantity_suffix<coulomb_unit, std::ratio<18, 5>> {
1182 static std::string value() { return "mAh"; }
1183};
1184
1185template<>
1186struct quantity_suffix<coulomb_unit, std::ratio<3600>> {
1187 static std::string value() { return "Ah"; }
1188};
1189
1190template<>
1191struct quantity_suffix<joule_unit, std::ratio<3600>> {
1192 static std::string value() { return "Wh"; }
1193};
1194
1195template<>
1196struct quantity_suffix<joule_unit, std::ratio<3600000>> {
1197 static std::string value() { return "kWh"; }
1198};
1199
1200template<typename Unit, typename Precision>
1202
1203// Display family for a (unit, precision) pair when a format spec is given:
1204// the value is rendered in `scale`-sized units labeled `suffix`. SI-prefixed
1205// precisions render in the base unit ("3.3V"); conventional non-decade
1206// precisions render in their conventional unit ("0.2Ah", never "720.0C").
1207template<typename Unit, typename Precision>
1208struct _display_unit {
1209 using scale = std::ratio<1>;
1210 static constexpr const char* suffix = Unit::symbol;
1211};
1212
1214 using scale = std::ratio<3600>;
1215 static constexpr const char* suffix = "Ah";
1216};
1217
1219 using scale = std::ratio<3600>;
1220 static constexpr const char* suffix = "Wh";
1221};
1222
1223template<>
1224struct _display_unit<coulomb_unit, std::ratio<18, 5>> : _ampere_hour_display_unit {};
1225
1226template<>
1227struct _display_unit<coulomb_unit, std::ratio<3600>> : _ampere_hour_display_unit {};
1228
1229template<>
1230struct _display_unit<joule_unit, std::ratio<3600>> : _watt_hour_display_unit {};
1231
1232template<>
1233struct _display_unit<joule_unit, std::ratio<3600000>> : _watt_hour_display_unit {};
1234
1235// Appends a NUL-terminated string to a format output iterator.
1236template<typename OutputIt>
1237constexpr OutputIt _format_append(OutputIt out, const char* s) {
1238 for (; *s != '\0'; ++s) {
1239 *out++ = *s;
1240 }
1241 return out;
1242}
1257template<typename Unit, typename Rep, typename Precision>
1259inline std::string to_string(const quantity<Unit, Rep, Precision>& q) {
1260 return std::to_string(q.count()) + quantity_suffix<Unit, typename Precision::type>::value();
1261}
1262
1263} // namespace electro
1264
1265namespace std {
1266
1267template<typename Unit, typename Rep1, typename Precision1, typename Rep2, typename Precision2>
1268struct common_type<electro::quantity<Unit, Rep1, Precision1>, electro::quantity<Unit, Rep2, Precision2>> {
1269private:
1270 using common_precision = std::ratio<
1271 electro::_gcd(Precision1::num, Precision2::num),
1272 (Precision1::den / electro::_gcd(Precision1::den, Precision2::den)) * Precision2::den>;
1273
1274public:
1276};
1277
1278#if CONFIG_ELECTRO_STD_FORMAT
1279// "{}" prints the exact stored count with a precision-qualified unit
1280// (millivolts(3300) -> "3300mV"). A non-empty spec is applied to the value in
1281// display units (as double): std::format("{:.1f}", millivolts(3300)) == "3.3V".
1282// SI-prefixed precisions display in the base unit; conventional non-decade
1283// precisions display in their conventional unit ("0.2Ah").
1284template<typename Unit, typename Rep, typename Precision>
1286struct formatter<electro::quantity<Unit, Rep, Precision>, char> {
1287private:
1289 // display units per count
1290 using _upc = typename ratio_divide<typename Precision::type, typename _display::scale>::type;
1291 std::formatter<double> _num;
1292 bool _has_spec = false;
1293
1294public:
1295 constexpr auto parse(format_parse_context& ctx) {
1296 auto it = ctx.begin();
1297 if (it == ctx.end() || *it == '}') {
1298 return it;
1299 }
1300 _has_spec = true;
1301 return _num.parse(ctx);
1302 }
1303
1304 template<typename FormatContext>
1305 auto format(const electro::quantity<Unit, Rep, Precision>& q, FormatContext& ctx) const {
1306 if (_has_spec) {
1307 double value = static_cast<double>(q.count()) * _upc::num / _upc::den;
1308 auto out = _num.format(value, ctx);
1309 return electro::_format_append(out, _display::suffix);
1310 }
1311 return std::format_to(
1313 );
1314 }
1315};
1316#endif
1317
1318} // namespace std
1319
1323namespace electro_literals {
1325namespace detail {
1326
1327template<unsigned long long Value, unsigned long long Power>
1328struct pow10 {
1329 static constexpr unsigned long long value = 10 * pow10<Value, Power - 1>::value;
1330};
1331
1332template<unsigned long long Value>
1333struct pow10<Value, 0> {
1334 static constexpr unsigned long long value = Value;
1335};
1336
1337template<char... Digits>
1338struct parse_int;
1339
1340template<char D, char... Rest>
1341struct parse_int<D, Rest...> {
1342 static_assert(D >= '0' && D <= '9', "invalid digit");
1343 static constexpr unsigned long long value = pow10<D - '0', sizeof...(Rest)>::value + parse_int<Rest...>::value;
1344};
1345
1346template<char D>
1347struct parse_int<D> {
1348 static_assert(D >= '0' && D <= '9', "invalid digit");
1349 static constexpr unsigned long long value = D - '0';
1350};
1351
1352template<typename Quantity, char... Digits>
1353constexpr Quantity check_overflow() {
1354 using parsed = parse_int<Digits...>;
1355 constexpr typename Quantity::rep repval = parsed::value;
1356 static_assert(
1357 repval >= 0 && static_cast<unsigned long long>(repval) == parsed::value,
1358 "literal value cannot be represented by quantity type"
1359 );
1360 return Quantity(repval);
1361}
1362
1363} // namespace detail
1367template<char... Digits>
1368constexpr electro::microvolts operator""_uV() {
1369 return detail::check_overflow<electro::microvolts, Digits...>();
1370}
1371
1373template<char... Digits>
1374constexpr electro::millivolts operator""_mV() {
1375 return detail::check_overflow<electro::millivolts, Digits...>();
1376}
1377
1379template<char... Digits>
1380constexpr electro::volts operator""_V() {
1381 return detail::check_overflow<electro::volts, Digits...>();
1382}
1383
1385template<char... Digits>
1386constexpr electro::kilovolts operator""_kV() {
1387 return detail::check_overflow<electro::kilovolts, Digits...>();
1388}
1389
1391template<char... Digits>
1392constexpr electro::microamperes operator""_uA() {
1393 return detail::check_overflow<electro::microamperes, Digits...>();
1394}
1395
1397template<char... Digits>
1398constexpr electro::milliamperes operator""_mA() {
1399 return detail::check_overflow<electro::milliamperes, Digits...>();
1400}
1401
1403template<char... Digits>
1404constexpr electro::amperes operator""_A() {
1405 return detail::check_overflow<electro::amperes, Digits...>();
1406}
1407
1409template<char... Digits>
1410constexpr electro::milliohms operator""_mOhm() {
1411 return detail::check_overflow<electro::milliohms, Digits...>();
1412}
1413
1415template<char... Digits>
1416constexpr electro::ohms operator""_Ohm() {
1417 return detail::check_overflow<electro::ohms, Digits...>();
1418}
1419
1421template<char... Digits>
1422constexpr electro::kiloohms operator""_kOhm() {
1423 return detail::check_overflow<electro::kiloohms, Digits...>();
1424}
1425
1427template<char... Digits>
1428constexpr electro::megaohms operator""_MOhm() {
1429 return detail::check_overflow<electro::megaohms, Digits...>();
1430}
1431
1433template<char... Digits>
1434constexpr electro::milliohms operator""_mΩ() {
1435 return detail::check_overflow<electro::milliohms, Digits...>();
1436}
1437
1439template<char... Digits>
1440constexpr electro::ohms operator""_Ω() {
1441 return detail::check_overflow<electro::ohms, Digits...>();
1442}
1443
1445template<char... Digits>
1446constexpr electro::kiloohms operator""_kΩ() {
1447 return detail::check_overflow<electro::kiloohms, Digits...>();
1448}
1449
1451template<char... Digits>
1452constexpr electro::megaohms operator""_MΩ() {
1453 return detail::check_overflow<electro::megaohms, Digits...>();
1454}
1455
1457template<char... Digits>
1458constexpr electro::microwatts operator""_uW() {
1459 return detail::check_overflow<electro::microwatts, Digits...>();
1460}
1461
1463template<char... Digits>
1464constexpr electro::milliwatts operator""_mW() {
1465 return detail::check_overflow<electro::milliwatts, Digits...>();
1466}
1467
1469template<char... Digits>
1470constexpr electro::watts operator""_W() {
1471 return detail::check_overflow<electro::watts, Digits...>();
1472}
1473
1475template<char... Digits>
1476constexpr electro::kilowatts operator""_kW() {
1477 return detail::check_overflow<electro::kilowatts, Digits...>();
1478}
1479
1481template<char... Digits>
1482constexpr electro::megawatts operator""_MW() {
1483 return detail::check_overflow<electro::megawatts, Digits...>();
1484}
1485
1487template<char... Digits>
1488constexpr electro::coulombs operator""_C() {
1489 return detail::check_overflow<electro::coulombs, Digits...>();
1490}
1491
1493template<char... Digits>
1494constexpr electro::milliampere_hours operator""_mAh() {
1495 return detail::check_overflow<electro::milliampere_hours, Digits...>();
1496}
1497
1499template<char... Digits>
1500constexpr electro::ampere_hours operator""_Ah() {
1501 return detail::check_overflow<electro::ampere_hours, Digits...>();
1502}
1503
1505template<char... Digits>
1506constexpr electro::millijoules operator""_mJ() {
1507 return detail::check_overflow<electro::millijoules, Digits...>();
1508}
1509
1511template<char... Digits>
1512constexpr electro::joules operator""_J() {
1513 return detail::check_overflow<electro::joules, Digits...>();
1514}
1515
1517template<char... Digits>
1518constexpr electro::kilojoules operator""_kJ() {
1519 return detail::check_overflow<electro::kilojoules, Digits...>();
1520}
1521
1523template<char... Digits>
1524constexpr electro::watt_hours operator""_Wh() {
1525 return detail::check_overflow<electro::watt_hours, Digits...>();
1526}
1527
1529template<char... Digits>
1530constexpr electro::kilowatt_hours operator""_kWh() {
1531 return detail::check_overflow<electro::kilowatt_hours, Digits...>();
1532}
1533
1535template<char... Digits>
1536constexpr electro::picofarads operator""_pF() {
1537 return detail::check_overflow<electro::picofarads, Digits...>();
1538}
1539
1541template<char... Digits>
1542constexpr electro::nanofarads operator""_nF() {
1543 return detail::check_overflow<electro::nanofarads, Digits...>();
1544}
1545
1547template<char... Digits>
1548constexpr electro::microfarads operator""_uF() {
1549 return detail::check_overflow<electro::microfarads, Digits...>();
1550}
1551
1553template<char... Digits>
1554constexpr electro::farads operator""_F() {
1555 return detail::check_overflow<electro::farads, Digits...>();
1556}
1557
1559template<char... Digits>
1560constexpr electro::nanohenries operator""_nH() {
1561 return detail::check_overflow<electro::nanohenries, Digits...>();
1562}
1563
1565template<char... Digits>
1566constexpr electro::microhenries operator""_uH() {
1567 return detail::check_overflow<electro::microhenries, Digits...>();
1568}
1569
1571template<char... Digits>
1572constexpr electro::millihenries operator""_mH() {
1573 return detail::check_overflow<electro::millihenries, Digits...>();
1574}
1575
1577template<char... Digits>
1578constexpr electro::henries operator""_H() {
1579 return detail::check_overflow<electro::henries, Digits...>();
1580}
1581
1582} // namespace electro_literals
A quantity of an electrical unit with a representation and precision.
Definition electro.hpp:286
Unit unit
The unit tag type.
Definition electro.hpp:293
constexpr quantity operator--(int)
Definition electro.hpp:373
constexpr quantity & operator*=(const rep &r)
Definition electro.hpp:385
constexpr quantity operator++(int)
Definition electro.hpp:366
constexpr quantity & operator++()
Definition electro.hpp:361
constexpr quantity & operator+=(const quantity &q)
Definition electro.hpp:375
Rep rep
The representation type.
Definition electro.hpp:295
static constexpr quantity zero() noexcept
Returns a zero quantity.
Definition electro.hpp:410
constexpr quantity()=default
Constructs a zero quantity.
constexpr quantity & operator/=(const rep &r)
Definition electro.hpp:390
static constexpr quantity max() noexcept
Returns the maximum representable quantity.
Definition electro.hpp:416
constexpr quantity< Unit, typename std::common_type< rep >::type, precision > operator+() const
Definition electro.hpp:353
constexpr quantity & operator--()
Definition electro.hpp:368
constexpr quantity< Unit, typename std::common_type< rep >::type, precision > operator-() const
Definition electro.hpp:357
quantity & operator=(const quantity &)=default
quantity(const quantity &)=default
~quantity()=default
typename Precision::type precision
The precision as a std::ratio.
Definition electro.hpp:297
constexpr rep count() const
Returns the tick count.
Definition electro.hpp:351
constexpr quantity & operator-=(const quantity &q)
Definition electro.hpp:380
constexpr quantity & operator%=(const rep &r)
Definition electro.hpp:395
static constexpr quantity min() noexcept
Returns the minimum representable quantity.
Definition electro.hpp:413
constexpr quantity(const quantity< Unit, Rep2, Precision2 > &q)
Explicit constructor for lossy precision conversions.
Definition electro.hpp:344
constexpr quantity & operator%=(const quantity &q)
Definition electro.hpp:402
constexpr quantity(const Rep2 &r)
Constructs from a tick count.
Definition electro.hpp:314
capacitance< int64_t, std::pico > picofarads
Capacitance with 1 pF precision.
Definition electro.hpp:1072
resistance< int64_t, std::milli > milliohms
Resistance with 1 mΩ precision.
Definition electro.hpp:1024
inductance< int64_t, std::milli > millihenries
Inductance with 1 mH precision.
Definition electro.hpp:1087
capacitance< int64_t > farads
Capacitance with 1 F precision.
Definition electro.hpp:1080
capacitance< int64_t, std::nano > nanofarads
Capacitance with 1 nF precision.
Definition electro.hpp:1074
energy< int64_t, std::ratio< 3600000 > > kilowatt_hours
Energy with 1 kWh (3,600,000 J) precision.
Definition electro.hpp:1069
power< int64_t, std::mega > megawatts
Power with 1 MW precision.
Definition electro.hpp:1043
inductance< int64_t, std::micro > microhenries
Inductance with 1 µH precision.
Definition electro.hpp:1085
charge< int64_t, std::ratio< 18, 5 > > milliampere_hours
Charge with 1 mAh (3.6 C) precision.
Definition electro.hpp:1054
voltage< int64_t, std::milli > millivolts
Voltage with 1 mV precision.
Definition electro.hpp:1004
energy< int64_t, std::kilo > kilojoules
Energy with 1 kJ precision.
Definition electro.hpp:1063
power< int64_t > watts
Power with 1 W precision.
Definition electro.hpp:1039
energy< int64_t, std::milli > millijoules
Energy with 1 mJ precision.
Definition electro.hpp:1059
voltage< int64_t, std::kilo > kilovolts
Voltage with 1 kV precision.
Definition electro.hpp:1008
capacitance< int64_t, std::micro > microfarads
Capacitance with 1 µF precision.
Definition electro.hpp:1076
current< int64_t, std::milli > milliamperes
Current with 1 mA precision.
Definition electro.hpp:1013
current< int64_t > amperes
Current with 1 A precision.
Definition electro.hpp:1015
resistance< int64_t > ohms
Resistance with 1 Ω precision.
Definition electro.hpp:1026
voltage< int64_t, std::micro > microvolts
Voltage with 1 µV precision.
Definition electro.hpp:1002
inductance< int64_t, std::nano > nanohenries
Inductance with 1 nH precision.
Definition electro.hpp:1083
voltage< int64_t > volts
Voltage with 1 V precision.
Definition electro.hpp:1006
charge< int64_t > coulombs
Charge with 1 C precision.
Definition electro.hpp:1052
power< int64_t, std::kilo > kilowatts
Power with 1 kW precision.
Definition electro.hpp:1041
inductance< int64_t > henries
Inductance with 1 H precision.
Definition electro.hpp:1089
energy< int64_t, std::ratio< 3600 > > watt_hours
Energy with 1 Wh (3600 J) precision.
Definition electro.hpp:1067
current< int64_t, std::micro > microamperes
Current with 1 µA precision.
Definition electro.hpp:1011
power< int64_t, std::micro > microwatts
Power with 1 µW precision.
Definition electro.hpp:1035
resistance< int64_t, std::kilo > kiloohms
Resistance with 1 kΩ precision.
Definition electro.hpp:1028
energy< int64_t > joules
Energy with 1 J precision.
Definition electro.hpp:1061
resistance< int64_t, std::mega > megaohms
Resistance with 1 MΩ precision.
Definition electro.hpp:1030
charge< int64_t, std::ratio< 3600 > > ampere_hours
Charge with 1 Ah (3600 C) precision.
Definition electro.hpp:1056
power< int64_t, std::milli > milliwatts
Power with 1 mW precision.
Definition electro.hpp:1037
User-defined literals for electrical quantity types.
Definition decibel.hpp:752
Electrical unit types and utilities.
Definition decibel.hpp:61
constexpr quantity< Unit, Rep, Precision > abs(const quantity< Unit, Rep, Precision > &q)
Returns the absolute value of a quantity.
Definition electro.hpp:610
constexpr ToQuantity quantity_cast(const quantity< Unit, Rep, Precision > &q)
Converts a quantity to a different precision or representation.
Definition electro.hpp:438
constexpr auto operator+(const level< Reference, Rep1, Precision1 > &l, const quantity< decibel_unit, Rep2, Precision2 > &g)
Applies a gain to a level.
Definition decibel.hpp:353
constexpr auto operator-(const level< Reference, Rep1, Precision1 > &l, const quantity< decibel_unit, Rep2, Precision2 > &g)
Applies a loss to a level.
Definition decibel.hpp:369
constexpr auto operator<=>(const level< Reference, Rep1, Precision1 > &a, const level< Reference, Rep2, Precision2 > &b)
Definition decibel.hpp:394
constexpr ToLevel ceil(const level< Reference, Rep, Precision > &l)
Converts a level to the target type, rounding toward positive infinity.
Definition decibel.hpp:329
constexpr auto operator*(const quantity< Unit, Rep1, Precision > &q, const Rep2 &r) -> quantity< Unit, std::common_type_t< Rep1, Rep2 >, Precision >
Multiplies a quantity by a scalar.
Definition electro.hpp:637
constexpr bool treat_as_inexact_v
Definition electro.hpp:162
constexpr ToLevel round(const level< Reference, Rep, Precision > &l)
Converts a level to the target type, rounding to nearest (ties to even).
Definition decibel.hpp:336
constexpr auto operator/(const quantity< Unit, Rep1, Precision > &q, const Rep2 &s) -> quantity< Unit, std::common_type_t< Rep1, Rep2 >, Precision >
Divides a quantity by a scalar.
Definition electro.hpp:654
constexpr bool is_quantity_v
Definition electro.hpp:105
constexpr ToLevel floor(const level< Reference, Rep, Precision > &l)
Converts a level to the target type, rounding toward negative infinity.
Definition decibel.hpp:322
std::string to_string(const quantity< decibel_unit, Rep, Precision > &g)
Converts a gain to a string, e.g.
Definition decibel.hpp:657
constexpr bool operator==(const level< Reference, Rep1, Precision1 > &a, const level< Reference, Rep2, Precision2 > &b)
Definition decibel.hpp:388
Electric current, measured in amperes.
Definition electro.hpp:220
static constexpr const char * symbol
Definition electro.hpp:221
Electric charge, measured in coulombs.
Definition electro.hpp:235
static constexpr const char * symbol
Definition electro.hpp:236
Capacitance, measured in farads.
Definition electro.hpp:245
static constexpr const char * symbol
Definition electro.hpp:246
Inductance, measured in henries.
Definition electro.hpp:250
static constexpr const char * symbol
Definition electro.hpp:251
Trait to detect quantity specializations.
Definition electro.hpp:99
Energy, measured in joules.
Definition electro.hpp:240
static constexpr const char * symbol
Definition electro.hpp:241
Electrical resistance, measured in ohms.
Definition electro.hpp:225
static constexpr const char * symbol
Definition electro.hpp:226
Trait giving the display suffix for a (unit, precision) pair.
Definition electro.hpp:1170
static std::string value()
Returns the display suffix.
Definition electro.hpp:1172
Provides special values for quantity representations.
Definition electro.hpp:133
static constexpr Rep max() noexcept
Returns the maximum representable quantity.
Definition electro.hpp:138
static constexpr Rep min() noexcept
Returns the minimum representable quantity.
Definition electro.hpp:141
static constexpr Rep zero() noexcept
Returns a zero quantity.
Definition electro.hpp:135
Trait giving the display symbol for an SI precision prefix.
Definition electro.hpp:1104
Trait to indicate floating-point-like behavior.
Definition electro.hpp:159
Voltage, measured in volts.
Definition electro.hpp:215
static constexpr const char * symbol
Definition electro.hpp:216
Power, measured in watts.
Definition electro.hpp:230
static constexpr const char * symbol
Definition electro.hpp:231