Synthesizing unit symbols in deduced_derived_unit support added (resolves #13)

This commit is contained in:
Mateusz Pusz
2019-11-10 16:55:16 +00:00
parent c2e5532ae2
commit 9a96235e6c
5 changed files with 66 additions and 36 deletions

View File

@@ -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`

View File

@@ -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>

View File

@@ -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 {

View File

@@ -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;
};

View File

@@ -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")