Dimension template parameter removed from quantity

This commit is contained in:
Mateusz Pusz
2019-07-19 13:49:49 +02:00
parent 03a22cba2a
commit eeb4c9c951
16 changed files with 328 additions and 409 deletions

View File

@@ -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<units::dimension_length, units::kilometer, double> d1(123);
auto d2 = 123_km; // units::quantity<units::dimension_length, units::kilometer, std::int64_t>
units::quantity<units::kilometer, double> d1(123);
auto d2 = 123_km; // units::quantity<units::kilometer, std::int64_t>
```
There are helper aliases provided to improve the work with quantities:
```
template<Unit U = struct meter, Number Rep = double>
using length = quantity<dimension_length, U, Rep>;
units::length d1(123); // units::length<units::meter, int>
units::length<units::mile> d2(3.14); // units::length<units::mile, double>
```
Also there are C++ concepts provided for each such quantity type:
There are C++ concepts provided for each such quantity type:
```cpp
template<typename T>
@@ -241,8 +231,7 @@ concept Unit =
expressed in a specific unit of that dimension:
```cpp
template<Dimension D, Unit U, Number Rep>
requires std::Same<D, typename U::dimension>
template<Unit U, Scalar Rep>
class quantity;
```
@@ -259,29 +248,36 @@ concept Quantity =
member types and functions as below:
```cpp
template<Dimension D, Unit U, Number Rep>
requires std::Same<D, typename U::dimension>
template<Unit U, Scalar Rep>
class quantity {
public:
using dimension = D;
using unit = U;
using rep = Rep;
using dimension = U::dimension;
template<Dimension D1, Unit U1, Number Rep1, Dimension D2, Unit U2, Number Rep2>
requires treat_as_floating_point<std::common_type_t<Rep1, Rep2>> || std::ratio_multiply<typename U1::ratio, typename U2::ratio>::den == 1
quantity<dimension_multiply_t<D1, D2>, downcasting_traits_t<unit<dimension_multiply_t<D1, D2>, std::ratio_multiply<typename U1::ratio, typename U2::ratio>>>, std::common_type_t<Rep1, Rep2>>
constexpr operator*(const quantity<D1, U1, Rep1>& lhs,
const quantity<D2, U2, Rep2>& rhs);
[[nodiscard]] static constexpr quantity one() noexcept { return quantity(quantity_values<Rep>::one()); }
template<Number Rep1, Dimension D, Unit U, Number Rep2>
quantity<dim_invert_t<D>, downcasting_traits_t<unit<dim_invert_t<D>, std::ratio<U::ratio::den, U::ratio::num>>>, std::common_type_t<Rep1, Rep2>>
constexpr operator/(const Rep1& v,
const quantity<D, U, Rep2>& q) [[expects: q != quantity<D, U, Rep2>(0)]];
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
requires treat_as_floating_point<decltype(lhs.count() * rhs.count())> ||
(std::ratio_multiply<typename U1::ratio, typename U2::ratio>::den == 1)
[[nodiscard]] constexpr Quantity operator*(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs);
template<Dimension D1, Unit U1, Number Rep1, Dimension D2, Unit U2, Number Rep2>
requires treat_as_floating_point<std::common_type_t<Rep1, Rep2>> || std::ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1
quantity<dimension_divide_t<D1, D2>, downcasting_traits_t<unit<dimension_divide_t<D1, D2>, std::ratio_divide<typename U1::ratio, typename U2::ratio>>>, std::common_type_t<Rep1, Rep2>>
constexpr operator/(const quantity<D1, U1, Rep1>& lhs,
const quantity<D2, U2, Rep2>& rhs) [[expects: rhs != quantity<D, U2, Rep2>(0)]];
template<Scalar Rep1, typename U, typename Rep2>
[[nodiscard]] constexpr Quantity operator/(const Rep1& v,
const quantity<U, Rep2>& q)
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
requires std::Same<typename U1::dimension, typename U2::dimension>
[[nodiscard]] constexpr Scalar operator/(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs);
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
requires (!std::Same<typename U1::dimension, typename U2::dimension>) &&
(treat_as_floating_point<decltype(lhs.count() / rhs.count())> ||
(ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1))
[[nodiscard]] constexpr Quantity operator/(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& 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<class T> concept const bool units::Velocity<T> [with T = units::quantity<units::dimension<units::exp<units::base_dim_time, 1> >, units::unit<units::dimension<units::exp<units::base_dim_time, 1> >, std::ratio<1> >, long long int>]'
<path>\example\example.cpp:39:22: error: deduced initializer does not satisfy placeholder constraints
const Velocity auto t = 20_s;
^~~~
In file included from <path>\example\example.cpp:23:
<path>/src/include/units/si/velocity.h:41:16: note: within 'template<class T> concept const bool units::Velocity<T> [with T = units::quantity<units::unit<units::dimension<units::exp<units::base_dim_time, 1> >, std::ratio<1> >, long long int>]'
concept Velocity = Quantity<T> && std::Same<typename T::dimension, dimension_velocity>;
^~~~~~~~
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<class T, class U> concept const bool std::Same<T, U> [with T = units::dimension<units::exp<units::base_dim_time, 1> >; U = units::dimension<units::exp<units::base_dim_length, 1>, units::exp<units::base_dim_time, -1> >]'
In file included from <path>/src/include/units/bits/tools.h:25,
from <path>/src/include/units/dimension.h:25,
from <path>/src/include/units/si/base_dimensions.h:25,
from <path>/src/include/units/si/velocity.h:25,
from <path>\example\example.cpp:23:
<path>/src/include/units/bits/stdconcepts.h:33:18: note: within 'template<class T, class U> concept const bool std::Same<T, U> [with T = units::dimension<units::exp<units::base_dim_time, 1> >; U = units::dimension<units::exp<units::base_dim_length, 1>, units::exp<units::base_dim_time, -1> >]'
concept Same = std::is_same_v<T, U>;
^~~~
C:/repos/units/src/include/units/bits/stdconcepts.h:33:18: note: 'std::is_same_v' evaluated to false
<path>/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::dimension<units::exp<units::base_dim_time, 1> >, units::unit<units::dimension<units::exp<units::base_dim_time, 1> >, std::ratio<1> >, long long int>]
[with T = units::quantity<units::unit<units::dimension<units::exp<units::base_dim_time, 1> >, 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
<path>\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<class T> concept const bool units::Velocity<T> [with T = units::quantity<units::dimension_time, units::second, long long int>]'
In file included from <path>\example\example.cpp:23:
<path>/src/include/units/si/velocity.h:48:16: note: within 'template<class T> concept const bool units::Velocity<T> [with T = units::quantity<units::second, long long int>]'
concept Velocity = Quantity<T> && std::Same<typename T::dimension, dimension_velocity>;
^~~~~~~~
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<class T, class U> concept const bool std::Same<T, U> [with T = units::dimension_time; U = units::dimension_velocity]'
In file included from <path>/src/include/units/bits/tools.h:25,
from <path>/src/include/units/dimension.h:25,
from <path>/src/include/units/si/base_dimensions.h:25,
from <path>/src/include/units/si/velocity.h:25,
from <path>\example\example.cpp:23:
<path>/src/include/units/bits/stdconcepts.h:33:18: note: within 'template<class T, class U> concept const bool std::Same<T, U> [with T = units::dimension_time; U = units::dimension_velocity]'
concept Same = std::is_same_v<T, U>;
^~~~
C:/repos/units/src/include/units/bits/stdconcepts.h:33:18: note: 'std::is_same_v' evaluated to false
<path>/src/include/units/bits/stdconcepts.h:33:18: note: 'std::is_same_v' evaluated to false
```
Now
```text
[with T = units::quantity<units::dimension_time, units::second, long long int>]
[with T = units::quantity<units::second, long long int>]
```
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<typename BaseType>
struct downcast_base {
using base_type = BaseType;
};
template<typename T>
concept bool Downcastable =
requires {
typename T::base_type;
} &&
std::DerivedFrom<T, downcast_base<typename T::base_type>>;
template<Downcastable T>
using downcast_from = T::base_type;
template<typename T>
template<Downcastable T>
using downcast_to = std::type_identity<T>;
template<typename T>
template<Downcastable T>
struct downcasting_traits : downcast_to<T> {};
template<typename T>
template<Downcastable T>
using downcasting_traits_t = downcasting_traits<T>::type;
```
@@ -424,21 +432,14 @@ struct dimension_velocity : make_dimension_t<exp<base_dim_length, 1>, exp<base_d
template<> struct downcasting_traits<downcast_from<dimension_velocity>> : downcast_to<dimension_velocity> {};
```
2. Provide `quantity` class template partial specialization for new dimension and provide its base type:
```cpp
template<Unit U = struct meter_per_second, Number Rep = double>
using velocity = quantity<dimension_velocity, U, Rep>;
```
3. Define a concept that will match a new dimension:
2. Define a concept that will match a new dimension:
```cpp
template<typename T>
concept Velocity = Quantity<T> && std::Same<typename T::dimension, dimension_velocity>;
```
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<downcast_from<kilometer_per_hour>> : downca
```cpp
inline namespace literals {
constexpr auto operator""_mps(unsigned long long l) { return velocity<meter_per_second, std::int64_t>(l); }
constexpr auto operator""_mps(long double l) { return velocity<meter_per_second, long double>(l); }
constexpr auto operator""_mps(unsigned long long l) { return quantity<meter_per_second, std::int64_t>(l); }
constexpr auto operator""_mps(long double l) { return quantity<meter_per_second, long double>(l); }
constexpr auto operator""_kmph(unsigned long long l) { return velocity<kilometer_per_hour, std::int64_t>(l); }
constexpr auto operator""_kmph(long double l) { return velocity<kilometer_per_hour, long double>(l); }
constexpr auto operator""_kmph(unsigned long long l) { return quantity<kilometer_per_hour, std::int64_t>(l); }
constexpr auto operator""_kmph(long double l) { return quantity<kilometer_per_hour, long double>(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<int>` or stay with `quantity<second, int>`?
4. What to do with `std::chrono::duration`? Is it possible to make it derive from
`quantity<dimension_time, U, Rep>` which will most probably an ABI break? Alternatively,
should we provide specialization of `quantity<dimension_time, U, Rep>` to work with/covnert
from/to `std::duration`?
5. What is the best way to add support for temperatures?
5. Should we provide `seconds<int>` or stay with `time<second, int>`? What about CTAD problem
for `units::length<units::mile> 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?
9. Should we require explicit casts (i.e. quantity_cast) between different systems of
measurement?
In example instead:
10. Should we support integral representations?
```cpp
struct celsius : unit<dimension_temperature, convert<offset<-27315, 100>>> {};
```
11. Provide ostream overloads to print quantity units (use `std::format`)?
we could think about something like:
```cpp
struct celsius : unit<dimension_temperature, kelvin() - 27315/100>>> {};
```
9. Do we need non-linear scale?
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<Dimension D, Unit U, Number Rep>
requires std::Same<D, typename U::dimension>
class quantity;
template<Unit U = meter_per_second, Number Rep = double>
using velocity = quantity<dimension_velocity, U, Rep>;
units::velocity<units::kilometer_per_hour> kmph = avg_speed(d, t);
```
or maybe we should leave the dimension only in unit
```cpp
template<Unit U, Number Rep>
class quantity;
units::quantity<units::kilometer_per_hour> 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`)?
19. Do we need to support fractional exponents (i.e. `dimension<exp<"length", 2, 3>>` as 2/3)?
20. implicit conversion of quantity<Unit,Y> to quantity<Unit,Z> is allowed if Y and Z are implicitly convertible.
assignment between quantity<Unit,Y> and quantity<Unit,Z> is allowed if Y and Z are implicitly convertible.
21. explicit conversion between quantity<Unit1,Y> and quantity<Unit2,Z> is allowed if Unit1 and Unit2 have the same dimensions and if Y and Z are implicitly convertible.
implicit conversion between quantity<Unit1,Y> and quantity<Unit2,Z> is allowed if Unit1 reduces to exactly the same combination of base units as Unit2 and if Y and Z are convertible.
13. Should we standardize accompany tools (`downcasting_traits`, `type_list` operations, `common_ratio`, etc)?
14. Do we need to support fractional exponents (i.e. `dimension<exp<"length", 2, 3>>` as 2/3)?

