mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-01 03:14:29 +02:00
Synthesizing unit symbols in deduced_derived_unit support added (resolves #13)
This commit is contained in:
@@ -173,7 +173,7 @@ described later in this document.
|
||||
So for example to create a `velocity` type we have to do:
|
||||
|
||||
```cpp
|
||||
struct velocity : derived_dimension<velocity, exp<base_dim_length, 1>, exp<base_dim_time, -1>> {};
|
||||
struct velocity : derived_dimension<velocity, exp<length, 1>, exp<time, -1>> {};
|
||||
```
|
||||
|
||||
In order to make `derived_dimension` work as expected it has to provide unique ordering for
|
||||
@@ -182,8 +182,11 @@ contained base dimensions. Beside providing ordering to base dimensions it also
|
||||
- eliminate two arguments of the same base dimension and with opposite equal exponents
|
||||
|
||||
`derived_dimension` is also able to form a dimension type based not only on base dimensions but
|
||||
it can take other derived dimensions as well. So for some more complex dimensions user can
|
||||
type either:
|
||||
it can take other derived dimensions as well. In such a case units defined with a
|
||||
`deduced_derived_unit` helper will get symbols of units for those derived dimension rather
|
||||
than system base units.
|
||||
|
||||
For example to form `pressure` user can provide the following two definitions:
|
||||
|
||||
```cpp
|
||||
struct pressure : derived_dimension<pressure, exp<base_dim_mass, 1>, exp<base_dim_length, -1>, exp<base_dim_time, -2>> {};
|
||||
@@ -196,8 +199,9 @@ struct pressure : derived_dimension<pressure, exp<force, 1>, exp<area, -1>> {};
|
||||
```
|
||||
|
||||
In the second case `derived_dimension` will extract all derived dimensions into the list of
|
||||
exponents of base dimensions. Thanks to that both cases will result with exactly the same base
|
||||
class formed only from the exponents of base units.
|
||||
exponents of base dimensions. Thanks to this both cases will result with exactly the same base
|
||||
class formed only from the exponents of base units but in the second case the recipe to form
|
||||
a derived dimension will be remembered and used for `deduced_derived_unit` usage.
|
||||
|
||||
#### `merge_dimension`
|
||||
|
||||
@@ -361,13 +365,27 @@ struct deduced_derived_unit : downcast_child<Child, /* magic to get the correct
|
||||
```
|
||||
|
||||
This will deduce the ratio based on the ingredient units and their relation defined in the
|
||||
dimension and in case the symbol name is not explicitly provided it with create one based
|
||||
on the provided ingredients:
|
||||
dimension. For example to create a deduced velocity unit a user can do:
|
||||
|
||||
```cpp
|
||||
struct kilometre_per_hour : deduced_derived_unit<kilometre_per_hour, velocity, kilometre, hour> {};
|
||||
```
|
||||
|
||||
`deduced_derived_unit` has also one more important feature. It is able to synthesize a unit
|
||||
symbol:
|
||||
- in case all units on the list are the units of base dimension (i.e. above we have a `kilometre`
|
||||
that is a unit of a base dimension `length`, and `hour` a unit of base dimension `time`),
|
||||
the resulting unit symbol will be created using base dimensions (`km/h`).
|
||||
- if at least one non-base dimension unit exists in a list than the recipe provided in the
|
||||
`derived_dimension` will be used to create a unit. For example:
|
||||
|
||||
```cpp
|
||||
struct surface_tension : derived_dimension<surface_tension, exp<force, 1>, exp<length, -1>> {};
|
||||
struct newton_per_centimetre : deduced_derived_unit<newton_per_centimetre, surface_tension, newton, centimetre> {};
|
||||
```
|
||||
|
||||
will result with a symbol `N/cm` for `newton_per_centimetre`.
|
||||
|
||||
|
||||
### `Quantities`
|
||||
|
||||
|
@@ -223,7 +223,9 @@ namespace units {
|
||||
|
||||
// derived_dimension
|
||||
template<typename Child, Exponent... Es>
|
||||
struct derived_dimension : downcast_child<Child, typename detail::make_dimension<Es...>> {};
|
||||
struct derived_dimension : downcast_child<Child, typename detail::make_dimension<Es...>> {
|
||||
using recipe = dimension<Es...>;
|
||||
};
|
||||
|
||||
// merge_dimension
|
||||
template<Dimension D1, Dimension D2>
|
||||
|
@@ -34,7 +34,7 @@ namespace units {
|
||||
struct cubic_metre : coherent_derived_unit<cubic_metre, volume> {};
|
||||
struct cubic_millimetre : deduced_derived_unit<cubic_millimetre, volume, millimetre> {};
|
||||
struct cubic_centimetre : deduced_derived_unit<cubic_centimetre, volume, centimetre> {};
|
||||
struct cubic_kilometre : deduced_derived_unit<cubic_kilometre, volume, kilometre, metre> {};
|
||||
struct cubic_kilometre : deduced_derived_unit<cubic_kilometre, volume, kilometre> {};
|
||||
struct cubic_foot : deduced_derived_unit<cubic_foot, volume, foot> {};
|
||||
|
||||
inline namespace literals {
|
||||
|
@@ -66,18 +66,6 @@ namespace units {
|
||||
using dimension = E::dimension;
|
||||
};
|
||||
|
||||
template<BaseDimension BD, typename... Us>
|
||||
struct get_ratio {
|
||||
using ratio = ::units::ratio<1>;
|
||||
};
|
||||
|
||||
template<BaseDimension BD, typename U, typename... Rest>
|
||||
struct get_ratio<BD, U, Rest...> {
|
||||
using unit_base_dim = get_unit_base_dim<typename U::dimension::base_type>::dimension;
|
||||
using ratio = conditional<std::is_same_v<unit_base_dim, BD>, typename U::ratio,
|
||||
typename get_ratio<BD, Rest...>::ratio>;
|
||||
};
|
||||
|
||||
template<typename Result, int UnitExpNum, int UnitExpDen, typename UnitRatio>
|
||||
struct ratio_op;
|
||||
|
||||
@@ -102,19 +90,30 @@ namespace units {
|
||||
using ratio = ::units::ratio<1>;
|
||||
};
|
||||
|
||||
template<typename E, typename... Rest, typename... Us>
|
||||
struct derived_ratio<dimension<E, Rest...>, Us...> {
|
||||
using rest_ratio = derived_ratio<dimension<Rest...>, Us...>::ratio;
|
||||
using e_ratio = get_ratio<typename E::dimension, Us...>::ratio;
|
||||
using ratio = ratio_op<rest_ratio, E::num, E::den, e_ratio>::ratio;
|
||||
template<typename E, typename... ERest, typename U, typename... URest>
|
||||
struct derived_ratio<dimension<E, ERest...>, U, URest...> {
|
||||
static_assert(same_dim<typename E::dimension, typename U::dimension>, "The order and number of units in `deduced_derived_unit<Us...>` should match dimensions provided in a `derived_dimension<>`");
|
||||
static_assert(sizeof...(ERest) == sizeof...(URest), "The number of `deduced_derived_unit<Us...>` units should match the number of exponents provided to `derived_dimension<>`");
|
||||
using rest_ratio = derived_ratio<dimension<ERest...>, URest...>::ratio;
|
||||
using ratio = ratio_op<rest_ratio, E::num, E::den, typename U::ratio>::ratio;
|
||||
};
|
||||
|
||||
template<typename... Es>
|
||||
constexpr auto exp_count(dimension<Es...>)
|
||||
{
|
||||
return sizeof...(Es);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline constexpr bool is_unit_of_base_dimension = (exp_count(typename U::dimension::base_type()) == 1);
|
||||
|
||||
template<Unit... Us>
|
||||
inline constexpr bool are_units_of_base_dimension = (is_unit_of_base_dimension<Us> && ...);
|
||||
|
||||
template<Dimension D, Unit... Us>
|
||||
using deduced_derived_unit = unit<D, typename detail::derived_ratio<typename D::base_type, Us...>::ratio>;
|
||||
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
using deduced_derived_unit =
|
||||
unit<D, typename detail::derived_ratio<std::conditional_t<are_units_of_base_dimension<Us...>,
|
||||
typename D::base_type, typename D::recipe>, Us...>::ratio>;
|
||||
|
||||
template<int Value>
|
||||
requires (0 <= Value) && (Value < 10)
|
||||
@@ -247,7 +246,7 @@ namespace units {
|
||||
template<typename E, typename U, std::size_t Idx>
|
||||
constexpr auto exp_validate_and_text()
|
||||
{
|
||||
static_assert(same_dim<typename E::dimension, typename U::dimension>);
|
||||
static_assert(same_dim<typename E::dimension, typename U::dimension>, "The order and number of units in `deduced_derived_unit<Us...>` should match dimensions provided in a `derived_dimension<>`");
|
||||
return exp_text<E, U::symbol, Idx>();
|
||||
}
|
||||
|
||||
@@ -260,10 +259,19 @@ namespace units {
|
||||
template<typename... Us, typename... Es>
|
||||
constexpr auto deduced_symbol_text(dimension<Es...> d)
|
||||
{
|
||||
static_assert(sizeof...(Us) == sizeof...(Es), "`deduced_derived_unit<Us...>` should get the same number of exponents as provided to `derived_dimension<>`");
|
||||
static_assert(sizeof...(Es) == sizeof...(Us), "The number of `deduced_derived_unit<Us...>` units should match the number of exponents provided to `derived_dimension<>`");
|
||||
return deduced_symbol_text_impl<Us...>(d, std::index_sequence_for<Es...>());
|
||||
}
|
||||
|
||||
template<typename Dim, typename... Us>
|
||||
constexpr auto deduced_symbol_text()
|
||||
{
|
||||
if constexpr(are_units_of_base_dimension<Us...>)
|
||||
return deduced_symbol_text<Us...>(typename Dim::base_type());
|
||||
else
|
||||
return deduced_symbol_text<Us...>(typename Dim::recipe());
|
||||
}
|
||||
|
||||
template<typename Unit>
|
||||
constexpr auto unit_text()
|
||||
{
|
||||
@@ -319,7 +327,7 @@ namespace units {
|
||||
|
||||
template<typename Child, Dimension Dim, Unit U, Unit... Us>
|
||||
struct deduced_derived_unit : downcast_child<Child, detail::deduced_derived_unit<Dim, U, Us...>> {
|
||||
static constexpr auto symbol = detail::deduced_symbol_text<U, Us...>(Dim());
|
||||
static constexpr auto symbol = detail::deduced_symbol_text<Dim, U, Us...>();
|
||||
using prefix_type = no_prefix;
|
||||
};
|
||||
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include "units/dimensions/power.h"
|
||||
#include "units/dimensions/velocity.h"
|
||||
#include "units/dimensions/volume.h"
|
||||
#include "units/dimensions/surface_tension.h"
|
||||
#include "units/format.h"
|
||||
#include "units/math.h"
|
||||
#include <catch2/catch.hpp>
|
||||
@@ -220,12 +221,13 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]")
|
||||
|
||||
SECTION("surface tension")
|
||||
{
|
||||
const auto q = 20.N / 2m;
|
||||
struct newton_per_centimetre : deduced_derived_unit<newton_per_centimetre, surface_tension, newton, centimetre> {};
|
||||
const quantity<newton_per_centimetre> q(123);
|
||||
stream << q;
|
||||
|
||||
SECTION("iostream")
|
||||
{
|
||||
REQUIRE(stream.str() == "10 N/m");
|
||||
REQUIRE(stream.str() == "123 N/cm");
|
||||
}
|
||||
|
||||
SECTION("fmt with default format {} on a quantity")
|
||||
|
Reference in New Issue
Block a user