diff --git a/doc/DESIGN.md b/doc/DESIGN.md index 90514804..00a83039 100644 --- a/doc/DESIGN.md +++ b/doc/DESIGN.md @@ -157,7 +157,7 @@ helper: ```cpp template -struct derived_dimension : downcast_helper::type> {}; +struct derived_dimension : downcast_child::type> {}; ``` `Child` class template parameter is a part of a CRTP idiom and is used to provide a downcasting facility @@ -244,7 +244,7 @@ template: ```cpp template -struct coherent_derived_unit : downcast_helper>> { +struct coherent_derived_unit : downcast_child>> { static constexpr auto symbol = Symbol; using prefix_type = PrefixType; }; @@ -267,7 +267,7 @@ To create the rest of derived units the following class template can be used: ```cpp template -struct derived_unit : downcast_helper> { +struct derived_unit : downcast_child> { static constexpr auto symbol = Symbol; }; ``` @@ -286,7 +286,7 @@ For example to create a prefixed unit the following may be used: ```cpp template requires requires { U::symbol; } -struct prefixed_derived_unit : downcast_helper>> { static constexpr auto symbol = P::symbol + U::symbol; @@ -298,7 +298,7 @@ where `Prefix` is a concept requiring the instantiation of the following class t ```cpp template -struct prefix : downcast_helper> { +struct prefix : downcast_child> { static constexpr auto symbol = Symbol; }; ``` @@ -322,7 +322,7 @@ For the cases where determining the exact ratio is not trivial another helper ca ```cpp template -struct deduced_derived_unit : downcast_helper> { +struct deduced_derived_unit : downcast_child> { static constexpr auto symbol = Symbol; }; ``` @@ -541,20 +541,31 @@ and are not arguably much easier to understand thus provide better user experience. -Downcasting facility provides a type substitution mechanism. It connects a specific primary template -class specialization with a strong type assigned to it by the user. A simplified mental model of the -facility may be represented as: +When dealing with simple types, aliases can be easily replaced with inheritance: -```cpp -struct metre : unit>, std::ratio<1, 1, 0>>; -``` +![UML](downcast_1.png) + +As a result we get strong types. There are however a few issues with such an approach: +- generic code getting a child class does not easily know the exact template parameters of + the base class +- generic code after computing the instantiation of the class template does not know if + this is a base class in some hierarchy, and in case it is, it does not know how to + replace the base class template instantiation with a derived strong type. + +Downcasting facility provides such a type substitution mechanism. It connects a specific primary +template class instantiation with a strong type assigned to it by the user. + +Here is the overview of resulting class hierarchy for our example: + +![UML](downcast_2.png) In the above example `metre` is a downcasting target (child class) and a specific `unit` class -template specialization is a downcasting source (base class). The downcasting facility provides -1 to 1 tpe substitution mechanism. Only one child class can be created for a specific base class +template instantiation is a downcasting source (base class). The downcasting facility provides +1 to 1 type substitution mechanism. Only one child class can be created for a specific base class template instantiation. -Downcasting facility is provided through 2 dedicated types, a concept, and a few helper template aliases. +Downcasting facility is provided through 2 dedicated types, a concept, and a few helper template +aliases. ```cpp template @@ -568,7 +579,7 @@ struct downcast_base { facility with a `base_type` member type, and provides a declaration of downcasting ADL friendly (Hidden Friend) entry point member function `downcast_guide`. An important design point is that this function does not return any specific type in its declaration. This non-member function -is going to be defined in a child class template `downcast_helper` and will return a target +is going to be defined in a child class template `downcast_child` and will return a target type of the downcasting operation there. ```cpp @@ -585,24 +596,24 @@ facility. ```cpp template -struct downcast_helper : T { - friend auto downcast_guide(typename downcast_helper::downcast_base) { return Target(); } +struct downcast_child : T { + friend auto downcast_guide(typename downcast_child::downcast_base) { return Target(); } }; ``` -`units::downcast_helper` is another CRTP class template that provides the implementation of a +`units::downcast_child` is another CRTP class template that provides the implementation of a non-member friend function of the `downcast_base` class template which defines the target type of a downcasting operation. It is used in the following way to define `dimension` and `unit` types in the library: ```cpp template -struct derived_dimension : downcast_helper> {}; +struct derived_dimension : downcast_child> {}; ``` ```cpp template -struct derived_unit : downcast_helper>> {}; +struct derived_unit : downcast_child>> {}; ``` With such CRTP types the only thing the user has to do to register a new type to the downcasting @@ -618,18 +629,18 @@ downcasting operation a dedicated template alias is provided: ```cpp template -using downcast_target = decltype(detail::downcast_target_impl()); +using downcast = decltype(detail::downcast_target_impl()); ``` -`units::downcast_target` is used to obtain the target type of the downcasting operation registered -for a given specialization in a base type. +`units::downcast` is used to obtain the target type of the downcasting operation registered +for a given instantiation in a base type. For example to determine a downcasted type of a quantity multiply operation the following can be done: ```cpp using dim = dimension_multiply; using common_rep = decltype(lhs.count() * rhs.count()); -using ret = quantity>>, common_rep>; +using ret = quantity>>, common_rep>; ``` `detail::downcast_target_impl` checks if a downcasting target is registered for the specific base class. diff --git a/doc/downcast_1.png b/doc/downcast_1.png new file mode 100644 index 00000000..22768197 Binary files /dev/null and b/doc/downcast_1.png differ diff --git a/doc/downcast_2.png b/doc/downcast_2.png new file mode 100644 index 00000000..7dbfc6c2 Binary files /dev/null and b/doc/downcast_2.png differ diff --git a/src/include/units/bits/downcasting.h b/src/include/units/bits/downcasting.h index f5bf3779..043b1522 100644 --- a/src/include/units/bits/downcasting.h +++ b/src/include/units/bits/downcasting.h @@ -41,8 +41,8 @@ namespace units { std::derived_from>; template - struct downcast_helper : T { - friend auto downcast_guide(typename downcast_helper::downcast_base) { return Target(); } + struct downcast_child : T { + friend auto downcast_guide(typename downcast_child::downcast_base) { return Target(); } }; namespace detail { @@ -53,7 +53,7 @@ namespace units { }; template - constexpr auto downcast_target_impl() + constexpr auto downcast_impl() { if constexpr(has_downcast) return decltype(downcast_guide(std::declval>()))(); @@ -64,7 +64,7 @@ namespace units { } template - using downcast_target = decltype(detail::downcast_target_impl()); + using downcast = decltype(detail::downcast_impl()); template using downcast_base_t = T::base_type; diff --git a/src/include/units/bits/format_utils.h b/src/include/units/bits/format_utils.h index a16f384f..2749e87c 100644 --- a/src/include/units/bits/format_utils.h +++ b/src/include/units/bits/format_utils.h @@ -48,7 +48,7 @@ namespace units { { if constexpr(Ratio::num != 1 || Ratio::den != 1) { if(!std::same_as) { - using prefix = downcast_target>; + using prefix = downcast>; if constexpr(!std::same_as>) { // print as a prefixed unit diff --git a/src/include/units/dimension.h b/src/include/units/dimension.h index 9d73a2f9..ed4add42 100644 --- a/src/include/units/dimension.h +++ b/src/include/units/dimension.h @@ -151,7 +151,7 @@ namespace units { struct dim_invert_impl; template - struct dim_invert_impl> : std::type_identity...>>> {}; + struct dim_invert_impl> : std::type_identity...>>> {}; } @@ -221,7 +221,7 @@ namespace units { // derived_dimension template - struct derived_dimension : downcast_helper::type> {}; + struct derived_dimension : downcast_child> {}; // merge_dimension namespace detail { @@ -243,7 +243,7 @@ namespace units { struct dimension_multiply_impl; template - struct dimension_multiply_impl, dimension> : std::type_identity, dimension>>> {}; + struct dimension_multiply_impl, dimension> : std::type_identity, dimension>>> {}; } @@ -273,7 +273,7 @@ namespace units { struct dimension_sqrt_impl; template - struct dimension_sqrt_impl> : std::type_identity...>>> {}; + struct dimension_sqrt_impl> : std::type_identity...>>> {}; } @@ -287,7 +287,7 @@ namespace units { struct dimension_pow_impl; template - struct dimension_pow_impl, N> : std::type_identity...>>> {}; + struct dimension_pow_impl, N> : std::type_identity...>>> {}; } diff --git a/src/include/units/math.h b/src/include/units/math.h index 3dd70d92..1fd79d17 100644 --- a/src/include/units/math.h +++ b/src/include/units/math.h @@ -39,7 +39,7 @@ namespace units { { using dim = dimension_pow; using r = ratio_pow; - return quantity>, Rep>(static_cast(std::pow(q.count(), N))); + return quantity>, Rep>(static_cast(std::pow(q.count(), N))); } template @@ -47,7 +47,7 @@ namespace units { { using dim = dimension_sqrt; using r = ratio_sqrt; - return quantity>, Rep>(static_cast(std::sqrt(q.count()))); + return quantity>, Rep>(static_cast(std::sqrt(q.count()))); } } // namespace units diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 86e45630..14097d77 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -76,7 +76,7 @@ namespace units { requires same_dim struct common_quantity_impl, quantity, Rep> { using type = - quantity>>, + quantity>>, Rep>; }; @@ -291,7 +291,7 @@ namespace units { using dim = quantity::unit::dimension; if constexpr(!detail::is_dimension) { // print as a prefix or ratio of a coherent unit symbol defined by the user - using coherent_unit = downcast_target>>; + using coherent_unit = downcast>>; detail::print_prefix_or_ratio(os); os << coherent_unit::symbol; } @@ -358,7 +358,7 @@ namespace units { { using dim = dimension_multiply; using common_rep = decltype(lhs.count() * rhs.count()); - using ret = quantity>>, common_rep>; + using ret = quantity>>, common_rep>; return ret(lhs.count() * rhs.count()); } @@ -370,9 +370,9 @@ namespace units { using dim = dim_invert; using common_rep = decltype(v / q.count()); - using ret = quantity>>, common_rep>; using den = quantity; return ret(v / den(q).count()); + using ret = quantity>>, common_rep>; } template @@ -407,7 +407,7 @@ namespace units { using common_rep = decltype(lhs.count() / rhs.count()); using dim = dimension_divide; - using ret = quantity>>, common_rep>; + using ret = quantity>>, common_rep>; return ret(lhs.count() / rhs.count()); } diff --git a/src/include/units/unit.h b/src/include/units/unit.h index d451961b..b75aeb93 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -63,7 +63,7 @@ namespace units { } template - struct prefix : downcast_helper> { + struct prefix : downcast_child> { static constexpr auto symbol = Symbol; }; @@ -150,25 +150,25 @@ namespace units { struct no_prefix; template - struct coherent_derived_unit : downcast_helper>> { + struct coherent_derived_unit : downcast_child>> { static constexpr auto symbol = Symbol; using prefix_type = PrefixType; }; template - struct derived_unit : downcast_helper> { + struct derived_unit : downcast_child> { static constexpr auto symbol = Symbol; }; template requires requires { U::symbol; } - struct prefixed_derived_unit : downcast_helper>> { + struct prefixed_derived_unit : downcast_child>> { static constexpr auto symbol = P::symbol + U::symbol; using prefix_type = P::prefix_type; }; template - struct deduced_derived_unit : downcast_helper> { + struct deduced_derived_unit : downcast_child> { static constexpr auto symbol = Symbol; };