View File

@@ -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<units::length<units::meter, double>>(distance).count() << " meters.\n";
<< units::quantity_cast<units::quantity<units::meter, double>>(distance).count() << " meters.\n";
}
void example_2(double distance_v, double duration_v)
{
units::length<units::kilometer> distance(distance_v);
units::time<units::hour> duration(duration_v);
units::quantity<units::kilometer> distance(distance_v);
units::quantity<units::hour> 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";

View File

@@ -32,9 +32,6 @@ namespace units {
template<typename T>
concept bool Area = Quantity<T> && std::Same<typename T::dimension, dimension_area>;
template<Unit U = struct square_meter, Number Rep = double>
using area = quantity<dimension_area, U, Rep>;
struct square_millimeter : derived_unit<dimension_area, millimeter> {};
template<> struct downcasting_traits<downcast_from<square_millimeter>> : downcast_to<square_millimeter> {};
@@ -53,20 +50,20 @@ namespace units {
inline namespace literals {
// sq_mm
constexpr auto operator""_sq_mm(unsigned long long l) { return area<square_millimeter, std::int64_t>(l); }
constexpr auto operator""_sq_mm(long double l) { return area<square_millimeter, long double>(l); }
constexpr auto operator""_sq_mm(unsigned long long l) { return quantity<square_millimeter, std::int64_t>(l); }
constexpr auto operator""_sq_mm(long double l) { return quantity<square_millimeter, long double>(l); }
// sq_cm
constexpr auto operator""_sq_cm(unsigned long long l) { return area<square_centimeter, std::int64_t>(l); }
constexpr auto operator""_sq_cm(long double l) { return area<square_centimeter, long double>(l); }
constexpr auto operator""_sq_cm(unsigned long long l) { return quantity<square_centimeter, std::int64_t>(l); }
constexpr auto operator""_sq_cm(long double l) { return quantity<square_centimeter, long double>(l); }
// sq_m
constexpr auto operator""_sq_m(unsigned long long l) { return area<square_meter, std::int64_t>(l); }
constexpr auto operator""_sq_m(long double l) { return area<square_meter, long double>(l); }
constexpr auto operator""_sq_m(unsigned long long l) { return quantity<square_meter, std::int64_t>(l); }
constexpr auto operator""_sq_m(long double l) { return quantity<square_meter, long double>(l); }
// sq_km
constexpr auto operator""_sq_km(unsigned long long l) { return area<square_kilometer, std::int64_t>(l); }
constexpr auto operator""_sq_km(long double l) { return area<square_kilometer, long double>(l); }
constexpr auto operator""_sq_km(unsigned long long l) { return quantity<square_kilometer, std::int64_t>(l); }
constexpr auto operator""_sq_km(long double l) { return quantity<square_kilometer, long double>(l); }
} // namespace literals

View File

@@ -37,6 +37,7 @@ namespace std {
using type_identity_t = typename type_identity<T>::type;
#endif // UNITS_HAS_STD_TYPE_IDENTITY
// concepts
using experimental::ranges::Same;
using experimental::ranges::Integral;

View File

@@ -33,17 +33,14 @@ namespace units {
template<typename T>
concept bool Current = Quantity<T> && std::Same<typename T::dimension, dimension_current>;
template<Unit U = struct ampere, Number Rep = double>
using current = quantity<dimension_current, U, Rep>;
struct ampere : unit<dimension_current> {};
template<> struct downcasting_traits<downcast_from<ampere>> : downcast_to<ampere> {};
inline namespace literals {
// A
constexpr auto operator""_A(unsigned long long l) { return current<ampere, std::int64_t>(l); }
constexpr auto operator""_A(long double l) { return current<ampere, long double>(l); }
constexpr auto operator""_A(unsigned long long l) { return quantity<ampere, std::int64_t>(l); }
constexpr auto operator""_A(long double l) { return quantity<ampere, long double>(l); }
}

View File

@@ -33,9 +33,6 @@ namespace units {
template<typename T>
concept bool Frequency = Quantity<T> && std::Same<typename T::dimension, dimension_frequency>;
template<Unit U = struct hertz, Number Rep = double>
using frequency = quantity<dimension_frequency, U, Rep>;
struct hertz : derived_unit<dimension_frequency, second> {};
template<> struct downcasting_traits<downcast_from<hertz>> : downcast_to<hertz> {};
@@ -57,28 +54,28 @@ namespace units {
inline namespace literals {
// mHz
constexpr auto operator""_mHz(unsigned long long l) { return frequency<millihertz, std::int64_t>(l); }
constexpr auto operator""_mHz(long double l) { return frequency<millihertz, long double>(l); }
constexpr auto operator""_mHz(unsigned long long l) { return quantity<millihertz, std::int64_t>(l); }
constexpr auto operator""_mHz(long double l) { return quantity<millihertz, long double>(l); }
// Hz
constexpr auto operator""_Hz(unsigned long long l) { return frequency<hertz, std::int64_t>(l); }
constexpr auto operator""_Hz(long double l) { return frequency<hertz, long double>(l); }
constexpr auto operator""_Hz(unsigned long long l) { return quantity<hertz, std::int64_t>(l); }
constexpr auto operator""_Hz(long double l) { return quantity<hertz, long double>(l); }
// kHz
constexpr auto operator""_kHz(unsigned long long l) { return frequency<kilohertz, std::int64_t>(l); }
constexpr auto operator""_kHz(long double l) { return frequency<kilohertz, long double>(l); }
constexpr auto operator""_kHz(unsigned long long l) { return quantity<kilohertz, std::int64_t>(l); }
constexpr auto operator""_kHz(long double l) { return quantity<kilohertz, long double>(l); }
// MHz
constexpr auto operator""_MHz(unsigned long long l) { return frequency<megahertz, std::int64_t>(l); }
constexpr auto operator""_MHz(long double l) { return frequency<megahertz, long double>(l); }
constexpr auto operator""_MHz(unsigned long long l) { return quantity<megahertz, std::int64_t>(l); }
constexpr auto operator""_MHz(long double l) { return quantity<megahertz, long double>(l); }
// GHz
constexpr auto operator""_GHz(unsigned long long l) { return frequency<gigahertz, std::int64_t>(l); }
constexpr auto operator""_GHz(long double l) { return frequency<gigahertz, long double>(l); }
constexpr auto operator""_GHz(unsigned long long l) { return quantity<gigahertz, std::int64_t>(l); }
constexpr auto operator""_GHz(long double l) { return quantity<gigahertz, long double>(l); }
// THz
constexpr auto operator""_THz(unsigned long long l) { return frequency<terahertz, std::int64_t>(l); }
constexpr auto operator""_THz(long double l) { return frequency<terahertz, long double>(l); }
constexpr auto operator""_THz(unsigned long long l) { return quantity<terahertz, std::int64_t>(l); }
constexpr auto operator""_THz(long double l) { return quantity<terahertz, long double>(l); }
} // namespace literals

View File

@@ -33,9 +33,6 @@ namespace units {
template<typename T>
concept bool Length = Quantity<T> && std::Same<typename T::dimension, dimension_length>;
template<Unit U = struct meter, Number Rep = double>
using length = quantity<dimension_length, U, Rep>;
// SI units
struct meter : unit<dimension_length> {};
template<> struct downcasting_traits<downcast_from<meter>> : downcast_to<meter> {};
@@ -52,20 +49,20 @@ namespace units {
inline namespace literals {
// mm
constexpr auto operator""_mm(unsigned long long l) { return length<millimeter, std::int64_t>(l); }
constexpr auto operator""_mm(long double l) { return length<millimeter, long double>(l); }
constexpr auto operator""_mm(unsigned long long l) { return quantity<millimeter, std::int64_t>(l); }
constexpr auto operator""_mm(long double l) { return quantity<millimeter, long double>(l); }
// cm
constexpr auto operator""_cm(unsigned long long l) { return length<centimeter, std::int64_t>(l); }
constexpr auto operator""_cm(long double l) { return length<centimeter, long double>(l); }
constexpr auto operator""_cm(unsigned long long l) { return quantity<centimeter, std::int64_t>(l); }
constexpr auto operator""_cm(long double l) { return quantity<centimeter, long double>(l); }
// m
constexpr auto operator""_m(unsigned long long l) { return length<meter, std::int64_t>(l); }
constexpr auto operator""_m(long double l) { return length<meter, long double>(l); }
constexpr auto operator""_m(unsigned long long l) { return quantity<meter, std::int64_t>(l); }
constexpr auto operator""_m(long double l) { return quantity<meter, long double>(l); }
// km
constexpr auto operator""_km(unsigned long long l) { return length<kilometer, std::int64_t>(l); }
constexpr auto operator""_km(long double l) { return length<kilometer, long double>(l); }
constexpr auto operator""_km(unsigned long long l) { return quantity<kilometer, std::int64_t>(l); }
constexpr auto operator""_km(long double l) { return quantity<kilometer, long double>(l); }
} // namespace literals
@@ -85,20 +82,20 @@ namespace units {
inline namespace literals {
// yd
constexpr auto operator""_yd(unsigned long long l) { return length<yard, std::int64_t>(l); }
constexpr auto operator""_yd(long double l) { return length<yard, long double>(l); }
constexpr auto operator""_yd(unsigned long long l) { return quantity<yard, std::int64_t>(l); }
constexpr auto operator""_yd(long double l) { return quantity<yard, long double>(l); }
// ft
constexpr auto operator""_ft(unsigned long long l) { return length<foot, std::int64_t>(l); }
constexpr auto operator""_ft(long double l) { return length<foot, long double>(l); }
constexpr auto operator""_ft(unsigned long long l) { return quantity<foot, std::int64_t>(l); }
constexpr auto operator""_ft(long double l) { return quantity<foot, long double>(l); }
// in
constexpr auto operator""_in(unsigned long long l) { return length<inch, std::int64_t>(l); }
constexpr auto operator""_in(long double l) { return length<inch, long double>(l); }
constexpr auto operator""_in(unsigned long long l) { return quantity<inch, std::int64_t>(l); }
constexpr auto operator""_in(long double l) { return quantity<inch, long double>(l); }
// mi
constexpr auto operator""_mi(unsigned long long l) { return length<mile, std::int64_t>(l); }
constexpr auto operator""_mi(long double l) { return length<mile, long double>(l); }
constexpr auto operator""_mi(unsigned long long l) { return quantity<mile, std::int64_t>(l); }
constexpr auto operator""_mi(long double l) { return quantity<mile, long double>(l); }
} // namespace literals

View File

@@ -33,17 +33,14 @@ namespace units {
template<typename T>
concept bool LuminousIntensity = Quantity<T> && std::Same<typename T::dimension, dimension_luminous_intensity>;
template<Unit U = struct candela, Number Rep = double>
using luminous_intensity = quantity<dimension_luminous_intensity, U, Rep>;
struct candela : unit<dimension_luminous_intensity> {};
template<> struct downcasting_traits<downcast_from<candela>> : downcast_to<candela> {};
inline namespace literals {
// cd
constexpr auto operator""_cd(unsigned long long l) { return luminous_intensity<candela, std::int64_t>(l); }
constexpr auto operator""_cd(long double l) { return luminous_intensity<candela, long double>(l); }
constexpr auto operator""_cd(unsigned long long l) { return quantity<candela, std::int64_t>(l); }
constexpr auto operator""_cd(long double l) { return quantity<candela, long double>(l); }
} // namespace literals

View File

@@ -33,9 +33,6 @@ namespace units {
template<typename T>
concept bool Mass = Quantity<T> && std::Same<typename T::dimension, dimension_mass>;
template<Unit U = class kilogram, Number Rep = double>
using mass = quantity<dimension_mass, U, Rep>;
struct gram : unit<dimension_mass, ratio<1, 1000>> {};
template<> struct downcasting_traits<downcast_from<gram>> : downcast_to<gram> {};
@@ -45,12 +42,12 @@ namespace units {
inline namespace literals {
// g
constexpr auto operator""_g(unsigned long long l) { return mass<gram, std::int64_t>(l); }
constexpr auto operator""_g(long double l) { return mass<gram, long double>(l); }
constexpr auto operator""_g(unsigned long long l) { return quantity<gram, std::int64_t>(l); }
constexpr auto operator""_g(long double l) { return quantity<gram, long double>(l); }
// kg
constexpr auto operator""_kg(unsigned long long l) { return mass<kilogram, std::int64_t>(l); }
constexpr auto operator""_kg(long double l) { return mass<kilogram, long double>(l); }
constexpr auto operator""_kg(unsigned long long l) { return quantity<kilogram, std::int64_t>(l); }
constexpr auto operator""_kg(long double l) { return quantity<kilogram, long double>(l); }
} // namespace literals

View File

@@ -45,14 +45,13 @@ namespace units {
template<typename T>
concept bool Scalar = Number<T> && !Quantity<T>;
template<Dimension D, Unit U, Scalar Rep>
requires std::Same<D, typename U::dimension>
template<Unit U, Scalar Rep>
class quantity;
namespace detail {
template<Dimension D, Unit U, Scalar Rep>
inline constexpr bool is_quantity<quantity<D, U, Rep>> = true;
template<Unit U, Scalar Rep>
inline constexpr bool is_quantity<quantity<U, Rep>> = true;
} // namespace detail
@@ -60,14 +59,15 @@ namespace units {
template<Quantity Q1, Quantity Q2, Scalar Rep>
struct common_quantity;
template<Dimension D, Unit U, Scalar Rep1, Scalar Rep2, Scalar Rep>
struct common_quantity<quantity<D, U, Rep1>, quantity<D, U, Rep2>, Rep> {
using type = quantity<D, U, Rep>;
template<Unit U, Scalar Rep1, Scalar Rep2, Scalar Rep>
struct common_quantity<quantity<U, Rep1>, quantity<U, Rep2>, Rep> {
using type = quantity<U, Rep>;
};
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2, Scalar Rep>
struct common_quantity<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, Rep> {
using type = quantity<D, downcasting_traits_t<unit<D, common_ratio<typename U1::ratio, typename U2::ratio>>>, Rep>;
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2, Scalar Rep>
requires std::Same<typename U1::dimension, typename U2::dimension>
struct common_quantity<quantity<U1, Rep1>, quantity<U2, Rep2>, Rep> {
using type = quantity<downcasting_traits_t<unit<typename U1::dimension, common_ratio<typename U1::ratio, typename U2::ratio>>>, Rep>;
};
template<Quantity Q1, Quantity Q2, Scalar Rep = std::common_type_t<typename Q1::rep, typename Q2::rep>>
@@ -121,9 +121,9 @@ namespace units {
} // namespace detail
template<Quantity To, Dimension D, Unit U, Scalar Rep>
requires std::Same<typename To::dimension, D>
constexpr To quantity_cast(const quantity<D, U, Rep>& q)
template<Quantity To, Unit U, Scalar Rep>
requires std::Same<typename To::dimension, typename U::dimension>
constexpr To quantity_cast(const quantity<U, Rep>& q)
{
using c_ratio = ratio_divide<typename U::ratio, typename To::unit::ratio>;
using c_rep = std::common_type_t<typename To::rep, Rep, intmax_t>;
@@ -143,15 +143,14 @@ namespace units {
// quantity
template<Dimension D, Unit U, Scalar Rep>
requires std::Same<D, typename U::dimension>
template<Unit U, Scalar Rep = double>
class quantity {
Rep value_;
public:
using dimension = D;
using unit = U;
using rep = Rep;
using dimension = U::dimension;
static_assert(!Quantity<Rep>, "rep cannot be a quantity");
@@ -238,163 +237,173 @@ namespace units {
};
// clang-format off
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr Quantity operator+(const quantity<D, U1, Rep1>& lhs,
const quantity<D, U2, Rep2>& rhs)
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr Quantity operator+(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs)
requires std::Same<typename U1::dimension, typename U2::dimension>
{
using common_rep = decltype(lhs.count() + rhs.count());
using ret = common_quantity_t<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, common_rep>;
using ret = common_quantity_t<quantity<U1, Rep1>, quantity<U2, Rep2>, common_rep>;
return ret(ret(lhs).count() + ret(rhs).count());
}
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr Quantity operator-(const quantity<D, U1, Rep1>& lhs,
const quantity<D, U2, Rep2>& rhs)
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr Quantity operator-(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs)
requires std::Same<typename U1::dimension, typename U2::dimension>
{
using common_rep = decltype(lhs.count() - rhs.count());
using ret = common_quantity_t<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, common_rep>;
using ret = common_quantity_t<quantity<U1, Rep1>, quantity<U2, Rep2>, common_rep>;
return ret(ret(lhs).count() - ret(rhs).count());
}
// template<Dimension D, Unit U, Scalar Rep1, Scalar Rep2>
template<typename D, typename U, typename Rep1, typename Rep2>
[[nodiscard]] constexpr Quantity operator*(const quantity<D, U, Rep1>& q,
// template<Unit U, Scalar Rep1, Scalar Rep2>
template<typename U, typename Rep1, typename Rep2>
[[nodiscard]] constexpr Quantity operator*(const quantity<U, Rep1>& q,
const Rep2& v)
requires (!Quantity<Rep2>)
{
using common_rep = decltype(q.count()* v);
using ret = quantity<D, U, common_rep>;
using ret = quantity<U, common_rep>;
return ret(ret(q).count() * v);
}
//template<Scalar Rep1, Dimension D, Unit U, Scalar Rep2>
template<typename Rep1, typename D, typename U, typename Rep2>
//template<Scalar Rep1, Unit U, Scalar Rep2>
template<typename Rep1, typename U, typename Rep2>
[[nodiscard]] constexpr Quantity operator*(const Rep1& v,
const quantity<D, U, Rep2>& q)
const quantity<U, Rep2>& q)
requires (!Quantity<Rep1>)
{
return q * v;
}
template<Dimension D1, Unit U1, Scalar Rep1, Dimension D2, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr Quantity operator*(const quantity<D1, U1, Rep1>& lhs,
const quantity<D2, U2, Rep2>& rhs)
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr Quantity operator*(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs)
requires treat_as_floating_point<decltype(lhs.count() * rhs.count())> ||
(std::ratio_multiply<typename U1::ratio, typename U2::ratio>::den == 1)
{
using dim = dimension_multiply_t<D1, D2>;
using dim = dimension_multiply_t<typename U1::dimension, typename U2::dimension>;
using common_rep = decltype(lhs.count() * rhs.count());
using ret = quantity<dim, downcasting_traits_t<unit<dim, ratio_multiply<typename U1::ratio, typename U2::ratio>>>, common_rep>;
using ret = quantity<downcasting_traits_t<unit<dim, ratio_multiply<typename U1::ratio, typename U2::ratio>>>, common_rep>;
return ret(lhs.count() * rhs.count());
}
// template<Scalar Rep1, Dimension D, Unit U, Scalar Rep2>
template<typename Rep1, typename D, typename U, typename Rep2>
// template<Scalar Rep1, Unit U, Scalar Rep2>
template<typename Rep1, typename U, typename Rep2>
[[nodiscard]] constexpr Quantity operator/(const Rep1& v,
const quantity<D, U, Rep2>& q)
const quantity<U, Rep2>& q)
requires (!Quantity<Rep1>)
{
Expects(q != std::remove_cvref_t<decltype(q)>(0));
using dim = dim_invert_t<D>;
using dim = dim_invert_t<typename U::dimension>;
using common_rep = decltype(v / q.count());
using ret = quantity<dim, downcasting_traits_t<unit<dim, ratio<U::ratio::den, U::ratio::num>>>, common_rep>;
using den = quantity<D, U, common_rep>;
using ret = quantity<downcasting_traits_t<unit<dim, ratio<U::ratio::den, U::ratio::num>>>, common_rep>;
using den = quantity<U, common_rep>;
return ret(v / den(q).count());
}
// template<Dimension D, Unit U, Scalar Rep1, Scalar Rep2>
template<typename D, typename U, typename Rep1, typename Rep2>
[[nodiscard]] constexpr Quantity operator/(const quantity<D, U, Rep1>& q,
// template<Unit U, Scalar Rep1, Scalar Rep2>
template<typename U, typename Rep1, typename Rep2>
[[nodiscard]] constexpr Quantity operator/(const quantity<U, Rep1>& q,
const Rep2& v)
requires (!Quantity<Rep2>)
{
Expects(v != Rep2{0});
using common_rep = decltype(q.count() / v);
using ret = quantity<D, U, common_rep>;
using ret = quantity<U, common_rep>;
return ret(ret(q).count() / v);
}
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr Scalar operator/(const quantity<D, U1, Rep1>& lhs,
const quantity<D, U2, Rep2>& rhs)
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr Scalar operator/(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs)
requires std::Same<typename U1::dimension, typename U2::dimension>
{
Expects(rhs != std::remove_cvref_t<decltype(rhs)>(0));
using common_rep = decltype(lhs.count() / rhs.count());
using cq = common_quantity_t<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, common_rep>;
using cq = common_quantity_t<quantity<U1, Rep1>, quantity<U2, Rep2>, common_rep>;
return cq(lhs).count() / cq(rhs).count();
}
template<Dimension D1, Unit U1, Scalar Rep1, Dimension D2, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr Quantity operator/(const quantity<D1, U1, Rep1>& lhs,
const quantity<D2, U2, Rep2>& rhs)
requires treat_as_floating_point<decltype(lhs.count() / rhs.count())> ||
(ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1)
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr Quantity operator/(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs)
requires (!std::Same<typename U1::dimension, typename U2::dimension>) &&
(treat_as_floating_point<decltype(lhs.count() / rhs.count())> ||
(ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1))
{
Expects(rhs != std::remove_cvref_t<decltype(rhs)>(0));
using common_rep = decltype(lhs.count() / rhs.count());
using dim = dimension_divide_t<D1, D2>;
using ret = quantity<dim, downcasting_traits_t<unit<dim, ratio_divide<typename U1::ratio, typename U2::ratio>>>, common_rep>;
using dim = dimension_divide_t<typename U1::dimension, typename U2::dimension>;
using ret = quantity<downcasting_traits_t<unit<dim, ratio_divide<typename U1::ratio, typename U2::ratio>>>, common_rep>;
return ret(lhs.count() / rhs.count());
}
template<Dimension D, Unit U, Scalar Rep1, Scalar Rep2>
[[nodiscard]] constexpr Quantity operator%(const quantity<D, U, Rep1>& q,
template<Unit U, Scalar Rep1, Scalar Rep2>
[[nodiscard]] constexpr Quantity operator%(const quantity<U, Rep1>& q,
const Rep2& v)
{
using common_rep = decltype(q.count() % v);
using ret = quantity<D, U, common_rep>;
using ret = quantity<U, common_rep>;
return ret(ret(q).count() % v);
}
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr Quantity operator%(const quantity<D, U1, Rep1>& lhs,
const quantity<D, U2, Rep2>& rhs)
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr Quantity operator%(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs)
{
using common_rep = decltype(lhs.count() % rhs.count());
using ret = common_quantity_t<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, common_rep>;
using ret = common_quantity_t<quantity<U1, Rep1>, quantity<U2, Rep2>, common_rep>;
return ret(ret(lhs).count() % ret(rhs).count());
}
// clang-format on
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr bool operator==(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr bool operator==(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs)
requires std::Same<typename U1::dimension, typename U2::dimension>
{
using ct = common_quantity_t<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>>;
using ct = common_quantity_t<quantity<U1, Rep1>, quantity<U2, Rep2>>;
return ct(lhs).count() == ct(rhs).count();
}
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr bool operator!=(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr bool operator!=(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs)
requires std::Same<typename U1::dimension, typename U2::dimension>
{
return !(lhs == rhs);
}
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr bool operator<(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr bool operator<(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs)
requires std::Same<typename U1::dimension, typename U2::dimension>
{
using ct = common_quantity_t<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>>;
using ct = common_quantity_t<quantity<U1, Rep1>, quantity<U2, Rep2>>;
return ct(lhs).count() < ct(rhs).count();
}
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr bool operator<=(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr bool operator<=(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs)
requires std::Same<typename U1::dimension, typename U2::dimension>
{
return !(rhs < lhs);
}
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr bool operator>(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr bool operator>(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs)
requires std::Same<typename U1::dimension, typename U2::dimension>
{
return rhs < lhs;
}
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr bool operator>=(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
[[nodiscard]] constexpr bool operator>=(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs)
requires std::Same<typename U1::dimension, typename U2::dimension>
{
return !(lhs < rhs);
}

View File

@@ -33,17 +33,14 @@ namespace units {
template<typename T>
concept bool Substance = Quantity<T> && std::Same<typename T::dimension, dimension_substance>;
template<Unit U = struct mole, Number Rep = double>
using substance = quantity<dimension_substance, U, Rep>;
struct mole : unit<dimension_substance> {};
template<> struct downcasting_traits<downcast_from<mole>> : downcast_to<mole> {};
inline namespace literals {
// mol
constexpr auto operator""_mol(unsigned long long l) { return substance<mole, std::int64_t>(l); }
constexpr auto operator""_mol(long double l) { return substance<mole, long double>(l); }
constexpr auto operator""_mol(unsigned long long l) { return quantity<mole, std::int64_t>(l); }
constexpr auto operator""_mol(long double l) { return quantity<mole, long double>(l); }
} // namespace literals

View File

@@ -33,17 +33,14 @@ namespace units {
template<typename T>
concept bool ThermodynamicTemperature = Quantity<T> && std::Same<typename T::dimension, dimension_temperature>;
template<Unit U = struct kelvin, Number Rep = double>
using temperature = quantity<dimension_temperature, U, Rep>;
struct kelvin : unit<dimension_temperature> {};
template<> struct downcasting_traits<downcast_from<kelvin>> : downcast_to<kelvin> {};
inline namespace literals {
// K
constexpr auto operator""_K(unsigned long long l) { return temperature<kelvin, std::int64_t>(l); }
constexpr auto operator""_K(long double l) { return temperature<kelvin, long double>(l); }
constexpr auto operator""_K(unsigned long long l) { return quantity<kelvin, std::int64_t>(l); }
constexpr auto operator""_K(long double l) { return quantity<kelvin, long double>(l); }
} // namespace literals

View File

@@ -33,9 +33,6 @@ namespace units {
template<typename T>
concept bool Time = Quantity<T> && std::Same<typename T::dimension, dimension_time>;
template<Unit U = struct second, Number Rep = double>
using time = quantity<dimension_time, U, Rep>;
struct second : unit<dimension_time> {};
template<> struct downcasting_traits<downcast_from<second>> : downcast_to<second> {};
@@ -57,28 +54,28 @@ namespace units {
inline namespace literals {
// ns
constexpr auto operator""_ns(unsigned long long l) { return time<nanosecond, std::int64_t>(l); }
constexpr auto operator""_ns(long double l) { return time<nanosecond, long double>(l); }
constexpr auto operator""_ns(unsigned long long l) { return quantity<nanosecond, std::int64_t>(l); }
constexpr auto operator""_ns(long double l) { return quantity<nanosecond, long double>(l); }
// us
constexpr auto operator""_us(unsigned long long l) { return time<microsecond, std::int64_t>(l); }
constexpr auto operator""_us(long double l) { return time<microsecond, long double>(l); }
constexpr auto operator""_us(unsigned long long l) { return quantity<microsecond, std::int64_t>(l); }
constexpr auto operator""_us(long double l) { return quantity<microsecond, long double>(l); }
// ms
constexpr auto operator""_ms(unsigned long long l) { return time<millisecond, std::int64_t>(l); }
constexpr auto operator""_ms(long double l) { return time<millisecond, long double>(l); }
constexpr auto operator""_ms(unsigned long long l) { return quantity<millisecond, std::int64_t>(l); }
constexpr auto operator""_ms(long double l) { return quantity<millisecond, long double>(l); }
// s
constexpr auto operator""_s(unsigned long long l) { return time<second, std::int64_t>(l); }
constexpr auto operator""_s(long double l) { return time<second, long double>(l); }
constexpr auto operator""_s(unsigned long long l) { return quantity<second, std::int64_t>(l); }
constexpr auto operator""_s(long double l) { return quantity<second, long double>(l); }
// min
constexpr auto operator""_min(unsigned long long l) { return time<minute, std::int64_t>(l); }
constexpr auto operator""_min(long double l) { return time<minute, long double>(l); }
constexpr auto operator""_min(unsigned long long l) { return quantity<minute, std::int64_t>(l); }
constexpr auto operator""_min(long double l) { return quantity<minute, long double>(l); }
// h
constexpr auto operator""_h(unsigned long long l) { return time<hour, std::int64_t>(l); }
constexpr auto operator""_h(long double l) { return time<hour, long double>(l); }
constexpr auto operator""_h(unsigned long long l) { return quantity<hour, std::int64_t>(l); }
constexpr auto operator""_h(long double l) { return quantity<hour, long double>(l); }
} // namespace literals

View File

@@ -33,9 +33,6 @@ namespace units {
template<typename T>
concept bool Velocity = Quantity<T> && std::Same<typename T::dimension, dimension_velocity>;
template<Unit U = struct meter_per_second, Number Rep = double>
using velocity = quantity<dimension_velocity, U, Rep>;
struct meter_per_second : derived_unit<dimension_velocity, meter, second> {};
template<> struct downcasting_traits<downcast_from<meter_per_second>> : downcast_to<meter_per_second> {};
@@ -48,16 +45,16 @@ namespace units {
inline namespace literals {
// mps
constexpr auto operator""_mps(unsigned long long l) { return velocity<meter_per_second, std::int64_t>(l); }
constexpr auto operator""_mps(long double l) { return velocity<meter_per_second, long double>(l); }
constexpr auto operator""_mps(unsigned long long l) { return quantity<meter_per_second, std::int64_t>(l); }
constexpr auto operator""_mps(long double l) { return quantity<meter_per_second, long double>(l); }
// kmph
constexpr auto operator""_kmph(unsigned long long l) { return velocity<kilometer_per_hour, std::int64_t>(l); }
constexpr auto operator""_kmph(long double l) { return velocity<kilometer_per_hour, long double>(l); }
constexpr auto operator""_kmph(unsigned long long l) { return quantity<kilometer_per_hour, std::int64_t>(l); }
constexpr auto operator""_kmph(long double l) { return quantity<kilometer_per_hour, long double>(l); }
// mph
constexpr auto operator""_mph(unsigned long long l) { return velocity<mile_per_hour, std::int64_t>(l); }
constexpr auto operator""_mph(long double l) { return velocity<mile_per_hour, long double>(l); }
constexpr auto operator""_mph(unsigned long long l) { return quantity<mile_per_hour, std::int64_t>(l); }
constexpr auto operator""_mph(long double l) { return quantity<mile_per_hour, long double>(l); }
} // namespace literals

View File

@@ -77,76 +77,76 @@ namespace {
// class invariants
// constexpr quantity<dimension_length, second, int> q; // should a static_assert
// constexpr quantity<dimension_length, meter, length<meter, int>> error(0_m); // should trigger a static_assert
// constexpr quantity<dimension_length, meter, quantity<meter, int>> error(0_m); // should trigger a static_assert
// constexpr quantity<int, int, double> error(0); // should trigger a static_assert
// constexpr quantity<dimension_length, unit<dimension_length, std::ratio<-1, 1>>, int> error(0); // should trigger a static_assert
// member types
static_assert(std::is_same_v<length<meter, int>::rep, int>);
static_assert(std::is_same_v<length<meter, double>::rep, double>);
static_assert(std::is_same_v<length<meter, int>::unit, meter>);
static_assert(std::is_same_v<length<kilometer, int>::unit, kilometer>);
static_assert(std::is_same_v<quantity<meter, int>::rep, int>);
static_assert(std::is_same_v<quantity<meter, double>::rep, double>);
static_assert(std::is_same_v<quantity<meter, int>::unit, meter>);
static_assert(std::is_same_v<quantity<kilometer, int>::unit, kilometer>);
// constructors
static_assert(length<meter, int>().count() == 0);
constexpr length<meter, int> km{1000};
static_assert(quantity<meter, int>().count() == 0);
constexpr quantity<meter, int> km{1000};
static_assert(km.count() == 1000);
static_assert(length<meter, int>(km).count() == km.count());
static_assert(quantity<meter, int>(km).count() == km.count());
static_assert(length<meter, int>(1).count() == 1);
static_assert(length<meter, int>(my_value(1)).count() == 1);
static_assert(length<meter, my_value<int>>(1).count() == 1);
// static_assert(length<meter, int>(1.0).count() == 1); // should not compile
// static_assert(length<meter, int>(my_value(1.0)).count() == 1); // should not compile
// static_assert(length<meter, my_value>(1.0).count() == 1); // should not compile
static_assert(length<meter, double>(1.0).count() == 1.0);
static_assert(length<meter, double>(my_value(1.0)).count() == 1.0);
static_assert(length<meter, double>(1).count() == 1.0);
static_assert(length<meter, double>(my_value(1)).count() == 1.0);
static_assert(length<meter, double>(3.14).count() == 3.14);
static_assert(length<meter, my_value<double>>(1.0).count() == 1.0);
static_assert(length<meter, my_value<double>>(1).count() == 1.0);
static_assert(length<meter, my_value<double>>(3.14).count() == 3.14);
static_assert(quantity<meter, int>(1).count() == 1);
static_assert(quantity<meter, int>(my_value(1)).count() == 1);
static_assert(quantity<meter, my_value<int>>(1).count() == 1);
// static_assert(quantity<meter, int>(1.0).count() == 1); // should not compile
// static_assert(quantity<meter, int>(my_value(1.0)).count() == 1); // should not compile
// static_assert(quantity<meter, my_value>(1.0).count() == 1); // should not compile
static_assert(quantity<meter, double>(1.0).count() == 1.0);
static_assert(quantity<meter, double>(my_value(1.0)).count() == 1.0);
static_assert(quantity<meter, double>(1).count() == 1.0);
static_assert(quantity<meter, double>(my_value(1)).count() == 1.0);
static_assert(quantity<meter, double>(3.14).count() == 3.14);
static_assert(quantity<meter, my_value<double>>(1.0).count() == 1.0);
static_assert(quantity<meter, my_value<double>>(1).count() == 1.0);
static_assert(quantity<meter, my_value<double>>(3.14).count() == 3.14);
static_assert(length<meter, int>(km).count() == 1000);
// static_assert(length<meter, int>(length<meter, double>(3.14)).count() == 3); // should not compile
static_assert(length<meter, int>(quantity_cast<length<meter, my_value<int>>>(3.14_m)).count() == 3);
// static_assert(length<meter, int>(length<meter, my_value<double>>(1000.0)).count() == 1000); // should not compile
// static_assert(length<meter, my_value>(1000.0_m).count() == 1000); // should not compile
static_assert(length<meter, double>(1000.0_m).count() == 1000.0);
static_assert(length<meter, double>(length<meter, my_value<double>>(1000.0)).count() == 1000.0);
static_assert(length<meter, my_value<double>>(1000.0_m).count() == 1000.0);
static_assert(length<meter, double>(km).count() == 1000.0);
static_assert(length<meter, my_value<double>>(km).count() == 1000.0);
static_assert(length<meter, int>(1_km).count() == 1000);
// static_assert(length<meter, int>(1_s).count() == 1); // should not compile
// static_assert(length<kilometer, int>(1010_m).count() == 1); // should not compile
static_assert(length<kilometer, int>(quantity_cast<length<kilometer, my_value<int>>>(1010_m)).count() == 1);
static_assert(quantity<meter, int>(km).count() == 1000);
// static_assert(quantity<meter, int>(quantity<meter, double>(3.14)).count() == 3); // should not compile
static_assert(quantity<meter, int>(quantity_cast<quantity<meter, my_value<int>>>(3.14_m)).count() == 3);
// static_assert(quantity<meter, int>(quantity<meter, my_value<double>>(1000.0)).count() == 1000); // should not compile
// static_assert(quantity<meter, my_value>(1000.0_m).count() == 1000); // should not compile
static_assert(quantity<meter, double>(1000.0_m).count() == 1000.0);
static_assert(quantity<meter, double>(quantity<meter, my_value<double>>(1000.0)).count() == 1000.0);
static_assert(quantity<meter, my_value<double>>(1000.0_m).count() == 1000.0);
static_assert(quantity<meter, double>(km).count() == 1000.0);
static_assert(quantity<meter, my_value<double>>(km).count() == 1000.0);
static_assert(quantity<meter, int>(1_km).count() == 1000);
// static_assert(quantity<meter, int>(1_s).count() == 1); // should not compile
// static_assert(quantity<kilometer, int>(1010_m).count() == 1); // should not compile
static_assert(quantity<kilometer, int>(quantity_cast<quantity<kilometer, my_value<int>>>(1010_m)).count() == 1);
// assignment operator
static_assert([]() {
length<meter, int> l1(1), l2(2);
quantity<meter, int> l1(1), l2(2);
return l2 = l1;
}()
.count() == 1);
// static member functions
static_assert(length<meter, int>::zero().count() == 0);
static_assert(length<meter, int>::min().count() == std::numeric_limits<int>::lowest());
static_assert(length<meter, int>::max().count() == std::numeric_limits<int>::max());
static_assert(length<meter, double>::zero().count() == 0.0);
static_assert(length<meter, double>::min().count() == std::numeric_limits<double>::lowest());
static_assert(length<meter, double>::max().count() == std::numeric_limits<double>::max());
static_assert(length<meter, my_value<int>>::zero().count() == 0);
static_assert(length<meter, my_value<int>>::min().count() == std::numeric_limits<int>::lowest());
static_assert(length<meter, my_value<int>>::max().count() == std::numeric_limits<int>::max());
static_assert(length<meter, my_value<double>>::zero().count() == 0.0);
static_assert(length<meter, my_value<double>>::min().count() == std::numeric_limits<double>::lowest());
static_assert(length<meter, my_value<double>>::max().count() == std::numeric_limits<double>::max());
static_assert(quantity<meter, int>::zero().count() == 0);
static_assert(quantity<meter, int>::min().count() == std::numeric_limits<int>::lowest());
static_assert(quantity<meter, int>::max().count() == std::numeric_limits<int>::max());
static_assert(quantity<meter, double>::zero().count() == 0.0);
static_assert(quantity<meter, double>::min().count() == std::numeric_limits<double>::lowest());
static_assert(quantity<meter, double>::max().count() == std::numeric_limits<double>::max());
static_assert(quantity<meter, my_value<int>>::zero().count() == 0);
static_assert(quantity<meter, my_value<int>>::min().count() == std::numeric_limits<int>::lowest());
static_assert(quantity<meter, my_value<int>>::max().count() == std::numeric_limits<int>::max());
static_assert(quantity<meter, my_value<double>>::zero().count() == 0.0);
static_assert(quantity<meter, my_value<double>>::min().count() == std::numeric_limits<double>::lowest());
static_assert(quantity<meter, my_value<double>>::max().count() == std::numeric_limits<double>::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<meter, int>(1001), length<meter, int>(1000)));
}(km) == std::make_pair(quantity<meter, int>(1001), quantity<meter, int>(1000)));
static_assert([](auto v) {
auto vv = ++v;
return std::make_pair(v, vv);
}(km) == std::make_pair(length<meter, int>(1001), length<meter, int>(1001)));
}(km) == std::make_pair(quantity<meter, int>(1001), quantity<meter, int>(1001)));
static_assert([](auto v) {
auto vv = v--;
return std::make_pair(v, vv);
}(km) == std::make_pair(length<meter, int>(999), length<meter, int>(1000)));
}(km) == std::make_pair(quantity<meter, int>(999), quantity<meter, int>(1000)));
static_assert([](auto v) {
auto vv = --v;
return std::make_pair(v, vv);
}(km) == std::make_pair(length<meter, int>(999), length<meter, int>(999)));
}(km) == std::make_pair(quantity<meter, int>(999), quantity<meter, int>(999)));
// compound assignment
@@ -185,21 +185,21 @@ namespace {
// non-member arithmetic operators
static_assert(std::is_same_v<decltype(length<meter, int>() + length<meter, double>()), quantity<dimension_length, meter, double>>);
static_assert(std::is_same_v<decltype(length<meter, int>() + length<meter, double>()), quantity<dimension_length, meter, double>>);
static_assert(std::is_same_v<decltype(length<kilometer, int>() + length<meter, double>()), quantity<dimension_length, meter, double>>);
static_assert(std::is_same_v<decltype(length<meter, double>() - length<meter, int>()), quantity<dimension_length, meter, double>>);
static_assert(std::is_same_v<decltype(length<kilometer, double>() - length<meter, int>()), quantity<dimension_length, meter, double>>);
static_assert(std::is_same_v<decltype(length<meter, int>() * 1.0), quantity<dimension_length, meter, double>>);
static_assert(std::is_same_v<decltype(1.0 * length<meter, int>()), quantity<dimension_length, meter, double>>);
static_assert(std::is_same_v<decltype(velocity<meter_per_second, int>() * units::time<second, int>()), quantity<dimension_length, units::meter, int>>);
static_assert(std::is_same_v<decltype(1 / units::time<second, int>()), quantity<dimension_frequency, hertz, int>>);
static_assert(std::is_same_v<decltype(length<meter, int>() / 1.0), quantity<dimension_length, meter, double>>);
static_assert(std::is_same_v<decltype(length<meter, int>() / length<meter, double>()), double>);
static_assert(std::is_same_v<decltype(length<kilometer, int>() / length<meter, double>()), double>);
static_assert(std::is_same_v<decltype(length<meter, int>() / units::time<second, int>()), quantity<dimension_velocity, meter_per_second, int>>);
static_assert(std::is_same_v<decltype(length<meter, int>() % short(1)), quantity<dimension_length, meter, int>>);
static_assert(std::is_same_v<decltype(length<meter, int>() % length<meter, short>(1)), quantity<dimension_length, meter, int>>);
static_assert(std::is_same_v<decltype(quantity<meter, int>() + quantity<meter, double>()), quantity<meter, double>>);
static_assert(std::is_same_v<decltype(quantity<meter, int>() + quantity<meter, double>()), quantity<meter, double>>);
static_assert(std::is_same_v<decltype(quantity<kilometer, int>() + quantity<meter, double>()), quantity<meter, double>>);
static_assert(std::is_same_v<decltype(quantity<meter, double>() - quantity<meter, int>()), quantity<meter, double>>);
static_assert(std::is_same_v<decltype(quantity<kilometer, double>() - quantity<meter, int>()), quantity<meter, double>>);
static_assert(std::is_same_v<decltype(quantity<meter, int>() * 1.0), quantity<meter, double>>);
static_assert(std::is_same_v<decltype(1.0 * quantity<meter, int>()), quantity<meter, double>>);
static_assert(std::is_same_v<decltype(quantity<meter_per_second, int>() * units::quantity<second, int>()), quantity<units::meter, int>>);
static_assert(std::is_same_v<decltype(1 / units::quantity<second, int>()), quantity<hertz, int>>);
static_assert(std::is_same_v<decltype(quantity<meter, int>() / 1.0), quantity<meter, double>>);
static_assert(std::is_same_v<decltype(quantity<meter, int>() / quantity<meter, double>()), double>);
static_assert(std::is_same_v<decltype(quantity<kilometer, int>() / quantity<meter, double>()), double>);
static_assert(std::is_same_v<decltype(quantity<meter, int>() / units::quantity<second, int>()), quantity<meter_per_second, int>>);
static_assert(std::is_same_v<decltype(quantity<meter, int>() % short(1)), quantity<meter, int>>);
static_assert(std::is_same_v<decltype(quantity<meter, int>() % quantity<meter, short>(1)), quantity<meter, int>>);
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<length<millimeter, int>>);
static_assert(Quantity<quantity<millimeter, int>>);
// common_quantity
static_assert(std::is_same_v<common_quantity_t<length<meter, int>, length<kilometer, int>>, length<meter, int>>);
static_assert(std::is_same_v<common_quantity_t<length<kilometer, long long>, length<meter, int>>, length<meter, long long>>);
static_assert(std::is_same_v<common_quantity_t<length<kilometer, long long>, length<millimeter, double>>, length<millimeter, double>>);
static_assert(std::is_same_v<common_quantity_t<quantity<meter, int>, quantity<kilometer, int>>, quantity<meter, int>>);
static_assert(std::is_same_v<common_quantity_t<quantity<kilometer, long long>, quantity<meter, int>>, quantity<meter, long long>>);
static_assert(std::is_same_v<common_quantity_t<quantity<kilometer, long long>, quantity<millimeter, double>>, quantity<millimeter, double>>);
// quantity_cast
// static_assert(quantity_cast<int>(2_km).count() == 2000); // should not compile
static_assert(quantity_cast<length<meter, int>>(2_km).count() == 2000);
static_assert(quantity_cast<length<kilometer, int>>(2000_m).count() == 2);
static_assert(quantity_cast<quantity<meter, int>>(2_km).count() == 2000);
static_assert(quantity_cast<quantity<kilometer, int>>(2000_m).count() == 2);
// time

View File

@@ -74,7 +74,7 @@ namespace {
// velocity
static_assert(std::is_same_v<decltype(1_km / 1_s), velocity<unit<dimension_velocity, ratio<1000, 1>>, std::int64_t>>);
static_assert(std::is_same_v<decltype(1_km / 1_s), quantity<unit<dimension_velocity, ratio<1000, 1>>, 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<length<kilometer, int>>(2000_m) / 2_kmph == 1_h);
static_assert(quantity_cast<quantity<kilometer, int>>(2000_m) / 2_kmph == 1_h);
// area