mirror of
https://github.com/mpusz/mp-units.git
synced 2025-07-31 02:47:16 +02:00
Dimension template parameter removed from quantity
This commit is contained in:
249
doc/DESIGN.md
249
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:
|
Quantity is a concrete amount of a unit for a specified dimension with a specific representation:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
units::quantity<units::dimension_length, units::kilometer, double> d1(123);
|
units::quantity<units::kilometer, double> d1(123);
|
||||||
auto d2 = 123_km; // units::quantity<units::dimension_length, units::kilometer, std::int64_t>
|
auto d2 = 123_km; // units::quantity<units::kilometer, std::int64_t>
|
||||||
```
|
```
|
||||||
|
|
||||||
There are helper aliases provided to improve the work with quantities:
|
There are C++ concepts provided for each such quantity type:
|
||||||
|
|
||||||
```
|
|
||||||
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:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@ -241,8 +231,7 @@ concept Unit =
|
|||||||
expressed in a specific unit of that dimension:
|
expressed in a specific unit of that dimension:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template<Dimension D, Unit U, Number Rep>
|
template<Unit U, Scalar Rep>
|
||||||
requires std::Same<D, typename U::dimension>
|
|
||||||
class quantity;
|
class quantity;
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -259,29 +248,36 @@ concept Quantity =
|
|||||||
member types and functions as below:
|
member types and functions as below:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template<Dimension D, Unit U, Number Rep>
|
template<Unit U, Scalar Rep>
|
||||||
requires std::Same<D, typename U::dimension>
|
|
||||||
class quantity {
|
class quantity {
|
||||||
public:
|
public:
|
||||||
using dimension = D;
|
|
||||||
using unit = U;
|
using unit = U;
|
||||||
|
using rep = Rep;
|
||||||
|
using dimension = U::dimension;
|
||||||
|
|
||||||
template<Dimension D1, Unit U1, Number Rep1, Dimension D2, Unit U2, Number Rep2>
|
[[nodiscard]] static constexpr quantity one() noexcept { return quantity(quantity_values<Rep>::one()); }
|
||||||
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);
|
|
||||||
|
|
||||||
template<Number Rep1, Dimension D, Unit U, Number Rep2>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar 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>>
|
requires treat_as_floating_point<decltype(lhs.count() * rhs.count())> ||
|
||||||
constexpr operator/(const Rep1& v,
|
(std::ratio_multiply<typename U1::ratio, typename U2::ratio>::den == 1)
|
||||||
const quantity<D, U, Rep2>& q) [[expects: q != quantity<D, U, Rep2>(0)]];
|
[[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>
|
template<Scalar Rep1, typename U, typename Rep2>
|
||||||
requires treat_as_floating_point<std::common_type_t<Rep1, Rep2>> || std::ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1
|
[[nodiscard]] constexpr Quantity operator/(const Rep1& v,
|
||||||
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>>
|
const quantity<U, Rep2>& q)
|
||||||
constexpr operator/(const quantity<D1, U1, Rep1>& lhs,
|
|
||||||
const quantity<D2, U2, Rep2>& rhs) [[expects: rhs != quantity<D, U2, Rep2>(0)]];
|
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:
|
For example with template aliases usage the following code:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const Velocity t = 20_s;
|
const Velocity auto t = 20_s;
|
||||||
```
|
```
|
||||||
|
|
||||||
could generate a following compile time error:
|
could generate a following compile time error:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
C:\repos\units\example\example.cpp:39:22: error: deduced initializer does not satisfy placeholder constraints
|
<path>\example\example.cpp:39:22: error: deduced initializer does not satisfy placeholder constraints
|
||||||
const Velocity t = 20_s;
|
const Velocity auto t = 20_s;
|
||||||
^~~~
|
^~~~
|
||||||
In file included from C:\repos\units\example\example.cpp:23:
|
In file included from <path>\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>/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>;
|
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,
|
In file included from <path>/src/include/units/bits/tools.h:25,
|
||||||
from C:/repos/units/src/include/units/dimension.h:25,
|
from <path>/src/include/units/dimension.h:25,
|
||||||
from C:/repos/units/src/include/units/si/base_dimensions.h:25,
|
from <path>/src/include/units/si/base_dimensions.h:25,
|
||||||
from C:/repos/units/src/include/units/si/velocity.h:25,
|
from <path>/src/include/units/si/velocity.h:25,
|
||||||
from C:\repos\units\example\example.cpp:23:
|
from <path>\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> >]'
|
<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>;
|
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
|
Time and velocity are not that complicated dimensions and there are much more complicated dimensions
|
||||||
out there, but even for those dimensions
|
out there, but even for those dimensions
|
||||||
|
|
||||||
```text
|
```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
|
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:
|
same code will result with such an error:
|
||||||
|
|
||||||
```text
|
```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;
|
const Velocity t = 20_s;
|
||||||
^~~~
|
^~~~
|
||||||
In file included from C:\repos\units\example\example.cpp:23:
|
In file included from <path>\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>]'
|
<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>;
|
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,
|
In file included from <path>/src/include/units/bits/tools.h:25,
|
||||||
from C:/repos/units/src/include/units/dimension.h:25,
|
from <path>/src/include/units/dimension.h:25,
|
||||||
from C:/repos/units/src/include/units/si/base_dimensions.h:25,
|
from <path>/src/include/units/si/base_dimensions.h:25,
|
||||||
from C:/repos/units/src/include/units/si/velocity.h:25,
|
from <path>/src/include/units/si/velocity.h:25,
|
||||||
from C:\repos\units\example\example.cpp:23:
|
from <path>\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]'
|
<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>;
|
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
|
Now
|
||||||
|
|
||||||
```text
|
```text
|
||||||
[with T = units::quantity<units::dimension_time, units::second, long long int>]
|
[with T = units::quantity<units::second, long long int>]
|
||||||
```
|
```
|
||||||
|
|
||||||
and
|
and
|
||||||
@ -383,20 +379,32 @@ and
|
|||||||
|
|
||||||
are not arguably much easier to understand thus provide better user experience.
|
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.
|
`base_type` member type in `downcast_base` class template.
|
||||||
|
|
||||||
```cpp
|
```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>
|
template<Downcastable T>
|
||||||
using downcast_from = T::base_type;
|
using downcast_from = T::base_type;
|
||||||
|
|
||||||
template<typename T>
|
template<Downcastable T>
|
||||||
using downcast_to = std::type_identity<T>;
|
using downcast_to = std::type_identity<T>;
|
||||||
|
|
||||||
template<typename T>
|
template<Downcastable T>
|
||||||
struct downcasting_traits : downcast_to<T> {};
|
struct downcasting_traits : downcast_to<T> {};
|
||||||
|
|
||||||
template<typename T>
|
template<Downcastable T>
|
||||||
using downcasting_traits_t = downcasting_traits<T>::type;
|
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> {};
|
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:
|
2. Define a concept that will match a new dimension:
|
||||||
|
|
||||||
```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:
|
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
template<typename T>
|
template<typename T>
|
||||||
concept Velocity = Quantity<T> && std::Same<typename T::dimension, dimension_velocity>;
|
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
|
- base unit
|
||||||
|
|
||||||
@ -465,11 +466,11 @@ template<> struct downcasting_traits<downcast_from<kilometer_per_hour>> : downca
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
constexpr auto operator""_mps(unsigned long long l) { return velocity<meter_per_second, std::int64_t>(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 velocity<meter_per_second, long double>(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(unsigned long long l) { return quantity<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(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?
|
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
|
5. What is the best way to add support for temperatures?
|
||||||
`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. Should we provide `seconds<int>` or stay with `time<second, int>`? What about CTAD problem
|
Temperature absolute values not only require `std::ratio` but also should be adjusted/shifted
|
||||||
for `units::length<units::mile> d3(3);`?
|
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
|
7. Should we provide cmath-like functions for quantities?
|
||||||
constant values (i.e. [°C] = [K] − 273.15).
|
|
||||||
|
|
||||||
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
|
9. Should we require explicit casts (i.e. quantity_cast) between different systems of
|
||||||
as they provide different ratio values rather than types?
|
measurement?
|
||||||
|
|
||||||
In example instead:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct celsius : unit<dimension_temperature, convert<offset<-27315, 100>>> {};
|
|
||||||
```
|
|
||||||
|
|
||||||
we could think about something like:
|
10. Should we support integral representations?
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct celsius : unit<dimension_temperature, kelvin() - 27315/100>>> {};
|
|
||||||
```
|
|
||||||
|
|
||||||
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?
|
12. Should we provide support for dimensionless 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?
|
|
||||||
|
|
||||||
Because dimensionless quantities have no associated units, they behave as normal scalars,
|
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
|
and allow implicit conversion to and from the underlying value type or types that are
|
||||||
convertible to/from that value type.
|
convertible to/from that value type.
|
||||||
|
|
||||||
17. Should we leave `quantity` and specific dimensions as
|
13. Should we standardize accompany tools (`downcasting_traits`, `type_list` operations, `common_ratio`, etc)?
|
||||||
```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)?
|
14. 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.
|
|
||||||
|
|
@ -38,13 +38,13 @@ void example_1(V v, T t)
|
|||||||
{
|
{
|
||||||
const units::Length distance = v * 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 "
|
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)
|
void example_2(double distance_v, double duration_v)
|
||||||
{
|
{
|
||||||
units::length<units::kilometer> distance(distance_v);
|
units::quantity<units::kilometer> distance(distance_v);
|
||||||
units::time<units::hour> duration(duration_v);
|
units::quantity<units::hour> duration(duration_v);
|
||||||
const auto kmph = avg_speed(distance, duration);
|
const auto kmph = avg_speed(distance, duration);
|
||||||
std::cout << "Average speed of a car that makes " << distance.count() << " km in "
|
std::cout << "Average speed of a car that makes " << distance.count() << " km in "
|
||||||
<< duration.count() << " hours is " << kmph.count() << " km/h.\n";
|
<< duration.count() << " hours is " << kmph.count() << " km/h.\n";
|
||||||
|
@ -32,9 +32,6 @@ namespace units {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
concept bool Area = Quantity<T> && std::Same<typename T::dimension, dimension_area>;
|
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> {};
|
struct square_millimeter : derived_unit<dimension_area, millimeter> {};
|
||||||
template<> struct downcasting_traits<downcast_from<square_millimeter>> : downcast_to<square_millimeter> {};
|
template<> struct downcasting_traits<downcast_from<square_millimeter>> : downcast_to<square_millimeter> {};
|
||||||
|
|
||||||
@ -53,20 +50,20 @@ namespace units {
|
|||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
|
|
||||||
// sq_mm
|
// sq_mm
|
||||||
constexpr auto operator""_sq_mm(unsigned long long l) { return area<square_millimeter, std::int64_t>(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 area<square_millimeter, long double>(l); }
|
constexpr auto operator""_sq_mm(long double l) { return quantity<square_millimeter, long double>(l); }
|
||||||
|
|
||||||
// sq_cm
|
// sq_cm
|
||||||
constexpr auto operator""_sq_cm(unsigned long long l) { return area<square_centimeter, std::int64_t>(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 area<square_centimeter, long double>(l); }
|
constexpr auto operator""_sq_cm(long double l) { return quantity<square_centimeter, long double>(l); }
|
||||||
|
|
||||||
// sq_m
|
// sq_m
|
||||||
constexpr auto operator""_sq_m(unsigned long long l) { return area<square_meter, std::int64_t>(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 area<square_meter, long double>(l); }
|
constexpr auto operator""_sq_m(long double l) { return quantity<square_meter, long double>(l); }
|
||||||
|
|
||||||
// sq_km
|
// sq_km
|
||||||
constexpr auto operator""_sq_km(unsigned long long l) { return area<square_kilometer, std::int64_t>(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 area<square_kilometer, long double>(l); }
|
constexpr auto operator""_sq_km(long double l) { return quantity<square_kilometer, long double>(l); }
|
||||||
|
|
||||||
} // namespace literals
|
} // namespace literals
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ namespace std {
|
|||||||
using type_identity_t = typename type_identity<T>::type;
|
using type_identity_t = typename type_identity<T>::type;
|
||||||
|
|
||||||
#endif // UNITS_HAS_STD_TYPE_IDENTITY
|
#endif // UNITS_HAS_STD_TYPE_IDENTITY
|
||||||
|
|
||||||
// concepts
|
// concepts
|
||||||
using experimental::ranges::Same;
|
using experimental::ranges::Same;
|
||||||
using experimental::ranges::Integral;
|
using experimental::ranges::Integral;
|
||||||
@ -45,4 +46,4 @@ namespace std {
|
|||||||
using experimental::ranges::StrictTotallyOrdered;
|
using experimental::ranges::StrictTotallyOrdered;
|
||||||
using experimental::ranges::ConvertibleTo;
|
using experimental::ranges::ConvertibleTo;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,17 +33,14 @@ namespace units {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
concept bool Current = Quantity<T> && std::Same<typename T::dimension, dimension_current>;
|
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> {};
|
struct ampere : unit<dimension_current> {};
|
||||||
template<> struct downcasting_traits<downcast_from<ampere>> : downcast_to<ampere> {};
|
template<> struct downcasting_traits<downcast_from<ampere>> : downcast_to<ampere> {};
|
||||||
|
|
||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
|
|
||||||
// A
|
// A
|
||||||
constexpr auto operator""_A(unsigned long long l) { return current<ampere, std::int64_t>(l); }
|
constexpr auto operator""_A(unsigned long long l) { return quantity<ampere, std::int64_t>(l); }
|
||||||
constexpr auto operator""_A(long double l) { return current<ampere, long double>(l); }
|
constexpr auto operator""_A(long double l) { return quantity<ampere, long double>(l); }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,9 +33,6 @@ namespace units {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
concept bool Frequency = Quantity<T> && std::Same<typename T::dimension, dimension_frequency>;
|
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> {};
|
struct hertz : derived_unit<dimension_frequency, second> {};
|
||||||
template<> struct downcasting_traits<downcast_from<hertz>> : downcast_to<hertz> {};
|
template<> struct downcasting_traits<downcast_from<hertz>> : downcast_to<hertz> {};
|
||||||
|
|
||||||
@ -57,28 +54,28 @@ namespace units {
|
|||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
|
|
||||||
// mHz
|
// mHz
|
||||||
constexpr auto operator""_mHz(unsigned long long l) { return frequency<millihertz, std::int64_t>(l); }
|
constexpr auto operator""_mHz(unsigned long long l) { return quantity<millihertz, std::int64_t>(l); }
|
||||||
constexpr auto operator""_mHz(long double l) { return frequency<millihertz, long double>(l); }
|
constexpr auto operator""_mHz(long double l) { return quantity<millihertz, long double>(l); }
|
||||||
|
|
||||||
// Hz
|
// Hz
|
||||||
constexpr auto operator""_Hz(unsigned long long l) { return frequency<hertz, std::int64_t>(l); }
|
constexpr auto operator""_Hz(unsigned long long l) { return quantity<hertz, std::int64_t>(l); }
|
||||||
constexpr auto operator""_Hz(long double l) { return frequency<hertz, long double>(l); }
|
constexpr auto operator""_Hz(long double l) { return quantity<hertz, long double>(l); }
|
||||||
|
|
||||||
// kHz
|
// kHz
|
||||||
constexpr auto operator""_kHz(unsigned long long l) { return frequency<kilohertz, std::int64_t>(l); }
|
constexpr auto operator""_kHz(unsigned long long l) { return quantity<kilohertz, std::int64_t>(l); }
|
||||||
constexpr auto operator""_kHz(long double l) { return frequency<kilohertz, long double>(l); }
|
constexpr auto operator""_kHz(long double l) { return quantity<kilohertz, long double>(l); }
|
||||||
|
|
||||||
// MHz
|
// MHz
|
||||||
constexpr auto operator""_MHz(unsigned long long l) { return frequency<megahertz, std::int64_t>(l); }
|
constexpr auto operator""_MHz(unsigned long long l) { return quantity<megahertz, std::int64_t>(l); }
|
||||||
constexpr auto operator""_MHz(long double l) { return frequency<megahertz, long double>(l); }
|
constexpr auto operator""_MHz(long double l) { return quantity<megahertz, long double>(l); }
|
||||||
|
|
||||||
// GHz
|
// GHz
|
||||||
constexpr auto operator""_GHz(unsigned long long l) { return frequency<gigahertz, std::int64_t>(l); }
|
constexpr auto operator""_GHz(unsigned long long l) { return quantity<gigahertz, std::int64_t>(l); }
|
||||||
constexpr auto operator""_GHz(long double l) { return frequency<gigahertz, long double>(l); }
|
constexpr auto operator""_GHz(long double l) { return quantity<gigahertz, long double>(l); }
|
||||||
|
|
||||||
// THz
|
// THz
|
||||||
constexpr auto operator""_THz(unsigned long long l) { return frequency<terahertz, std::int64_t>(l); }
|
constexpr auto operator""_THz(unsigned long long l) { return quantity<terahertz, std::int64_t>(l); }
|
||||||
constexpr auto operator""_THz(long double l) { return frequency<terahertz, long double>(l); }
|
constexpr auto operator""_THz(long double l) { return quantity<terahertz, long double>(l); }
|
||||||
|
|
||||||
} // namespace literals
|
} // namespace literals
|
||||||
|
|
||||||
|
@ -33,9 +33,6 @@ namespace units {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
concept bool Length = Quantity<T> && std::Same<typename T::dimension, dimension_length>;
|
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
|
// SI units
|
||||||
struct meter : unit<dimension_length> {};
|
struct meter : unit<dimension_length> {};
|
||||||
template<> struct downcasting_traits<downcast_from<meter>> : downcast_to<meter> {};
|
template<> struct downcasting_traits<downcast_from<meter>> : downcast_to<meter> {};
|
||||||
@ -52,20 +49,20 @@ namespace units {
|
|||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
|
|
||||||
// mm
|
// mm
|
||||||
constexpr auto operator""_mm(unsigned long long l) { return length<millimeter, std::int64_t>(l); }
|
constexpr auto operator""_mm(unsigned long long l) { return quantity<millimeter, std::int64_t>(l); }
|
||||||
constexpr auto operator""_mm(long double l) { return length<millimeter, long double>(l); }
|
constexpr auto operator""_mm(long double l) { return quantity<millimeter, long double>(l); }
|
||||||
|
|
||||||
// cm
|
// cm
|
||||||
constexpr auto operator""_cm(unsigned long long l) { return length<centimeter, std::int64_t>(l); }
|
constexpr auto operator""_cm(unsigned long long l) { return quantity<centimeter, std::int64_t>(l); }
|
||||||
constexpr auto operator""_cm(long double l) { return length<centimeter, long double>(l); }
|
constexpr auto operator""_cm(long double l) { return quantity<centimeter, long double>(l); }
|
||||||
|
|
||||||
// m
|
// m
|
||||||
constexpr auto operator""_m(unsigned long long l) { return length<meter, std::int64_t>(l); }
|
constexpr auto operator""_m(unsigned long long l) { return quantity<meter, std::int64_t>(l); }
|
||||||
constexpr auto operator""_m(long double l) { return length<meter, long double>(l); }
|
constexpr auto operator""_m(long double l) { return quantity<meter, long double>(l); }
|
||||||
|
|
||||||
// km
|
// km
|
||||||
constexpr auto operator""_km(unsigned long long l) { return length<kilometer, std::int64_t>(l); }
|
constexpr auto operator""_km(unsigned long long l) { return quantity<kilometer, std::int64_t>(l); }
|
||||||
constexpr auto operator""_km(long double l) { return length<kilometer, long double>(l); }
|
constexpr auto operator""_km(long double l) { return quantity<kilometer, long double>(l); }
|
||||||
|
|
||||||
} // namespace literals
|
} // namespace literals
|
||||||
|
|
||||||
@ -85,20 +82,20 @@ namespace units {
|
|||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
|
|
||||||
// yd
|
// yd
|
||||||
constexpr auto operator""_yd(unsigned long long l) { return length<yard, std::int64_t>(l); }
|
constexpr auto operator""_yd(unsigned long long l) { return quantity<yard, std::int64_t>(l); }
|
||||||
constexpr auto operator""_yd(long double l) { return length<yard, long double>(l); }
|
constexpr auto operator""_yd(long double l) { return quantity<yard, long double>(l); }
|
||||||
|
|
||||||
// ft
|
// ft
|
||||||
constexpr auto operator""_ft(unsigned long long l) { return length<foot, std::int64_t>(l); }
|
constexpr auto operator""_ft(unsigned long long l) { return quantity<foot, std::int64_t>(l); }
|
||||||
constexpr auto operator""_ft(long double l) { return length<foot, long double>(l); }
|
constexpr auto operator""_ft(long double l) { return quantity<foot, long double>(l); }
|
||||||
|
|
||||||
// in
|
// in
|
||||||
constexpr auto operator""_in(unsigned long long l) { return length<inch, std::int64_t>(l); }
|
constexpr auto operator""_in(unsigned long long l) { return quantity<inch, std::int64_t>(l); }
|
||||||
constexpr auto operator""_in(long double l) { return length<inch, long double>(l); }
|
constexpr auto operator""_in(long double l) { return quantity<inch, long double>(l); }
|
||||||
|
|
||||||
// mi
|
// mi
|
||||||
constexpr auto operator""_mi(unsigned long long l) { return length<mile, std::int64_t>(l); }
|
constexpr auto operator""_mi(unsigned long long l) { return quantity<mile, std::int64_t>(l); }
|
||||||
constexpr auto operator""_mi(long double l) { return length<mile, long double>(l); }
|
constexpr auto operator""_mi(long double l) { return quantity<mile, long double>(l); }
|
||||||
|
|
||||||
} // namespace literals
|
} // namespace literals
|
||||||
|
|
||||||
|
@ -33,17 +33,14 @@ namespace units {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
concept bool LuminousIntensity = Quantity<T> && std::Same<typename T::dimension, dimension_luminous_intensity>;
|
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> {};
|
struct candela : unit<dimension_luminous_intensity> {};
|
||||||
template<> struct downcasting_traits<downcast_from<candela>> : downcast_to<candela> {};
|
template<> struct downcasting_traits<downcast_from<candela>> : downcast_to<candela> {};
|
||||||
|
|
||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
|
|
||||||
// cd
|
// cd
|
||||||
constexpr auto operator""_cd(unsigned long long l) { return luminous_intensity<candela, std::int64_t>(l); }
|
constexpr auto operator""_cd(unsigned long long l) { return quantity<candela, std::int64_t>(l); }
|
||||||
constexpr auto operator""_cd(long double l) { return luminous_intensity<candela, long double>(l); }
|
constexpr auto operator""_cd(long double l) { return quantity<candela, long double>(l); }
|
||||||
|
|
||||||
} // namespace literals
|
} // namespace literals
|
||||||
|
|
||||||
|
@ -33,9 +33,6 @@ namespace units {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
concept bool Mass = Quantity<T> && std::Same<typename T::dimension, dimension_mass>;
|
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>> {};
|
struct gram : unit<dimension_mass, ratio<1, 1000>> {};
|
||||||
template<> struct downcasting_traits<downcast_from<gram>> : downcast_to<gram> {};
|
template<> struct downcasting_traits<downcast_from<gram>> : downcast_to<gram> {};
|
||||||
|
|
||||||
@ -45,12 +42,12 @@ namespace units {
|
|||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
|
|
||||||
// g
|
// g
|
||||||
constexpr auto operator""_g(unsigned long long l) { return mass<gram, std::int64_t>(l); }
|
constexpr auto operator""_g(unsigned long long l) { return quantity<gram, std::int64_t>(l); }
|
||||||
constexpr auto operator""_g(long double l) { return mass<gram, long double>(l); }
|
constexpr auto operator""_g(long double l) { return quantity<gram, long double>(l); }
|
||||||
|
|
||||||
// kg
|
// kg
|
||||||
constexpr auto operator""_kg(unsigned long long l) { return mass<kilogram, std::int64_t>(l); }
|
constexpr auto operator""_kg(unsigned long long l) { return quantity<kilogram, std::int64_t>(l); }
|
||||||
constexpr auto operator""_kg(long double l) { return mass<kilogram, long double>(l); }
|
constexpr auto operator""_kg(long double l) { return quantity<kilogram, long double>(l); }
|
||||||
|
|
||||||
} // namespace literals
|
} // namespace literals
|
||||||
|
|
||||||
|
@ -45,14 +45,13 @@ namespace units {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
concept bool Scalar = Number<T> && !Quantity<T>;
|
concept bool Scalar = Number<T> && !Quantity<T>;
|
||||||
|
|
||||||
template<Dimension D, Unit U, Scalar Rep>
|
template<Unit U, Scalar Rep>
|
||||||
requires std::Same<D, typename U::dimension>
|
|
||||||
class quantity;
|
class quantity;
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<Dimension D, Unit U, Scalar Rep>
|
template<Unit U, Scalar Rep>
|
||||||
inline constexpr bool is_quantity<quantity<D, U, Rep>> = true;
|
inline constexpr bool is_quantity<quantity<U, Rep>> = true;
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
@ -60,14 +59,15 @@ namespace units {
|
|||||||
template<Quantity Q1, Quantity Q2, Scalar Rep>
|
template<Quantity Q1, Quantity Q2, Scalar Rep>
|
||||||
struct common_quantity;
|
struct common_quantity;
|
||||||
|
|
||||||
template<Dimension D, Unit U, Scalar Rep1, Scalar Rep2, Scalar Rep>
|
template<Unit U, Scalar Rep1, Scalar Rep2, Scalar Rep>
|
||||||
struct common_quantity<quantity<D, U, Rep1>, quantity<D, U, Rep2>, Rep> {
|
struct common_quantity<quantity<U, Rep1>, quantity<U, Rep2>, Rep> {
|
||||||
using type = quantity<D, U, Rep>;
|
using type = quantity<U, Rep>;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2, Scalar Rep>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2, Scalar Rep>
|
||||||
struct common_quantity<quantity<D, U1, Rep1>, quantity<D, U2, Rep2>, Rep> {
|
requires std::Same<typename U1::dimension, typename U2::dimension>
|
||||||
using type = quantity<D, downcasting_traits_t<unit<D, common_ratio<typename U1::ratio, typename U2::ratio>>>, Rep>;
|
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>>
|
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
|
} // namespace detail
|
||||||
|
|
||||||
template<Quantity To, Dimension D, Unit U, Scalar Rep>
|
template<Quantity To, Unit U, Scalar Rep>
|
||||||
requires std::Same<typename To::dimension, D>
|
requires std::Same<typename To::dimension, typename U::dimension>
|
||||||
constexpr To quantity_cast(const quantity<D, U, Rep>& q)
|
constexpr To quantity_cast(const quantity<U, Rep>& q)
|
||||||
{
|
{
|
||||||
using c_ratio = ratio_divide<typename U::ratio, typename To::unit::ratio>;
|
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>;
|
using c_rep = std::common_type_t<typename To::rep, Rep, intmax_t>;
|
||||||
@ -143,15 +143,14 @@ namespace units {
|
|||||||
|
|
||||||
// quantity
|
// quantity
|
||||||
|
|
||||||
template<Dimension D, Unit U, Scalar Rep>
|
template<Unit U, Scalar Rep = double>
|
||||||
requires std::Same<D, typename U::dimension>
|
|
||||||
class quantity {
|
class quantity {
|
||||||
Rep value_;
|
Rep value_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using dimension = D;
|
|
||||||
using unit = U;
|
using unit = U;
|
||||||
using rep = Rep;
|
using rep = Rep;
|
||||||
|
using dimension = U::dimension;
|
||||||
|
|
||||||
static_assert(!Quantity<Rep>, "rep cannot be a quantity");
|
static_assert(!Quantity<Rep>, "rep cannot be a quantity");
|
||||||
|
|
||||||
@ -238,163 +237,173 @@ namespace units {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
||||||
[[nodiscard]] constexpr Quantity operator+(const quantity<D, U1, Rep1>& lhs,
|
[[nodiscard]] constexpr Quantity operator+(const quantity<U1, Rep1>& lhs,
|
||||||
const quantity<D, U2, Rep2>& rhs)
|
const quantity<U2, Rep2>& rhs)
|
||||||
|
requires std::Same<typename U1::dimension, typename U2::dimension>
|
||||||
{
|
{
|
||||||
using common_rep = decltype(lhs.count() + rhs.count());
|
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());
|
return ret(ret(lhs).count() + ret(rhs).count());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
||||||
[[nodiscard]] constexpr Quantity operator-(const quantity<D, U1, Rep1>& lhs,
|
[[nodiscard]] constexpr Quantity operator-(const quantity<U1, Rep1>& lhs,
|
||||||
const quantity<D, U2, Rep2>& rhs)
|
const quantity<U2, Rep2>& rhs)
|
||||||
|
requires std::Same<typename U1::dimension, typename U2::dimension>
|
||||||
{
|
{
|
||||||
using common_rep = decltype(lhs.count() - rhs.count());
|
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());
|
return ret(ret(lhs).count() - ret(rhs).count());
|
||||||
}
|
}
|
||||||
|
|
||||||
// template<Dimension D, Unit U, Scalar Rep1, Scalar Rep2>
|
// template<Unit U, Scalar Rep1, Scalar Rep2>
|
||||||
template<typename D, typename U, typename Rep1, typename Rep2>
|
template<typename U, typename Rep1, typename Rep2>
|
||||||
[[nodiscard]] constexpr Quantity operator*(const quantity<D, U, Rep1>& q,
|
[[nodiscard]] constexpr Quantity operator*(const quantity<U, Rep1>& q,
|
||||||
const Rep2& v)
|
const Rep2& v)
|
||||||
requires (!Quantity<Rep2>)
|
requires (!Quantity<Rep2>)
|
||||||
{
|
{
|
||||||
using common_rep = decltype(q.count()* 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);
|
return ret(ret(q).count() * v);
|
||||||
}
|
}
|
||||||
|
|
||||||
//template<Scalar Rep1, Dimension D, Unit U, Scalar Rep2>
|
//template<Scalar Rep1, Unit U, Scalar Rep2>
|
||||||
template<typename Rep1, typename D, typename U, typename Rep2>
|
template<typename Rep1, typename U, typename Rep2>
|
||||||
[[nodiscard]] constexpr Quantity operator*(const Rep1& v,
|
[[nodiscard]] constexpr Quantity operator*(const Rep1& v,
|
||||||
const quantity<D, U, Rep2>& q)
|
const quantity<U, Rep2>& q)
|
||||||
requires (!Quantity<Rep1>)
|
requires (!Quantity<Rep1>)
|
||||||
{
|
{
|
||||||
return q * v;
|
return q * v;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Dimension D1, Unit U1, Scalar Rep1, Dimension D2, Unit U2, Scalar Rep2>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
||||||
[[nodiscard]] constexpr Quantity operator*(const quantity<D1, U1, Rep1>& lhs,
|
[[nodiscard]] constexpr Quantity operator*(const quantity<U1, Rep1>& lhs,
|
||||||
const quantity<D2, U2, Rep2>& rhs)
|
const quantity<U2, Rep2>& rhs)
|
||||||
requires treat_as_floating_point<decltype(lhs.count() * rhs.count())> ||
|
requires treat_as_floating_point<decltype(lhs.count() * rhs.count())> ||
|
||||||
(std::ratio_multiply<typename U1::ratio, typename U2::ratio>::den == 1)
|
(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 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());
|
return ret(lhs.count() * rhs.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
// template<Scalar Rep1, Dimension D, Unit U, Scalar Rep2>
|
// template<Scalar Rep1, Unit U, Scalar Rep2>
|
||||||
template<typename Rep1, typename D, typename U, typename Rep2>
|
template<typename Rep1, typename U, typename Rep2>
|
||||||
[[nodiscard]] constexpr Quantity operator/(const Rep1& v,
|
[[nodiscard]] constexpr Quantity operator/(const Rep1& v,
|
||||||
const quantity<D, U, Rep2>& q)
|
const quantity<U, Rep2>& q)
|
||||||
requires (!Quantity<Rep1>)
|
requires (!Quantity<Rep1>)
|
||||||
{
|
{
|
||||||
Expects(q != std::remove_cvref_t<decltype(q)>(0));
|
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 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 ret = quantity<downcasting_traits_t<unit<dim, ratio<U::ratio::den, U::ratio::num>>>, common_rep>;
|
||||||
using den = quantity<D, U, common_rep>;
|
using den = quantity<U, common_rep>;
|
||||||
return ret(v / den(q).count());
|
return ret(v / den(q).count());
|
||||||
}
|
}
|
||||||
|
|
||||||
// template<Dimension D, Unit U, Scalar Rep1, Scalar Rep2>
|
// template<Unit U, Scalar Rep1, Scalar Rep2>
|
||||||
template<typename D, typename U, typename Rep1, typename Rep2>
|
template<typename U, typename Rep1, typename Rep2>
|
||||||
[[nodiscard]] constexpr Quantity operator/(const quantity<D, U, Rep1>& q,
|
[[nodiscard]] constexpr Quantity operator/(const quantity<U, Rep1>& q,
|
||||||
const Rep2& v)
|
const Rep2& v)
|
||||||
requires (!Quantity<Rep2>)
|
requires (!Quantity<Rep2>)
|
||||||
{
|
{
|
||||||
Expects(v != Rep2{0});
|
Expects(v != Rep2{0});
|
||||||
|
|
||||||
using common_rep = decltype(q.count() / 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);
|
return ret(ret(q).count() / v);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
||||||
[[nodiscard]] constexpr Scalar operator/(const quantity<D, U1, Rep1>& lhs,
|
[[nodiscard]] constexpr Scalar operator/(const quantity<U1, Rep1>& lhs,
|
||||||
const quantity<D, U2, Rep2>& rhs)
|
const quantity<U2, Rep2>& rhs)
|
||||||
|
requires std::Same<typename U1::dimension, typename U2::dimension>
|
||||||
{
|
{
|
||||||
Expects(rhs != std::remove_cvref_t<decltype(rhs)>(0));
|
Expects(rhs != std::remove_cvref_t<decltype(rhs)>(0));
|
||||||
|
|
||||||
using common_rep = decltype(lhs.count() / rhs.count());
|
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();
|
return cq(lhs).count() / cq(rhs).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Dimension D1, Unit U1, Scalar Rep1, Dimension D2, Unit U2, Scalar Rep2>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
||||||
[[nodiscard]] constexpr Quantity operator/(const quantity<D1, U1, Rep1>& lhs,
|
[[nodiscard]] constexpr Quantity operator/(const quantity<U1, Rep1>& lhs,
|
||||||
const quantity<D2, U2, Rep2>& rhs)
|
const quantity<U2, Rep2>& rhs)
|
||||||
requires treat_as_floating_point<decltype(lhs.count() / rhs.count())> ||
|
requires (!std::Same<typename U1::dimension, typename U2::dimension>) &&
|
||||||
(ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1)
|
(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));
|
Expects(rhs != std::remove_cvref_t<decltype(rhs)>(0));
|
||||||
|
|
||||||
using common_rep = decltype(lhs.count() / rhs.count());
|
using common_rep = decltype(lhs.count() / rhs.count());
|
||||||
using dim = dimension_divide_t<D1, D2>;
|
using dim = dimension_divide_t<typename U1::dimension, typename U2::dimension>;
|
||||||
using ret = quantity<dim, downcasting_traits_t<unit<dim, ratio_divide<typename U1::ratio, typename U2::ratio>>>, common_rep>;
|
using ret = quantity<downcasting_traits_t<unit<dim, ratio_divide<typename U1::ratio, typename U2::ratio>>>, common_rep>;
|
||||||
return ret(lhs.count() / rhs.count());
|
return ret(lhs.count() / rhs.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Dimension D, Unit U, Scalar Rep1, Scalar Rep2>
|
template<Unit U, Scalar Rep1, Scalar Rep2>
|
||||||
[[nodiscard]] constexpr Quantity operator%(const quantity<D, U, Rep1>& q,
|
[[nodiscard]] constexpr Quantity operator%(const quantity<U, Rep1>& q,
|
||||||
const Rep2& v)
|
const Rep2& v)
|
||||||
{
|
{
|
||||||
using common_rep = decltype(q.count() % 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);
|
return ret(ret(q).count() % v);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
||||||
[[nodiscard]] constexpr Quantity operator%(const quantity<D, U1, Rep1>& lhs,
|
[[nodiscard]] constexpr Quantity operator%(const quantity<U1, Rep1>& lhs,
|
||||||
const quantity<D, U2, Rep2>& rhs)
|
const quantity<U2, Rep2>& rhs)
|
||||||
{
|
{
|
||||||
using common_rep = decltype(lhs.count() % rhs.count());
|
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());
|
return ret(ret(lhs).count() % ret(rhs).count());
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
||||||
[[nodiscard]] constexpr bool operator==(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
|
[[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();
|
return ct(lhs).count() == ct(rhs).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
||||||
[[nodiscard]] constexpr bool operator!=(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
|
[[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);
|
return !(lhs == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
||||||
[[nodiscard]] constexpr bool operator<(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
|
[[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();
|
return ct(lhs).count() < ct(rhs).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
||||||
[[nodiscard]] constexpr bool operator<=(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
|
[[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);
|
return !(rhs < lhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
||||||
[[nodiscard]] constexpr bool operator>(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
|
[[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;
|
return rhs < lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Dimension D, Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
template<Unit U1, Scalar Rep1, Unit U2, Scalar Rep2>
|
||||||
[[nodiscard]] constexpr bool operator>=(const quantity<D, U1, Rep1>& lhs, const quantity<D, U2, Rep2>& rhs)
|
[[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);
|
return !(lhs < rhs);
|
||||||
}
|
}
|
||||||
|
@ -33,17 +33,14 @@ namespace units {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
concept bool Substance = Quantity<T> && std::Same<typename T::dimension, dimension_substance>;
|
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> {};
|
struct mole : unit<dimension_substance> {};
|
||||||
template<> struct downcasting_traits<downcast_from<mole>> : downcast_to<mole> {};
|
template<> struct downcasting_traits<downcast_from<mole>> : downcast_to<mole> {};
|
||||||
|
|
||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
|
|
||||||
// mol
|
// mol
|
||||||
constexpr auto operator""_mol(unsigned long long l) { return substance<mole, std::int64_t>(l); }
|
constexpr auto operator""_mol(unsigned long long l) { return quantity<mole, std::int64_t>(l); }
|
||||||
constexpr auto operator""_mol(long double l) { return substance<mole, long double>(l); }
|
constexpr auto operator""_mol(long double l) { return quantity<mole, long double>(l); }
|
||||||
|
|
||||||
} // namespace literals
|
} // namespace literals
|
||||||
|
|
||||||
|
@ -33,17 +33,14 @@ namespace units {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
concept bool ThermodynamicTemperature = Quantity<T> && std::Same<typename T::dimension, dimension_temperature>;
|
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> {};
|
struct kelvin : unit<dimension_temperature> {};
|
||||||
template<> struct downcasting_traits<downcast_from<kelvin>> : downcast_to<kelvin> {};
|
template<> struct downcasting_traits<downcast_from<kelvin>> : downcast_to<kelvin> {};
|
||||||
|
|
||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
|
|
||||||
// K
|
// K
|
||||||
constexpr auto operator""_K(unsigned long long l) { return temperature<kelvin, std::int64_t>(l); }
|
constexpr auto operator""_K(unsigned long long l) { return quantity<kelvin, std::int64_t>(l); }
|
||||||
constexpr auto operator""_K(long double l) { return temperature<kelvin, long double>(l); }
|
constexpr auto operator""_K(long double l) { return quantity<kelvin, long double>(l); }
|
||||||
|
|
||||||
} // namespace literals
|
} // namespace literals
|
||||||
|
|
||||||
|
@ -33,9 +33,6 @@ namespace units {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
concept bool Time = Quantity<T> && std::Same<typename T::dimension, dimension_time>;
|
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> {};
|
struct second : unit<dimension_time> {};
|
||||||
template<> struct downcasting_traits<downcast_from<second>> : downcast_to<second> {};
|
template<> struct downcasting_traits<downcast_from<second>> : downcast_to<second> {};
|
||||||
|
|
||||||
@ -57,28 +54,28 @@ namespace units {
|
|||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
|
|
||||||
// ns
|
// ns
|
||||||
constexpr auto operator""_ns(unsigned long long l) { return time<nanosecond, std::int64_t>(l); }
|
constexpr auto operator""_ns(unsigned long long l) { return quantity<nanosecond, std::int64_t>(l); }
|
||||||
constexpr auto operator""_ns(long double l) { return time<nanosecond, long double>(l); }
|
constexpr auto operator""_ns(long double l) { return quantity<nanosecond, long double>(l); }
|
||||||
|
|
||||||
// us
|
// us
|
||||||
constexpr auto operator""_us(unsigned long long l) { return time<microsecond, std::int64_t>(l); }
|
constexpr auto operator""_us(unsigned long long l) { return quantity<microsecond, std::int64_t>(l); }
|
||||||
constexpr auto operator""_us(long double l) { return time<microsecond, long double>(l); }
|
constexpr auto operator""_us(long double l) { return quantity<microsecond, long double>(l); }
|
||||||
|
|
||||||
// ms
|
// ms
|
||||||
constexpr auto operator""_ms(unsigned long long l) { return time<millisecond, std::int64_t>(l); }
|
constexpr auto operator""_ms(unsigned long long l) { return quantity<millisecond, std::int64_t>(l); }
|
||||||
constexpr auto operator""_ms(long double l) { return time<millisecond, long double>(l); }
|
constexpr auto operator""_ms(long double l) { return quantity<millisecond, long double>(l); }
|
||||||
|
|
||||||
// s
|
// s
|
||||||
constexpr auto operator""_s(unsigned long long l) { return time<second, std::int64_t>(l); }
|
constexpr auto operator""_s(unsigned long long l) { return quantity<second, std::int64_t>(l); }
|
||||||
constexpr auto operator""_s(long double l) { return time<second, long double>(l); }
|
constexpr auto operator""_s(long double l) { return quantity<second, long double>(l); }
|
||||||
|
|
||||||
// min
|
// min
|
||||||
constexpr auto operator""_min(unsigned long long l) { return time<minute, std::int64_t>(l); }
|
constexpr auto operator""_min(unsigned long long l) { return quantity<minute, std::int64_t>(l); }
|
||||||
constexpr auto operator""_min(long double l) { return time<minute, long double>(l); }
|
constexpr auto operator""_min(long double l) { return quantity<minute, long double>(l); }
|
||||||
|
|
||||||
// h
|
// h
|
||||||
constexpr auto operator""_h(unsigned long long l) { return time<hour, std::int64_t>(l); }
|
constexpr auto operator""_h(unsigned long long l) { return quantity<hour, std::int64_t>(l); }
|
||||||
constexpr auto operator""_h(long double l) { return time<hour, long double>(l); }
|
constexpr auto operator""_h(long double l) { return quantity<hour, long double>(l); }
|
||||||
|
|
||||||
} // namespace literals
|
} // namespace literals
|
||||||
|
|
||||||
|
@ -33,9 +33,6 @@ namespace units {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
concept bool Velocity = Quantity<T> && std::Same<typename T::dimension, dimension_velocity>;
|
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> {};
|
struct meter_per_second : derived_unit<dimension_velocity, meter, second> {};
|
||||||
template<> struct downcasting_traits<downcast_from<meter_per_second>> : downcast_to<meter_per_second> {};
|
template<> struct downcasting_traits<downcast_from<meter_per_second>> : downcast_to<meter_per_second> {};
|
||||||
|
|
||||||
@ -48,16 +45,16 @@ namespace units {
|
|||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
|
|
||||||
// mps
|
// mps
|
||||||
constexpr auto operator""_mps(unsigned long long l) { return velocity<meter_per_second, std::int64_t>(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 velocity<meter_per_second, long double>(l); }
|
constexpr auto operator""_mps(long double l) { return quantity<meter_per_second, long double>(l); }
|
||||||
|
|
||||||
// kmph
|
// kmph
|
||||||
constexpr auto operator""_kmph(unsigned long long l) { return velocity<kilometer_per_hour, std::int64_t>(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 velocity<kilometer_per_hour, long double>(l); }
|
constexpr auto operator""_kmph(long double l) { return quantity<kilometer_per_hour, long double>(l); }
|
||||||
|
|
||||||
// mph
|
// mph
|
||||||
constexpr auto operator""_mph(unsigned long long l) { return velocity<mile_per_hour, std::int64_t>(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 velocity<mile_per_hour, long double>(l); }
|
constexpr auto operator""_mph(long double l) { return quantity<mile_per_hour, long double>(l); }
|
||||||
|
|
||||||
} // namespace literals
|
} // namespace literals
|
||||||
|
|
||||||
|
@ -77,76 +77,76 @@ namespace {
|
|||||||
// class invariants
|
// class invariants
|
||||||
|
|
||||||
// constexpr quantity<dimension_length, second, int> q; // should a static_assert
|
// 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<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
|
// constexpr quantity<dimension_length, unit<dimension_length, std::ratio<-1, 1>>, int> error(0); // should trigger a static_assert
|
||||||
|
|
||||||
// member types
|
// member types
|
||||||
|
|
||||||
static_assert(std::is_same_v<length<meter, int>::rep, int>);
|
static_assert(std::is_same_v<quantity<meter, int>::rep, int>);
|
||||||
static_assert(std::is_same_v<length<meter, double>::rep, double>);
|
static_assert(std::is_same_v<quantity<meter, double>::rep, double>);
|
||||||
static_assert(std::is_same_v<length<meter, int>::unit, meter>);
|
static_assert(std::is_same_v<quantity<meter, int>::unit, meter>);
|
||||||
static_assert(std::is_same_v<length<kilometer, int>::unit, kilometer>);
|
static_assert(std::is_same_v<quantity<kilometer, int>::unit, kilometer>);
|
||||||
|
|
||||||
// constructors
|
// constructors
|
||||||
|
|
||||||
static_assert(length<meter, int>().count() == 0);
|
static_assert(quantity<meter, int>().count() == 0);
|
||||||
constexpr length<meter, int> km{1000};
|
constexpr quantity<meter, int> km{1000};
|
||||||
static_assert(km.count() == 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(quantity<meter, int>(1).count() == 1);
|
||||||
static_assert(length<meter, int>(my_value(1)).count() == 1);
|
static_assert(quantity<meter, int>(my_value(1)).count() == 1);
|
||||||
static_assert(length<meter, my_value<int>>(1).count() == 1);
|
static_assert(quantity<meter, my_value<int>>(1).count() == 1);
|
||||||
// static_assert(length<meter, int>(1.0).count() == 1); // should not compile
|
// static_assert(quantity<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(quantity<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(quantity<meter, my_value>(1.0).count() == 1); // should not compile
|
||||||
static_assert(length<meter, double>(1.0).count() == 1.0);
|
static_assert(quantity<meter, double>(1.0).count() == 1.0);
|
||||||
static_assert(length<meter, double>(my_value(1.0)).count() == 1.0);
|
static_assert(quantity<meter, double>(my_value(1.0)).count() == 1.0);
|
||||||
static_assert(length<meter, double>(1).count() == 1.0);
|
static_assert(quantity<meter, double>(1).count() == 1.0);
|
||||||
static_assert(length<meter, double>(my_value(1)).count() == 1.0);
|
static_assert(quantity<meter, double>(my_value(1)).count() == 1.0);
|
||||||
static_assert(length<meter, double>(3.14).count() == 3.14);
|
static_assert(quantity<meter, double>(3.14).count() == 3.14);
|
||||||
static_assert(length<meter, my_value<double>>(1.0).count() == 1.0);
|
static_assert(quantity<meter, my_value<double>>(1.0).count() == 1.0);
|
||||||
static_assert(length<meter, my_value<double>>(1).count() == 1.0);
|
static_assert(quantity<meter, my_value<double>>(1).count() == 1.0);
|
||||||
static_assert(length<meter, my_value<double>>(3.14).count() == 3.14);
|
static_assert(quantity<meter, my_value<double>>(3.14).count() == 3.14);
|
||||||
|
|
||||||
static_assert(length<meter, int>(km).count() == 1000);
|
static_assert(quantity<meter, int>(km).count() == 1000);
|
||||||
// static_assert(length<meter, int>(length<meter, double>(3.14)).count() == 3); // should not compile
|
// static_assert(quantity<meter, int>(quantity<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(quantity<meter, int>(quantity_cast<quantity<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(quantity<meter, int>(quantity<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(quantity<meter, my_value>(1000.0_m).count() == 1000); // should not compile
|
||||||
static_assert(length<meter, double>(1000.0_m).count() == 1000.0);
|
static_assert(quantity<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(quantity<meter, double>(quantity<meter, my_value<double>>(1000.0)).count() == 1000.0);
|
||||||
static_assert(length<meter, my_value<double>>(1000.0_m).count() == 1000.0);
|
static_assert(quantity<meter, my_value<double>>(1000.0_m).count() == 1000.0);
|
||||||
static_assert(length<meter, double>(km).count() == 1000.0);
|
static_assert(quantity<meter, double>(km).count() == 1000.0);
|
||||||
static_assert(length<meter, my_value<double>>(km).count() == 1000.0);
|
static_assert(quantity<meter, my_value<double>>(km).count() == 1000.0);
|
||||||
static_assert(length<meter, int>(1_km).count() == 1000);
|
static_assert(quantity<meter, int>(1_km).count() == 1000);
|
||||||
// static_assert(length<meter, int>(1_s).count() == 1); // should not compile
|
// static_assert(quantity<meter, int>(1_s).count() == 1); // should not compile
|
||||||
// static_assert(length<kilometer, int>(1010_m).count() == 1); // should not compile
|
// static_assert(quantity<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<kilometer, int>(quantity_cast<quantity<kilometer, my_value<int>>>(1010_m)).count() == 1);
|
||||||
|
|
||||||
// assignment operator
|
// assignment operator
|
||||||
|
|
||||||
static_assert([]() {
|
static_assert([]() {
|
||||||
length<meter, int> l1(1), l2(2);
|
quantity<meter, int> l1(1), l2(2);
|
||||||
return l2 = l1;
|
return l2 = l1;
|
||||||
}()
|
}()
|
||||||
.count() == 1);
|
.count() == 1);
|
||||||
|
|
||||||
// static member functions
|
// static member functions
|
||||||
|
|
||||||
static_assert(length<meter, int>::zero().count() == 0);
|
static_assert(quantity<meter, int>::zero().count() == 0);
|
||||||
static_assert(length<meter, int>::min().count() == std::numeric_limits<int>::lowest());
|
static_assert(quantity<meter, int>::min().count() == std::numeric_limits<int>::lowest());
|
||||||
static_assert(length<meter, int>::max().count() == std::numeric_limits<int>::max());
|
static_assert(quantity<meter, int>::max().count() == std::numeric_limits<int>::max());
|
||||||
static_assert(length<meter, double>::zero().count() == 0.0);
|
static_assert(quantity<meter, double>::zero().count() == 0.0);
|
||||||
static_assert(length<meter, double>::min().count() == std::numeric_limits<double>::lowest());
|
static_assert(quantity<meter, double>::min().count() == std::numeric_limits<double>::lowest());
|
||||||
static_assert(length<meter, double>::max().count() == std::numeric_limits<double>::max());
|
static_assert(quantity<meter, double>::max().count() == std::numeric_limits<double>::max());
|
||||||
static_assert(length<meter, my_value<int>>::zero().count() == 0);
|
static_assert(quantity<meter, my_value<int>>::zero().count() == 0);
|
||||||
static_assert(length<meter, my_value<int>>::min().count() == std::numeric_limits<int>::lowest());
|
static_assert(quantity<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(quantity<meter, my_value<int>>::max().count() == std::numeric_limits<int>::max());
|
||||||
static_assert(length<meter, my_value<double>>::zero().count() == 0.0);
|
static_assert(quantity<meter, my_value<double>>::zero().count() == 0.0);
|
||||||
static_assert(length<meter, my_value<double>>::min().count() == std::numeric_limits<double>::lowest());
|
static_assert(quantity<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, my_value<double>>::max().count() == std::numeric_limits<double>::max());
|
||||||
|
|
||||||
// unary member operators
|
// unary member operators
|
||||||
|
|
||||||
@ -160,19 +160,19 @@ namespace {
|
|||||||
static_assert([](auto v) {
|
static_assert([](auto v) {
|
||||||
auto vv = v++;
|
auto vv = v++;
|
||||||
return std::make_pair(v, vv);
|
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) {
|
static_assert([](auto v) {
|
||||||
auto vv = ++v;
|
auto vv = ++v;
|
||||||
return std::make_pair(v, vv);
|
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) {
|
static_assert([](auto v) {
|
||||||
auto vv = v--;
|
auto vv = v--;
|
||||||
return std::make_pair(v, vv);
|
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) {
|
static_assert([](auto v) {
|
||||||
auto vv = --v;
|
auto vv = --v;
|
||||||
return std::make_pair(v, vv);
|
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
|
// compound assignment
|
||||||
|
|
||||||
@ -185,21 +185,21 @@ namespace {
|
|||||||
|
|
||||||
// non-member arithmetic operators
|
// 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(quantity<meter, int>() + quantity<meter, double>()), quantity<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(quantity<meter, int>() + quantity<meter, double>()), quantity<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(quantity<kilometer, int>() + quantity<meter, double>()), quantity<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(quantity<meter, double>() - quantity<meter, int>()), quantity<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(quantity<kilometer, double>() - quantity<meter, int>()), quantity<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(quantity<meter, int>() * 1.0), quantity<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(1.0 * quantity<meter, int>()), quantity<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(quantity<meter_per_second, int>() * units::quantity<second, int>()), quantity<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(1 / units::quantity<second, int>()), quantity<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(quantity<meter, int>() / 1.0), quantity<meter, double>>);
|
||||||
static_assert(std::is_same_v<decltype(length<meter, int>() / length<meter, double>()), double>);
|
static_assert(std::is_same_v<decltype(quantity<meter, int>() / quantity<meter, double>()), double>);
|
||||||
static_assert(std::is_same_v<decltype(length<kilometer, int>() / length<meter, double>()), double>);
|
static_assert(std::is_same_v<decltype(quantity<kilometer, int>() / quantity<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(quantity<meter, int>() / units::quantity<second, int>()), quantity<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(quantity<meter, int>() % short(1)), quantity<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, short>(1)), quantity<meter, int>>);
|
||||||
|
|
||||||
static_assert((1_m + km).count() == 1001);
|
static_assert((1_m + km).count() == 1001);
|
||||||
static_assert((1_m + 1_km).count() == 1001);
|
static_assert((1_m + 1_km).count() == 1001);
|
||||||
@ -247,19 +247,19 @@ namespace {
|
|||||||
|
|
||||||
// is_quantity
|
// is_quantity
|
||||||
|
|
||||||
static_assert(Quantity<length<millimeter, int>>);
|
static_assert(Quantity<quantity<millimeter, int>>);
|
||||||
|
|
||||||
// common_quantity
|
// 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<quantity<meter, int>, quantity<kilometer, int>>, quantity<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<quantity<kilometer, long long>, quantity<meter, int>>, quantity<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<kilometer, long long>, quantity<millimeter, double>>, quantity<millimeter, double>>);
|
||||||
|
|
||||||
// quantity_cast
|
// quantity_cast
|
||||||
|
|
||||||
// static_assert(quantity_cast<int>(2_km).count() == 2000); // should not compile
|
// 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<quantity<meter, int>>(2_km).count() == 2000);
|
||||||
static_assert(quantity_cast<length<kilometer, int>>(2000_m).count() == 2);
|
static_assert(quantity_cast<quantity<kilometer, int>>(2000_m).count() == 2);
|
||||||
|
|
||||||
// time
|
// time
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ namespace {
|
|||||||
|
|
||||||
// velocity
|
// 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_m / 5_s == 2_mps);
|
||||||
static_assert(10 / 5_s * 1_m == 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(2_km / 2_kmph == 1_h);
|
||||||
// static_assert(2000_m / 2_kmph == 1_h); // should not compile
|
// 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
|
// area
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user