diff --git a/doc/DESIGN.md b/doc/DESIGN.md index 5c11c2a1..d3b38465 100644 --- a/doc/DESIGN.md +++ b/doc/DESIGN.md @@ -50,21 +50,11 @@ point of view the most important is a quantity. Quantity is a concrete amount of a unit for a specified dimension with a specific representation: ```cpp -units::quantity d1(123); -auto d2 = 123_km; // units::quantity +units::quantity d1(123); +auto d2 = 123_km; // units::quantity ``` -There are helper aliases provided to improve the work with quantities: - -``` -template -using length = quantity; - -units::length d1(123); // units::length -units::length d2(3.14); // units::length -``` - -Also there are C++ concepts provided for each such quantity type: +There are C++ concepts provided for each such quantity type: ```cpp template @@ -241,8 +231,7 @@ concept Unit = expressed in a specific unit of that dimension: ```cpp -template - requires std::Same +template class quantity; ``` @@ -259,29 +248,36 @@ concept Quantity = member types and functions as below: ```cpp -template - requires std::Same +template class quantity { public: - using dimension = D; using unit = U; + using rep = Rep; + using dimension = U::dimension; - template - requires treat_as_floating_point> || std::ratio_multiply::den == 1 - quantity, downcasting_traits_t, std::ratio_multiply>>, std::common_type_t> - constexpr operator*(const quantity& lhs, - const quantity& rhs); + [[nodiscard]] static constexpr quantity one() noexcept { return quantity(quantity_values::one()); } - template - quantity, downcasting_traits_t, std::ratio>>, std::common_type_t> - constexpr operator/(const Rep1& v, - const quantity& q) [[expects: q != quantity(0)]]; + template + requires treat_as_floating_point || + (std::ratio_multiply::den == 1) + [[nodiscard]] constexpr Quantity operator*(const quantity& lhs, + const quantity& rhs); - template - requires treat_as_floating_point> || std::ratio_divide::den == 1 - quantity, downcasting_traits_t, std::ratio_divide>>, std::common_type_t> - constexpr operator/(const quantity& lhs, - const quantity& rhs) [[expects: rhs != quantity(0)]]; + template + [[nodiscard]] constexpr Quantity operator/(const Rep1& v, + const quantity& q) + + template + requires std::Same + [[nodiscard]] constexpr Scalar operator/(const quantity& lhs, + const quantity& rhs); + + template + requires (!std::Same) && + (treat_as_floating_point || + (ratio_divide::den == 1)) + [[nodiscard]] constexpr Quantity operator/(const quantity& lhs, + const quantity& rhs); }; ``` @@ -308,35 +304,35 @@ the best user experience as possible. For example with template aliases usage the following code: ```cpp -const Velocity t = 20_s; +const Velocity auto t = 20_s; ``` could generate a following compile time error: ```text -C:\repos\units\example\example.cpp:39:22: error: deduced initializer does not satisfy placeholder constraints - const Velocity t = 20_s; - ^~~~ -In file included from C:\repos\units\example\example.cpp:23: -C:/repos/units/src/include/units/si/velocity.h:41:16: note: within 'template concept const bool units::Velocity [with T = units::quantity >, units::unit >, std::ratio<1> >, long long int>]' +\example\example.cpp:39:22: error: deduced initializer does not satisfy placeholder constraints + const Velocity auto t = 20_s; + ^~~~ +In file included from \example\example.cpp:23: +/src/include/units/si/velocity.h:41:16: note: within 'template concept const bool units::Velocity [with T = units::quantity >, std::ratio<1> >, long long int>]' concept Velocity = Quantity && std::Same; ^~~~~~~~ -In file included from C:/repos/units/src/include/units/bits/tools.h:25, - from C:/repos/units/src/include/units/dimension.h:25, - from C:/repos/units/src/include/units/si/base_dimensions.h:25, - from C:/repos/units/src/include/units/si/velocity.h:25, - from C:\repos\units\example\example.cpp:23: -C:/repos/units/src/include/units/bits/stdconcepts.h:33:18: note: within 'template concept const bool std::Same [with T = units::dimension >; U = units::dimension, units::exp >]' +In file included from /src/include/units/bits/tools.h:25, + from /src/include/units/dimension.h:25, + from /src/include/units/si/base_dimensions.h:25, + from /src/include/units/si/velocity.h:25, + from \example\example.cpp:23: +/src/include/units/bits/stdconcepts.h:33:18: note: within 'template concept const bool std::Same [with T = units::dimension >; U = units::dimension, units::exp >]' concept Same = std::is_same_v; ^~~~ -C:/repos/units/src/include/units/bits/stdconcepts.h:33:18: note: 'std::is_same_v' evaluated to false +/src/include/units/bits/stdconcepts.h:33:18: note: 'std::is_same_v' evaluated to false ``` Time and velocity are not that complicated dimensions and there are much more complicated dimensions out there, but even for those dimensions ```text -[with T = units::quantity >, units::unit >, std::ratio<1> >, long long int>] +[with T = units::quantity >, std::ratio<1> >, long long int>] ``` and @@ -351,28 +347,28 @@ That is why it was decided to provide automated downcasting capability when poss same code will result with such an error: ```text -C:\repos\units\example\example.cpp:40:22: error: deduced initializer does not satisfy placeholder constraints +\example\example.cpp:40:22: error: deduced initializer does not satisfy placeholder constraints const Velocity t = 20_s; ^~~~ -In file included from C:\repos\units\example\example.cpp:23: -C:/repos/units/src/include/units/si/velocity.h:48:16: note: within 'template concept const bool units::Velocity [with T = units::quantity]' +In file included from \example\example.cpp:23: +/src/include/units/si/velocity.h:48:16: note: within 'template concept const bool units::Velocity [with T = units::quantity]' concept Velocity = Quantity && std::Same; ^~~~~~~~ -In file included from C:/repos/units/src/include/units/bits/tools.h:25, - from C:/repos/units/src/include/units/dimension.h:25, - from C:/repos/units/src/include/units/si/base_dimensions.h:25, - from C:/repos/units/src/include/units/si/velocity.h:25, - from C:\repos\units\example\example.cpp:23: -C:/repos/units/src/include/units/bits/stdconcepts.h:33:18: note: within 'template concept const bool std::Same [with T = units::dimension_time; U = units::dimension_velocity]' +In file included from /src/include/units/bits/tools.h:25, + from /src/include/units/dimension.h:25, + from /src/include/units/si/base_dimensions.h:25, + from /src/include/units/si/velocity.h:25, + from \example\example.cpp:23: +/src/include/units/bits/stdconcepts.h:33:18: note: within 'template concept const bool std::Same [with T = units::dimension_time; U = units::dimension_velocity]' concept Same = std::is_same_v; ^~~~ -C:/repos/units/src/include/units/bits/stdconcepts.h:33:18: note: 'std::is_same_v' evaluated to false +/src/include/units/bits/stdconcepts.h:33:18: note: 'std::is_same_v' evaluated to false ``` Now ```text -[with T = units::quantity] +[with T = units::quantity] ``` and @@ -383,20 +379,32 @@ and are not arguably much easier to understand thus provide better user experience. -downcasting capability is provided through dedicated `downcasting_traits`, a few helper aliases and by +Downcasting capability is provided through dedicated `downcasting_traits`, concept, a few helper aliases and by `base_type` member type in `downcast_base` class template. ```cpp +template +struct downcast_base { + using base_type = BaseType; +}; + +template +concept bool Downcastable = + requires { + typename T::base_type; + } && + std::DerivedFrom>; + template using downcast_from = T::base_type; -template +template using downcast_to = std::type_identity; -template +template struct downcasting_traits : downcast_to {}; -template +template using downcasting_traits_t = downcasting_traits::type; ``` @@ -424,21 +432,14 @@ struct dimension_velocity : make_dimension_t, exp struct downcasting_traits> : downcast_to {}; ``` -2. Provide `quantity` class template partial specialization for new dimension and provide its base type: - -```cpp -template -using velocity = quantity; -``` - -3. Define a concept that will match a new dimension: +2. Define a concept that will match a new dimension: ```cpp template concept Velocity = Quantity && std::Same; ``` -4. Define units and provide downcasting traits for them: +3. Define units and provide downcasting traits for them: - base unit @@ -465,11 +466,11 @@ template<> struct downcasting_traits> : downca ```cpp inline namespace literals { - constexpr auto operator""_mps(unsigned long long l) { return velocity(l); } - constexpr auto operator""_mps(long double l) { return velocity(l); } + constexpr auto operator""_mps(unsigned long long l) { return quantity(l); } + constexpr auto operator""_mps(long double l) { return quantity(l); } - constexpr auto operator""_kmph(unsigned long long l) { return velocity(l); } - constexpr auto operator""_kmph(long double l) { return velocity(l); } + constexpr auto operator""_kmph(unsigned long long l) { return quantity(l); } + constexpr auto operator""_kmph(long double l) { return quantity(l); } } ``` @@ -507,101 +508,39 @@ Additionally, it should make the error logs even shorter thus easier to understa 1. Should we ensure that dimension is always a result of `make_dimension`? How to do it? -2. Should we provide strong types and downcasting_traits for `quantity` type? +2. What to do with `time` which is ambiguous (conflict wit ANSI C)? - In such a case all the operators have to be provided to a child class. Or maybe use CRTP? +3. What to do with `std::chrono::duration`? -3. What to do with `time` which is ambiguous (conflict wit ANSI C)? +4. Should we provide `seconds` or stay with `quantity`? -4. What to do with `std::chrono::duration`? Is it possible to make it derive from - `quantity` which will most probably an ABI break? Alternatively, - should we provide specialization of `quantity` to work with/covnert - from/to `std::duration`? +5. What is the best way to add support for temperatures? -5. Should we provide `seconds` or stay with `time`? What about CTAD problem - for `units::length d3(3);`? + Temperature absolute values not only require `std::ratio` but also should be adjusted/shifted + by some constant values (i.e. [°C] = [K] − 273.15). Relative temperatures does need an offset. + Users will most probably have problems with differentiating those two. Maybe the best solution + is to provide only `K` support in quantity and provide non-member helper conversion functions + with verbose names to convert to `°C` and `°C`? -6. What is the best way to add support for temperatures? +6. Do we need non-linear scale? - Temperatures not only require `std::ratio` but also should be adjusted/shifted by some - constant values (i.e. [°C] = [K] − 273.15). +7. Should we provide cmath-like functions for quantities? -7. Should we use `units::multiply` or stay with `std::ratio` for multiplication? +8. What should be the resulting type of `auto d = 1_km + 1_ft;`? -8. Should we consider making `units::multiply` and `units::offset` a non-class template parameters - as they provide different ratio values rather than types? - - In example instead: - - ```cpp - struct celsius : unit>> {}; - ``` +9. Should we require explicit casts (i.e. quantity_cast) between different systems of + measurement? - we could think about something like: - - ```cpp - struct celsius : unit>> {}; - ``` +10. Should we support integral representations? -9. Do we need non-linear scale? +11. Provide ostream overloads to print quantity units (use `std::format`)? -10. Should we provide cmath-like functions for quantities? - -11. What should be the resulting type of `auto d = 1_km + 1_ft;`? - -12. Should we require explicit casts (i.e. quantity_cast) between different systems of - measurement? - -13. Should we provide Boost-like support for a `quantity_cast` to a reference that allows - direct access to the underlying value of a quantity variable? - -14. What should be the default representation (integral or `double`)? - -15. Provide ostream overloads to print quantity units (use `std::format`)? - -16. Should we provide support for dimensionless quantities? +12. Should we provide support for dimensionless quantities? Because dimensionless quantities have no associated units, they behave as normal scalars, and allow implicit conversion to and from the underlying value type or types that are convertible to/from that value type. -17. Should we leave `quantity` and specific dimensions as - ```cpp - template - requires std::Same - class quantity; - - template - using velocity = quantity; - - units::velocity kmph = avg_speed(d, t); - ``` - or maybe we should leave the dimension only in unit - ```cpp - template - class quantity; - - units::quantity kmph = avg_speed(d, t); - ``` - which will simplify the design and shorten compile time errors but possibly will add - more ambiguity to some cases. For example when using CTAD: - ```cpp - units::velocity kmph = avg_speed(d, t); - ``` - vs - ```cpp - units::quantity kmph = avg_speed(d, t); - ``` - It would be also incopatible with concepts named i.e. `Velocity`. - -18. Should we standardize accompany tools (`type_list` operations, `static_sign`, `static_abs`, - `static_gcd`, `common_ratio`)? +13. Should we standardize accompany tools (`downcasting_traits`, `type_list` operations, `common_ratio`, etc)? -19. Do we need to support fractional exponents (i.e. `dimension>` as 2/3)? - -20. implicit conversion of quantity to quantity is allowed if Y and Z are implicitly convertible. - assignment between quantity and quantity is allowed if Y and Z are implicitly convertible. - -21. explicit conversion between quantity and quantity is allowed if Unit1 and Unit2 have the same dimensions and if Y and Z are implicitly convertible. - implicit conversion between quantity and quantity is allowed if Unit1 reduces to exactly the same combination of base units as Unit2 and if Y and Z are convertible. - +14. Do we need to support fractional exponents (i.e. `dimension>` as 2/3)? \ No newline at end of file diff --git a/example/example.cpp b/example/example.cpp index 6e89dda0..633b287a 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -38,13 +38,13 @@ void example_1(V v, T t) { const units::Length distance = v * t; std::cout << "A car driving " << v.count() << " km/h in a time of " << t.count() << " minutes will pass " - << units::quantity_cast>(distance).count() << " meters.\n"; + << units::quantity_cast>(distance).count() << " meters.\n"; } void example_2(double distance_v, double duration_v) { - units::length distance(distance_v); - units::time duration(duration_v); + units::quantity distance(distance_v); + units::quantity duration(duration_v); const auto kmph = avg_speed(distance, duration); std::cout << "Average speed of a car that makes " << distance.count() << " km in " << duration.count() << " hours is " << kmph.count() << " km/h.\n"; diff --git a/src/include/units/area.h b/src/include/units/area.h index 791eea8b..9e8abbef 100644 --- a/src/include/units/area.h +++ b/src/include/units/area.h @@ -32,9 +32,6 @@ namespace units { template concept bool Area = Quantity && std::Same; - template - using area = quantity; - struct square_millimeter : derived_unit {}; template<> struct downcasting_traits> : downcast_to {}; @@ -53,20 +50,20 @@ namespace units { inline namespace literals { // sq_mm - constexpr auto operator""_sq_mm(unsigned long long l) { return area(l); } - constexpr auto operator""_sq_mm(long double l) { return area(l); } + constexpr auto operator""_sq_mm(unsigned long long l) { return quantity(l); } + constexpr auto operator""_sq_mm(long double l) { return quantity(l); } // sq_cm - constexpr auto operator""_sq_cm(unsigned long long l) { return area(l); } - constexpr auto operator""_sq_cm(long double l) { return area(l); } + constexpr auto operator""_sq_cm(unsigned long long l) { return quantity(l); } + constexpr auto operator""_sq_cm(long double l) { return quantity(l); } // sq_m - constexpr auto operator""_sq_m(unsigned long long l) { return area(l); } - constexpr auto operator""_sq_m(long double l) { return area(l); } + constexpr auto operator""_sq_m(unsigned long long l) { return quantity(l); } + constexpr auto operator""_sq_m(long double l) { return quantity(l); } // sq_km - constexpr auto operator""_sq_km(unsigned long long l) { return area(l); } - constexpr auto operator""_sq_km(long double l) { return area(l); } + constexpr auto operator""_sq_km(unsigned long long l) { return quantity(l); } + constexpr auto operator""_sq_km(long double l) { return quantity(l); } } // namespace literals diff --git a/src/include/units/bits/hacks.h b/src/include/units/bits/hacks.h index 49dba893..88fdc49b 100644 --- a/src/include/units/bits/hacks.h +++ b/src/include/units/bits/hacks.h @@ -37,6 +37,7 @@ namespace std { using type_identity_t = typename type_identity::type; #endif // UNITS_HAS_STD_TYPE_IDENTITY + // concepts using experimental::ranges::Same; using experimental::ranges::Integral; @@ -45,4 +46,4 @@ namespace std { using experimental::ranges::StrictTotallyOrdered; using experimental::ranges::ConvertibleTo; -} \ No newline at end of file +} diff --git a/src/include/units/current.h b/src/include/units/current.h index 69e070c3..6011a1ef 100644 --- a/src/include/units/current.h +++ b/src/include/units/current.h @@ -33,17 +33,14 @@ namespace units { template concept bool Current = Quantity && std::Same; - template - using current = quantity; - struct ampere : unit {}; template<> struct downcasting_traits> : downcast_to {}; inline namespace literals { // A - constexpr auto operator""_A(unsigned long long l) { return current(l); } - constexpr auto operator""_A(long double l) { return current(l); } + constexpr auto operator""_A(unsigned long long l) { return quantity(l); } + constexpr auto operator""_A(long double l) { return quantity(l); } } diff --git a/src/include/units/frequency.h b/src/include/units/frequency.h index 6b262696..d4625a92 100644 --- a/src/include/units/frequency.h +++ b/src/include/units/frequency.h @@ -33,9 +33,6 @@ namespace units { template concept bool Frequency = Quantity && std::Same; - template - using frequency = quantity; - struct hertz : derived_unit {}; template<> struct downcasting_traits> : downcast_to {}; @@ -57,28 +54,28 @@ namespace units { inline namespace literals { // mHz - constexpr auto operator""_mHz(unsigned long long l) { return frequency(l); } - constexpr auto operator""_mHz(long double l) { return frequency(l); } + constexpr auto operator""_mHz(unsigned long long l) { return quantity(l); } + constexpr auto operator""_mHz(long double l) { return quantity(l); } // Hz - constexpr auto operator""_Hz(unsigned long long l) { return frequency(l); } - constexpr auto operator""_Hz(long double l) { return frequency(l); } + constexpr auto operator""_Hz(unsigned long long l) { return quantity(l); } + constexpr auto operator""_Hz(long double l) { return quantity(l); } // kHz - constexpr auto operator""_kHz(unsigned long long l) { return frequency(l); } - constexpr auto operator""_kHz(long double l) { return frequency(l); } + constexpr auto operator""_kHz(unsigned long long l) { return quantity(l); } + constexpr auto operator""_kHz(long double l) { return quantity(l); } // MHz - constexpr auto operator""_MHz(unsigned long long l) { return frequency(l); } - constexpr auto operator""_MHz(long double l) { return frequency(l); } + constexpr auto operator""_MHz(unsigned long long l) { return quantity(l); } + constexpr auto operator""_MHz(long double l) { return quantity(l); } // GHz - constexpr auto operator""_GHz(unsigned long long l) { return frequency(l); } - constexpr auto operator""_GHz(long double l) { return frequency(l); } + constexpr auto operator""_GHz(unsigned long long l) { return quantity(l); } + constexpr auto operator""_GHz(long double l) { return quantity(l); } // THz - constexpr auto operator""_THz(unsigned long long l) { return frequency(l); } - constexpr auto operator""_THz(long double l) { return frequency(l); } + constexpr auto operator""_THz(unsigned long long l) { return quantity(l); } + constexpr auto operator""_THz(long double l) { return quantity(l); } } // namespace literals diff --git a/src/include/units/length.h b/src/include/units/length.h index 9e366c67..fa43b527 100644 --- a/src/include/units/length.h +++ b/src/include/units/length.h @@ -33,9 +33,6 @@ namespace units { template concept bool Length = Quantity && std::Same; - template - using length = quantity; - // SI units struct meter : unit {}; template<> struct downcasting_traits> : downcast_to {}; @@ -52,20 +49,20 @@ namespace units { inline namespace literals { // mm - constexpr auto operator""_mm(unsigned long long l) { return length(l); } - constexpr auto operator""_mm(long double l) { return length(l); } + constexpr auto operator""_mm(unsigned long long l) { return quantity(l); } + constexpr auto operator""_mm(long double l) { return quantity(l); } // cm - constexpr auto operator""_cm(unsigned long long l) { return length(l); } - constexpr auto operator""_cm(long double l) { return length(l); } + constexpr auto operator""_cm(unsigned long long l) { return quantity(l); } + constexpr auto operator""_cm(long double l) { return quantity(l); } // m - constexpr auto operator""_m(unsigned long long l) { return length(l); } - constexpr auto operator""_m(long double l) { return length(l); } + constexpr auto operator""_m(unsigned long long l) { return quantity(l); } + constexpr auto operator""_m(long double l) { return quantity(l); } // km - constexpr auto operator""_km(unsigned long long l) { return length(l); } - constexpr auto operator""_km(long double l) { return length(l); } + constexpr auto operator""_km(unsigned long long l) { return quantity(l); } + constexpr auto operator""_km(long double l) { return quantity(l); } } // namespace literals @@ -85,20 +82,20 @@ namespace units { inline namespace literals { // yd - constexpr auto operator""_yd(unsigned long long l) { return length(l); } - constexpr auto operator""_yd(long double l) { return length(l); } + constexpr auto operator""_yd(unsigned long long l) { return quantity(l); } + constexpr auto operator""_yd(long double l) { return quantity(l); } // ft - constexpr auto operator""_ft(unsigned long long l) { return length(l); } - constexpr auto operator""_ft(long double l) { return length(l); } + constexpr auto operator""_ft(unsigned long long l) { return quantity(l); } + constexpr auto operator""_ft(long double l) { return quantity(l); } // in - constexpr auto operator""_in(unsigned long long l) { return length(l); } - constexpr auto operator""_in(long double l) { return length(l); } + constexpr auto operator""_in(unsigned long long l) { return quantity(l); } + constexpr auto operator""_in(long double l) { return quantity(l); } // mi - constexpr auto operator""_mi(unsigned long long l) { return length(l); } - constexpr auto operator""_mi(long double l) { return length(l); } + constexpr auto operator""_mi(unsigned long long l) { return quantity(l); } + constexpr auto operator""_mi(long double l) { return quantity(l); } } // namespace literals diff --git a/src/include/units/luminous_intensity.h b/src/include/units/luminous_intensity.h index 518ef760..338d634b 100644 --- a/src/include/units/luminous_intensity.h +++ b/src/include/units/luminous_intensity.h @@ -33,17 +33,14 @@ namespace units { template concept bool LuminousIntensity = Quantity && std::Same; - template - using luminous_intensity = quantity; - struct candela : unit {}; template<> struct downcasting_traits> : downcast_to {}; inline namespace literals { // cd - constexpr auto operator""_cd(unsigned long long l) { return luminous_intensity(l); } - constexpr auto operator""_cd(long double l) { return luminous_intensity(l); } + constexpr auto operator""_cd(unsigned long long l) { return quantity(l); } + constexpr auto operator""_cd(long double l) { return quantity(l); } } // namespace literals diff --git a/src/include/units/mass.h b/src/include/units/mass.h index c7e04493..b0515980 100644 --- a/src/include/units/mass.h +++ b/src/include/units/mass.h @@ -33,9 +33,6 @@ namespace units { template concept bool Mass = Quantity && std::Same; - template - using mass = quantity; - struct gram : unit> {}; template<> struct downcasting_traits> : downcast_to {}; @@ -45,12 +42,12 @@ namespace units { inline namespace literals { // g - constexpr auto operator""_g(unsigned long long l) { return mass(l); } - constexpr auto operator""_g(long double l) { return mass(l); } + constexpr auto operator""_g(unsigned long long l) { return quantity(l); } + constexpr auto operator""_g(long double l) { return quantity(l); } // kg - constexpr auto operator""_kg(unsigned long long l) { return mass(l); } - constexpr auto operator""_kg(long double l) { return mass(l); } + constexpr auto operator""_kg(unsigned long long l) { return quantity(l); } + constexpr auto operator""_kg(long double l) { return quantity(l); } } // namespace literals diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index d79ec59c..4e0d4ba2 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -45,14 +45,13 @@ namespace units { template concept bool Scalar = Number && !Quantity; - template - requires std::Same + template class quantity; namespace detail { - template - inline constexpr bool is_quantity> = true; + template + inline constexpr bool is_quantity> = true; } // namespace detail @@ -60,14 +59,15 @@ namespace units { template struct common_quantity; - template - struct common_quantity, quantity, Rep> { - using type = quantity; + template + struct common_quantity, quantity, Rep> { + using type = quantity; }; - template - struct common_quantity, quantity, Rep> { - using type = quantity>>, Rep>; + template + requires std::Same + struct common_quantity, quantity, Rep> { + using type = quantity>>, Rep>; }; template> @@ -121,9 +121,9 @@ namespace units { } // namespace detail - template - requires std::Same - constexpr To quantity_cast(const quantity& q) + template + requires std::Same + constexpr To quantity_cast(const quantity& q) { using c_ratio = ratio_divide; using c_rep = std::common_type_t; @@ -143,15 +143,14 @@ namespace units { // quantity - template - requires std::Same + template class quantity { Rep value_; public: - using dimension = D; using unit = U; using rep = Rep; + using dimension = U::dimension; static_assert(!Quantity, "rep cannot be a quantity"); @@ -238,163 +237,173 @@ namespace units { }; // clang-format off - template - [[nodiscard]] constexpr Quantity operator+(const quantity& lhs, - const quantity& rhs) + template + [[nodiscard]] constexpr Quantity operator+(const quantity& lhs, + const quantity& rhs) + requires std::Same { using common_rep = decltype(lhs.count() + rhs.count()); - using ret = common_quantity_t, quantity, common_rep>; + using ret = common_quantity_t, quantity, common_rep>; return ret(ret(lhs).count() + ret(rhs).count()); } - template - [[nodiscard]] constexpr Quantity operator-(const quantity& lhs, - const quantity& rhs) + template + [[nodiscard]] constexpr Quantity operator-(const quantity& lhs, + const quantity& rhs) + requires std::Same { using common_rep = decltype(lhs.count() - rhs.count()); - using ret = common_quantity_t, quantity, common_rep>; + using ret = common_quantity_t, quantity, common_rep>; return ret(ret(lhs).count() - ret(rhs).count()); } -// template - template - [[nodiscard]] constexpr Quantity operator*(const quantity& q, +// template + template + [[nodiscard]] constexpr Quantity operator*(const quantity& q, const Rep2& v) requires (!Quantity) { using common_rep = decltype(q.count()* v); - using ret = quantity; + using ret = quantity; return ret(ret(q).count() * v); } - //template - template + //template + template [[nodiscard]] constexpr Quantity operator*(const Rep1& v, - const quantity& q) + const quantity& q) requires (!Quantity) { return q * v; } - template - [[nodiscard]] constexpr Quantity operator*(const quantity& lhs, - const quantity& rhs) + template + [[nodiscard]] constexpr Quantity operator*(const quantity& lhs, + const quantity& rhs) requires treat_as_floating_point || (std::ratio_multiply::den == 1) { - using dim = dimension_multiply_t; + using dim = dimension_multiply_t; using common_rep = decltype(lhs.count() * rhs.count()); - using ret = quantity>>, common_rep>; + using ret = quantity>>, common_rep>; return ret(lhs.count() * rhs.count()); } -// template - template +// template + template [[nodiscard]] constexpr Quantity operator/(const Rep1& v, - const quantity& q) + const quantity& q) requires (!Quantity) { Expects(q != std::remove_cvref_t(0)); - using dim = dim_invert_t; + using dim = dim_invert_t; using common_rep = decltype(v / q.count()); - using ret = quantity>>, common_rep>; - using den = quantity; + using ret = quantity>>, common_rep>; + using den = quantity; return ret(v / den(q).count()); } -// template - template - [[nodiscard]] constexpr Quantity operator/(const quantity& q, +// template + template + [[nodiscard]] constexpr Quantity operator/(const quantity& q, const Rep2& v) requires (!Quantity) { Expects(v != Rep2{0}); using common_rep = decltype(q.count() / v); - using ret = quantity; + using ret = quantity; return ret(ret(q).count() / v); } - template - [[nodiscard]] constexpr Scalar operator/(const quantity& lhs, - const quantity& rhs) + template + [[nodiscard]] constexpr Scalar operator/(const quantity& lhs, + const quantity& rhs) + requires std::Same { Expects(rhs != std::remove_cvref_t(0)); using common_rep = decltype(lhs.count() / rhs.count()); - using cq = common_quantity_t, quantity, common_rep>; + using cq = common_quantity_t, quantity, common_rep>; return cq(lhs).count() / cq(rhs).count(); } - template - [[nodiscard]] constexpr Quantity operator/(const quantity& lhs, - const quantity& rhs) - requires treat_as_floating_point || - (ratio_divide::den == 1) + template + [[nodiscard]] constexpr Quantity operator/(const quantity& lhs, + const quantity& rhs) + requires (!std::Same) && + (treat_as_floating_point || + (ratio_divide::den == 1)) { Expects(rhs != std::remove_cvref_t(0)); using common_rep = decltype(lhs.count() / rhs.count()); - using dim = dimension_divide_t; - using ret = quantity>>, common_rep>; + using dim = dimension_divide_t; + using ret = quantity>>, common_rep>; return ret(lhs.count() / rhs.count()); } - template - [[nodiscard]] constexpr Quantity operator%(const quantity& q, + template + [[nodiscard]] constexpr Quantity operator%(const quantity& q, const Rep2& v) { using common_rep = decltype(q.count() % v); - using ret = quantity; + using ret = quantity; return ret(ret(q).count() % v); } - template - [[nodiscard]] constexpr Quantity operator%(const quantity& lhs, - const quantity& rhs) + template + [[nodiscard]] constexpr Quantity operator%(const quantity& lhs, + const quantity& rhs) { using common_rep = decltype(lhs.count() % rhs.count()); - using ret = common_quantity_t, quantity, common_rep>; + using ret = common_quantity_t, quantity, common_rep>; return ret(ret(lhs).count() % ret(rhs).count()); } // clang-format on - template - [[nodiscard]] constexpr bool operator==(const quantity& lhs, const quantity& rhs) + template + [[nodiscard]] constexpr bool operator==(const quantity& lhs, const quantity& rhs) + requires std::Same { - using ct = common_quantity_t, quantity>; + using ct = common_quantity_t, quantity>; return ct(lhs).count() == ct(rhs).count(); } - template - [[nodiscard]] constexpr bool operator!=(const quantity& lhs, const quantity& rhs) + template + [[nodiscard]] constexpr bool operator!=(const quantity& lhs, const quantity& rhs) + requires std::Same { return !(lhs == rhs); } - template - [[nodiscard]] constexpr bool operator<(const quantity& lhs, const quantity& rhs) + template + [[nodiscard]] constexpr bool operator<(const quantity& lhs, const quantity& rhs) + requires std::Same { - using ct = common_quantity_t, quantity>; + using ct = common_quantity_t, quantity>; return ct(lhs).count() < ct(rhs).count(); } - template - [[nodiscard]] constexpr bool operator<=(const quantity& lhs, const quantity& rhs) + template + [[nodiscard]] constexpr bool operator<=(const quantity& lhs, const quantity& rhs) + requires std::Same { return !(rhs < lhs); } - template - [[nodiscard]] constexpr bool operator>(const quantity& lhs, const quantity& rhs) + template + [[nodiscard]] constexpr bool operator>(const quantity& lhs, const quantity& rhs) + requires std::Same { return rhs < lhs; } - template - [[nodiscard]] constexpr bool operator>=(const quantity& lhs, const quantity& rhs) + template + [[nodiscard]] constexpr bool operator>=(const quantity& lhs, const quantity& rhs) + requires std::Same { return !(lhs < rhs); } diff --git a/src/include/units/substance.h b/src/include/units/substance.h index 0239116e..9a44590c 100644 --- a/src/include/units/substance.h +++ b/src/include/units/substance.h @@ -33,17 +33,14 @@ namespace units { template concept bool Substance = Quantity && std::Same; - template - using substance = quantity; - struct mole : unit {}; template<> struct downcasting_traits> : downcast_to {}; inline namespace literals { // mol - constexpr auto operator""_mol(unsigned long long l) { return substance(l); } - constexpr auto operator""_mol(long double l) { return substance(l); } + constexpr auto operator""_mol(unsigned long long l) { return quantity(l); } + constexpr auto operator""_mol(long double l) { return quantity(l); } } // namespace literals diff --git a/src/include/units/temperature.h b/src/include/units/temperature.h index 0942525d..3f69403e 100644 --- a/src/include/units/temperature.h +++ b/src/include/units/temperature.h @@ -33,17 +33,14 @@ namespace units { template concept bool ThermodynamicTemperature = Quantity && std::Same; - template - using temperature = quantity; - struct kelvin : unit {}; template<> struct downcasting_traits> : downcast_to {}; inline namespace literals { // K - constexpr auto operator""_K(unsigned long long l) { return temperature(l); } - constexpr auto operator""_K(long double l) { return temperature(l); } + constexpr auto operator""_K(unsigned long long l) { return quantity(l); } + constexpr auto operator""_K(long double l) { return quantity(l); } } // namespace literals diff --git a/src/include/units/time.h b/src/include/units/time.h index 62245a03..950730be 100644 --- a/src/include/units/time.h +++ b/src/include/units/time.h @@ -33,9 +33,6 @@ namespace units { template concept bool Time = Quantity && std::Same; - template - using time = quantity; - struct second : unit {}; template<> struct downcasting_traits> : downcast_to {}; @@ -57,28 +54,28 @@ namespace units { inline namespace literals { // ns - constexpr auto operator""_ns(unsigned long long l) { return time(l); } - constexpr auto operator""_ns(long double l) { return time(l); } + constexpr auto operator""_ns(unsigned long long l) { return quantity(l); } + constexpr auto operator""_ns(long double l) { return quantity(l); } // us - constexpr auto operator""_us(unsigned long long l) { return time(l); } - constexpr auto operator""_us(long double l) { return time(l); } + constexpr auto operator""_us(unsigned long long l) { return quantity(l); } + constexpr auto operator""_us(long double l) { return quantity(l); } // ms - constexpr auto operator""_ms(unsigned long long l) { return time(l); } - constexpr auto operator""_ms(long double l) { return time(l); } + constexpr auto operator""_ms(unsigned long long l) { return quantity(l); } + constexpr auto operator""_ms(long double l) { return quantity(l); } // s - constexpr auto operator""_s(unsigned long long l) { return time(l); } - constexpr auto operator""_s(long double l) { return time(l); } + constexpr auto operator""_s(unsigned long long l) { return quantity(l); } + constexpr auto operator""_s(long double l) { return quantity(l); } // min - constexpr auto operator""_min(unsigned long long l) { return time(l); } - constexpr auto operator""_min(long double l) { return time(l); } + constexpr auto operator""_min(unsigned long long l) { return quantity(l); } + constexpr auto operator""_min(long double l) { return quantity(l); } // h - constexpr auto operator""_h(unsigned long long l) { return time(l); } - constexpr auto operator""_h(long double l) { return time(l); } + constexpr auto operator""_h(unsigned long long l) { return quantity(l); } + constexpr auto operator""_h(long double l) { return quantity(l); } } // namespace literals diff --git a/src/include/units/velocity.h b/src/include/units/velocity.h index 72e9898d..69b7b5f0 100644 --- a/src/include/units/velocity.h +++ b/src/include/units/velocity.h @@ -33,9 +33,6 @@ namespace units { template concept bool Velocity = Quantity && std::Same; - template - using velocity = quantity; - struct meter_per_second : derived_unit {}; template<> struct downcasting_traits> : downcast_to {}; @@ -48,16 +45,16 @@ namespace units { inline namespace literals { // mps - constexpr auto operator""_mps(unsigned long long l) { return velocity(l); } - constexpr auto operator""_mps(long double l) { return velocity(l); } + constexpr auto operator""_mps(unsigned long long l) { return quantity(l); } + constexpr auto operator""_mps(long double l) { return quantity(l); } // kmph - constexpr auto operator""_kmph(unsigned long long l) { return velocity(l); } - constexpr auto operator""_kmph(long double l) { return velocity(l); } + constexpr auto operator""_kmph(unsigned long long l) { return quantity(l); } + constexpr auto operator""_kmph(long double l) { return quantity(l); } // mph - constexpr auto operator""_mph(unsigned long long l) { return velocity(l); } - constexpr auto operator""_mph(long double l) { return velocity(l); } + constexpr auto operator""_mph(unsigned long long l) { return quantity(l); } + constexpr auto operator""_mph(long double l) { return quantity(l); } } // namespace literals diff --git a/test/unit_test/test_quantity.cpp b/test/unit_test/test_quantity.cpp index b82438c7..4f2a10f4 100644 --- a/test/unit_test/test_quantity.cpp +++ b/test/unit_test/test_quantity.cpp @@ -77,76 +77,76 @@ namespace { // class invariants // constexpr quantity q; // should a static_assert - // constexpr quantity> error(0_m); // should trigger a static_assert + // constexpr quantity> error(0_m); // should trigger a static_assert // constexpr quantity error(0); // should trigger a static_assert // constexpr quantity>, int> error(0); // should trigger a static_assert // member types - static_assert(std::is_same_v::rep, int>); - static_assert(std::is_same_v::rep, double>); - static_assert(std::is_same_v::unit, meter>); - static_assert(std::is_same_v::unit, kilometer>); + static_assert(std::is_same_v::rep, int>); + static_assert(std::is_same_v::rep, double>); + static_assert(std::is_same_v::unit, meter>); + static_assert(std::is_same_v::unit, kilometer>); // constructors - static_assert(length().count() == 0); - constexpr length km{1000}; + static_assert(quantity().count() == 0); + constexpr quantity km{1000}; static_assert(km.count() == 1000); - static_assert(length(km).count() == km.count()); + static_assert(quantity(km).count() == km.count()); - static_assert(length(1).count() == 1); - static_assert(length(my_value(1)).count() == 1); - static_assert(length>(1).count() == 1); - // static_assert(length(1.0).count() == 1); // should not compile - // static_assert(length(my_value(1.0)).count() == 1); // should not compile - // static_assert(length(1.0).count() == 1); // should not compile - static_assert(length(1.0).count() == 1.0); - static_assert(length(my_value(1.0)).count() == 1.0); - static_assert(length(1).count() == 1.0); - static_assert(length(my_value(1)).count() == 1.0); - static_assert(length(3.14).count() == 3.14); - static_assert(length>(1.0).count() == 1.0); - static_assert(length>(1).count() == 1.0); - static_assert(length>(3.14).count() == 3.14); + static_assert(quantity(1).count() == 1); + static_assert(quantity(my_value(1)).count() == 1); + static_assert(quantity>(1).count() == 1); + // static_assert(quantity(1.0).count() == 1); // should not compile + // static_assert(quantity(my_value(1.0)).count() == 1); // should not compile + // static_assert(quantity(1.0).count() == 1); // should not compile + static_assert(quantity(1.0).count() == 1.0); + static_assert(quantity(my_value(1.0)).count() == 1.0); + static_assert(quantity(1).count() == 1.0); + static_assert(quantity(my_value(1)).count() == 1.0); + static_assert(quantity(3.14).count() == 3.14); + static_assert(quantity>(1.0).count() == 1.0); + static_assert(quantity>(1).count() == 1.0); + static_assert(quantity>(3.14).count() == 3.14); - static_assert(length(km).count() == 1000); - // static_assert(length(length(3.14)).count() == 3); // should not compile - static_assert(length(quantity_cast>>(3.14_m)).count() == 3); - // static_assert(length(length>(1000.0)).count() == 1000); // should not compile - // static_assert(length(1000.0_m).count() == 1000); // should not compile - static_assert(length(1000.0_m).count() == 1000.0); - static_assert(length(length>(1000.0)).count() == 1000.0); - static_assert(length>(1000.0_m).count() == 1000.0); - static_assert(length(km).count() == 1000.0); - static_assert(length>(km).count() == 1000.0); - static_assert(length(1_km).count() == 1000); - // static_assert(length(1_s).count() == 1); // should not compile - // static_assert(length(1010_m).count() == 1); // should not compile - static_assert(length(quantity_cast>>(1010_m)).count() == 1); + static_assert(quantity(km).count() == 1000); + // static_assert(quantity(quantity(3.14)).count() == 3); // should not compile + static_assert(quantity(quantity_cast>>(3.14_m)).count() == 3); + // static_assert(quantity(quantity>(1000.0)).count() == 1000); // should not compile + // static_assert(quantity(1000.0_m).count() == 1000); // should not compile + static_assert(quantity(1000.0_m).count() == 1000.0); + static_assert(quantity(quantity>(1000.0)).count() == 1000.0); + static_assert(quantity>(1000.0_m).count() == 1000.0); + static_assert(quantity(km).count() == 1000.0); + static_assert(quantity>(km).count() == 1000.0); + static_assert(quantity(1_km).count() == 1000); + // static_assert(quantity(1_s).count() == 1); // should not compile + // static_assert(quantity(1010_m).count() == 1); // should not compile + static_assert(quantity(quantity_cast>>(1010_m)).count() == 1); // assignment operator static_assert([]() { - length l1(1), l2(2); + quantity l1(1), l2(2); return l2 = l1; }() .count() == 1); // static member functions - static_assert(length::zero().count() == 0); - static_assert(length::min().count() == std::numeric_limits::lowest()); - static_assert(length::max().count() == std::numeric_limits::max()); - static_assert(length::zero().count() == 0.0); - static_assert(length::min().count() == std::numeric_limits::lowest()); - static_assert(length::max().count() == std::numeric_limits::max()); - static_assert(length>::zero().count() == 0); - static_assert(length>::min().count() == std::numeric_limits::lowest()); - static_assert(length>::max().count() == std::numeric_limits::max()); - static_assert(length>::zero().count() == 0.0); - static_assert(length>::min().count() == std::numeric_limits::lowest()); - static_assert(length>::max().count() == std::numeric_limits::max()); + static_assert(quantity::zero().count() == 0); + static_assert(quantity::min().count() == std::numeric_limits::lowest()); + static_assert(quantity::max().count() == std::numeric_limits::max()); + static_assert(quantity::zero().count() == 0.0); + static_assert(quantity::min().count() == std::numeric_limits::lowest()); + static_assert(quantity::max().count() == std::numeric_limits::max()); + static_assert(quantity>::zero().count() == 0); + static_assert(quantity>::min().count() == std::numeric_limits::lowest()); + static_assert(quantity>::max().count() == std::numeric_limits::max()); + static_assert(quantity>::zero().count() == 0.0); + static_assert(quantity>::min().count() == std::numeric_limits::lowest()); + static_assert(quantity>::max().count() == std::numeric_limits::max()); // unary member operators @@ -160,19 +160,19 @@ namespace { static_assert([](auto v) { auto vv = v++; return std::make_pair(v, vv); - }(km) == std::make_pair(length(1001), length(1000))); + }(km) == std::make_pair(quantity(1001), quantity(1000))); static_assert([](auto v) { auto vv = ++v; return std::make_pair(v, vv); - }(km) == std::make_pair(length(1001), length(1001))); + }(km) == std::make_pair(quantity(1001), quantity(1001))); static_assert([](auto v) { auto vv = v--; return std::make_pair(v, vv); - }(km) == std::make_pair(length(999), length(1000))); + }(km) == std::make_pair(quantity(999), quantity(1000))); static_assert([](auto v) { auto vv = --v; return std::make_pair(v, vv); - }(km) == std::make_pair(length(999), length(999))); + }(km) == std::make_pair(quantity(999), quantity(999))); // compound assignment @@ -185,21 +185,21 @@ namespace { // non-member arithmetic operators - static_assert(std::is_same_v() + length()), quantity>); - static_assert(std::is_same_v() + length()), quantity>); - static_assert(std::is_same_v() + length()), quantity>); - static_assert(std::is_same_v() - length()), quantity>); - static_assert(std::is_same_v() - length()), quantity>); - static_assert(std::is_same_v() * 1.0), quantity>); - static_assert(std::is_same_v()), quantity>); - static_assert(std::is_same_v() * units::time()), quantity>); - static_assert(std::is_same_v()), quantity>); - static_assert(std::is_same_v() / 1.0), quantity>); - static_assert(std::is_same_v() / length()), double>); - static_assert(std::is_same_v() / length()), double>); - static_assert(std::is_same_v() / units::time()), quantity>); - static_assert(std::is_same_v() % short(1)), quantity>); - static_assert(std::is_same_v() % length(1)), quantity>); + static_assert(std::is_same_v() + quantity()), quantity>); + static_assert(std::is_same_v() + quantity()), quantity>); + static_assert(std::is_same_v() + quantity()), quantity>); + static_assert(std::is_same_v() - quantity()), quantity>); + static_assert(std::is_same_v() - quantity()), quantity>); + static_assert(std::is_same_v() * 1.0), quantity>); + static_assert(std::is_same_v()), quantity>); + static_assert(std::is_same_v() * units::quantity()), quantity>); + static_assert(std::is_same_v()), quantity>); + static_assert(std::is_same_v() / 1.0), quantity>); + static_assert(std::is_same_v() / quantity()), double>); + static_assert(std::is_same_v() / quantity()), double>); + static_assert(std::is_same_v() / units::quantity()), quantity>); + static_assert(std::is_same_v() % short(1)), quantity>); + static_assert(std::is_same_v() % quantity(1)), quantity>); static_assert((1_m + km).count() == 1001); static_assert((1_m + 1_km).count() == 1001); @@ -247,19 +247,19 @@ namespace { // is_quantity - static_assert(Quantity>); + static_assert(Quantity>); // common_quantity - static_assert(std::is_same_v, length>, length>); - static_assert(std::is_same_v, length>, length>); - static_assert(std::is_same_v, length>, length>); + static_assert(std::is_same_v, quantity>, quantity>); + static_assert(std::is_same_v, quantity>, quantity>); + static_assert(std::is_same_v, quantity>, quantity>); // quantity_cast // static_assert(quantity_cast(2_km).count() == 2000); // should not compile - static_assert(quantity_cast>(2_km).count() == 2000); - static_assert(quantity_cast>(2000_m).count() == 2); + static_assert(quantity_cast>(2_km).count() == 2000); + static_assert(quantity_cast>(2000_m).count() == 2); // time diff --git a/test/unit_test/test_units.cpp b/test/unit_test/test_units.cpp index cbb679ff..48dcee0b 100644 --- a/test/unit_test/test_units.cpp +++ b/test/unit_test/test_units.cpp @@ -74,7 +74,7 @@ namespace { // velocity - static_assert(std::is_same_v>, std::int64_t>>); + static_assert(std::is_same_v>, std::int64_t>>); static_assert(10_m / 5_s == 2_mps); static_assert(10 / 5_s * 1_m == 2_mps); @@ -92,7 +92,7 @@ namespace { static_assert(2_km / 2_kmph == 1_h); // static_assert(2000_m / 2_kmph == 1_h); // should not compile - static_assert(quantity_cast>(2000_m) / 2_kmph == 1_h); + static_assert(quantity_cast>(2000_m) / 2_kmph == 1_h); // area