diff --git a/doc/DESIGN.md b/doc/DESIGN.md index c1a35c85..88b85fc4 100644 --- a/doc/DESIGN.md +++ b/doc/DESIGN.md @@ -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, exp> {}; +struct velocity : derived_dimension, exp> {}; ``` 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, exp, exp> {}; @@ -196,8 +199,9 @@ struct pressure : derived_dimension, exp> {}; ``` 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 {}; ``` +`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, exp> {}; + struct newton_per_centimetre : deduced_derived_unit {}; + ``` + + will result with a symbol `N/cm` for `newton_per_centimetre`. + ### `Quantities` diff --git a/src/include/units/dimension.h b/src/include/units/dimension.h index 2ff0ca49..2e795d14 100644 --- a/src/include/units/dimension.h +++ b/src/include/units/dimension.h @@ -223,7 +223,9 @@ namespace units { // derived_dimension template - struct derived_dimension : downcast_child> {}; + struct derived_dimension : downcast_child> { + using recipe = dimension; + }; // merge_dimension template diff --git a/src/include/units/dimensions/volume.h b/src/include/units/dimensions/volume.h index bdd549d4..9cf72b53 100644 --- a/src/include/units/dimensions/volume.h +++ b/src/include/units/dimensions/volume.h @@ -34,7 +34,7 @@ namespace units { struct cubic_metre : coherent_derived_unit {}; struct cubic_millimetre : deduced_derived_unit {}; struct cubic_centimetre : deduced_derived_unit {}; - struct cubic_kilometre : deduced_derived_unit {}; + struct cubic_kilometre : deduced_derived_unit {}; struct cubic_foot : deduced_derived_unit {}; inline namespace literals { diff --git a/src/include/units/unit.h b/src/include/units/unit.h index 62aa9f75..53696969 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -66,18 +66,6 @@ namespace units { using dimension = E::dimension; }; - template - struct get_ratio { - using ratio = ::units::ratio<1>; - }; - - template - struct get_ratio { - using unit_base_dim = get_unit_base_dim::dimension; - using ratio = conditional, typename U::ratio, - typename get_ratio::ratio>; - }; - template struct ratio_op; @@ -102,19 +90,30 @@ namespace units { using ratio = ::units::ratio<1>; }; - template - struct derived_ratio, Us...> { - using rest_ratio = derived_ratio, Us...>::ratio; - using e_ratio = get_ratio::ratio; - using ratio = ratio_op::ratio; + template + struct derived_ratio, U, URest...> { + static_assert(same_dim, "The order and number of units in `deduced_derived_unit` should match dimensions provided in a `derived_dimension<>`"); + static_assert(sizeof...(ERest) == sizeof...(URest), "The number of `deduced_derived_unit` units should match the number of exponents provided to `derived_dimension<>`"); + using rest_ratio = derived_ratio, URest...>::ratio; + using ratio = ratio_op::ratio; }; + template + constexpr auto exp_count(dimension) + { + return sizeof...(Es); + } + + template + inline constexpr bool is_unit_of_base_dimension = (exp_count(typename U::dimension::base_type()) == 1); + + template + inline constexpr bool are_units_of_base_dimension = (is_unit_of_base_dimension && ...); + template - using deduced_derived_unit = unit::ratio>; - - } - - namespace detail { + using deduced_derived_unit = + unit, + typename D::base_type, typename D::recipe>, Us...>::ratio>; template requires (0 <= Value) && (Value < 10) @@ -247,7 +246,7 @@ namespace units { template constexpr auto exp_validate_and_text() { - static_assert(same_dim); + static_assert(same_dim, "The order and number of units in `deduced_derived_unit` should match dimensions provided in a `derived_dimension<>`"); return exp_text(); } @@ -260,10 +259,19 @@ namespace units { template constexpr auto deduced_symbol_text(dimension d) { - static_assert(sizeof...(Us) == sizeof...(Es), "`deduced_derived_unit` should get the same number of exponents as provided to `derived_dimension<>`"); + static_assert(sizeof...(Es) == sizeof...(Us), "The number of `deduced_derived_unit` units should match the number of exponents provided to `derived_dimension<>`"); return deduced_symbol_text_impl(d, std::index_sequence_for()); } + template + constexpr auto deduced_symbol_text() + { + if constexpr(are_units_of_base_dimension) + return deduced_symbol_text(typename Dim::base_type()); + else + return deduced_symbol_text(typename Dim::recipe()); + } + template constexpr auto unit_text() { @@ -319,7 +327,7 @@ namespace units { template struct deduced_derived_unit : downcast_child> { - static constexpr auto symbol = detail::deduced_symbol_text(Dim()); + static constexpr auto symbol = detail::deduced_symbol_text(); using prefix_type = no_prefix; }; diff --git a/test/unit_test/runtime/text_test.cpp b/test/unit_test/runtime/text_test.cpp index 8a5dfa1c..8c324915 100644 --- a/test/unit_test/runtime/text_test.cpp +++ b/test/unit_test/runtime/text_test.cpp @@ -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 @@ -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 {}; + const quantity 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")