From aa33964782ae2a7e60649afea4ffdec24605eee6 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 19 Oct 2022 19:56:35 +0200 Subject: [PATCH] refactor: expression templates engine refactored --- .../include/units/bits/expression_template.h | 259 ++++++++++-------- 1 file changed, 146 insertions(+), 113 deletions(-) diff --git a/src/core/include/units/bits/expression_template.h b/src/core/include/units/bits/expression_template.h index 3d387efa..e7ead18e 100644 --- a/src/core/include/units/bits/expression_template.h +++ b/src/core/include/units/bits/expression_template.h @@ -28,14 +28,30 @@ namespace units { +/** + * @brief Type list type used by the expression template framework + * + * @tparam Ts The list of types + */ template struct type_list {}; +/** + * @brief Type list type storing the list of components with negative exponents + * + * @note Can't be empty + */ template struct per {}; namespace detail { +template +inline constexpr bool is_specialization_of_per = false; + +template +inline constexpr bool is_specialization_of_per> = true; + template inline constexpr bool valid_ratio = true; @@ -65,6 +81,19 @@ inline constexpr bool ratio_one = true; } // namespace detail + +/** + * @brief Type container for exponents with ratio different than `1` + * + * Ratio must be mathematically valid and non-negative. Negative exponents are passed to + * `per<...>` with inverted sign (as positive exponents). + * + * @tparam F factor to be raised to specified power + * @tparam Num Power ratio numerator + * @tparam Den [optional] Power ration denominator + * + * @note @p Den is an optional parameter to shorten the types presented to the user in the case when @p Den equals `1`. + */ template requires(detail::valid_ratio && detail::positive_ratio && !detail::ratio_one) struct power { @@ -80,12 +109,7 @@ inline constexpr bool is_specialization_of_power = false; template inline constexpr bool is_specialization_of_power> = true; -} // namespace detail - -namespace detail { - -template -// template // TODO +template consteval auto power_or_T_impl() { if constexpr (R.den == 1) { @@ -99,42 +123,15 @@ consteval auto power_or_T_impl() }; template -// template // TODO +// template // TODO ICE gcc 12 using power_or_T = decltype(power_or_T_impl()); -// type_power -// template -// struct type_power { -// using type = conditional, power>; -// }; - -// template -// struct type_power, Num, 1> { -// using type = power; -// }; - -// template -// struct type_power, Num, Den> { -// static constexpr ratio r = ratio(power::num, power::den) * ratio(Num, Den); -// using type = power_or_T; -// }; - -// expr_power -// template -// struct expr_power; - -// template -// struct expr_power, Num, Den> { -// using type = type_list::type...>; -// }; /** - * @brief Consolidates contiguous ranges of exponents of the same dimension + * @brief Consolidates contiguous ranges of exponents of the same type * - * If there is more than one exponent with the same dimension they are aggregated into one exponent by adding - * their exponents. If this accumulation will result with 0, such a dimension is removed from the list. - * - * @tparam D derived dimension to consolidate + * If there is more than one exponent with the same type they are aggregated into one type by adding + * their powers. */ template struct expr_consolidate_impl; @@ -154,29 +151,65 @@ struct expr_consolidate_impl> { using type = type_list_push_front>::type, T>; }; +// replaces two instances of a type with one having the power of `2` template requires(!is_specialization_of_power) struct expr_consolidate_impl> { using type = expr_consolidate_impl, Rest...>>::type; }; +// replaces the instance of a type and a power of it with one with incremented power template struct expr_consolidate_impl, Rest...>> { using type = expr_consolidate_impl::exponent + 1>, Rest...>>::type; }; +// accumulates the powers of instances of the same type (removes the element in case the accumulation result is `0`) template struct expr_consolidate_impl, power, Rest...>> { static constexpr ratio r = power::exponent + power::exponent; - using type = conditional>::type, - typename expr_consolidate_impl, Rest...>>::type>; + using type = typename expr_consolidate_impl, Rest...>>::type; }; template using expr_consolidate = typename expr_consolidate_impl::type; -// expr_simplify +/** + * @brief Simplifies the expression template + * + * Analyzes provided numerator and denominator type lists and simplifies elements with the same type. + * If the same power exists in both list, this elements gets omitted. Otherwise, the power of its + * exponent gets subtracted according to numerator and denominator elements powers. + * + * @tparam NumList type list for expression numerator + * @tparam DenList type list for expression denominator + * @tparam Pred predicate to be used for elements comparisons + */ +template typename Pred> +struct expr_simplify; + +// when one of the lists is empty there is nothing to do +template typename Pred> + requires(type_list_size == 0) || (type_list_size == 0) +struct expr_simplify { + using num = NumList; + using den = DenList; +}; + +// in case when front elements are different progress to the next element +template typename Pred> +struct expr_simplify, type_list, Pred> { + using impl = conditional::value, expr_simplify, type_list, Pred>, + expr_simplify, type_list, Pred>>; + using num = conditional::value, type_list_push_front, typename impl::num>; + using den = conditional::value, typename impl::den, type_list_push_front>; +}; + +// in case two elements are of the same power such element gets omitted +template typename Pred> +struct expr_simplify, type_list, Pred> : + expr_simplify, type_list, Pred> {}; template struct expr_simplify_power { @@ -186,28 +219,7 @@ struct expr_simplify_power { using den = conditional<(r < 0), type_list, type_list<>>; }; -template typename Pred> -struct expr_simplify; - -template typename Pred> - requires(type_list_size == 0) || (type_list_size == 0) -struct expr_simplify { - using num = NumList; - using den = DenList; -}; - -template typename Pred> -struct expr_simplify, type_list, Pred> { - using impl = conditional::value, expr_simplify, type_list, Pred>, - expr_simplify, type_list, Pred>>; - using num = conditional::value, type_list_push_front, typename impl::num>; - using den = conditional::value, typename impl::den, type_list_push_front>; -}; - -template typename Pred> -struct expr_simplify, type_list, Pred> : - expr_simplify, type_list, Pred> {}; - +// in case there are different powers for the same element simplify the power template typename Pred> struct expr_simplify, NRest...>, type_list, Pred> { using impl = expr_simplify, type_list, Pred>; @@ -216,6 +228,7 @@ struct expr_simplify, NRest...>, type_list; }; +// in case there are different powers for the same element simplify the power template typename Pred> struct expr_simplify, type_list, DRest...>, Pred> { using impl = expr_simplify, type_list, Pred>; @@ -224,6 +237,7 @@ struct expr_simplify, type_list, DRest. using den = type_list_join; }; +// in case there are different powers for the same element simplify the power template typename Pred> requires(!std::same_as, power>) @@ -251,61 +265,57 @@ struct expr_less_impl, Pred> : Pred {}; template typename Pred> struct expr_less_impl, Pred> : std::true_type {}; +/** + * @brief Compares two types with a given predicate + * + * Algorithm accounts not only for explicit types but also for the case when they + * are wrapped within `power`. + */ template typename Pred> using expr_less = expr_less_impl; // expr_fractions -template -struct expr_fractions_impl; - -template -struct expr_fractions_impl { - using den = type_list<>; +template, typename Den = type_list<>> +struct expr_fractions_result { + using _num_ = Num; // exposition only + using _den_ = Den; // exposition only }; -template -struct expr_fractions_impl { - using den = type_list_push_front::den, T>; -}; +template +[[nodiscard]] consteval auto expr_fractions_impl() +{ + constexpr std::size_t size = type_list_size; -template T, typename... Rest> -struct expr_fractions_impl : - TYPENAME expr_fractions_impl::den {}; + if constexpr (size == 0) + return expr_fractions_result<>{}; + else if constexpr (size == 1) + return expr_fractions_result{}; + else { + using last_element = type_list_back; -template -struct expr_fractions_impl { - using num = type_list<>; - using den = type_list<>; -}; - -template -struct expr_fractions_impl> { - using num = type_list<>; - using den = TYPENAME expr_fractions_impl::den; -}; - -template T, typename... Rest> -struct expr_fractions_impl : expr_fractions_impl {}; - -template -struct expr_fractions_impl { - using impl = expr_fractions_impl; - using num = type_list_push_front; - using den = TYPENAME impl::den; -}; + if constexpr (is_specialization_of_per) { + if constexpr (size == 2 && std::derived_from, OneTypeBase>) + return expr_fractions_result, type_list_map>{}; + else { + using split = type_list_split; + return expr_fractions_result>{}; + } + } else { + return expr_fractions_result{}; + } + } +} +/** + * @brief Divides expression template spec to numerator and denominator parts + */ template -struct expr_fractions { -private: - using impl = expr_fractions_impl; -public: - using _num_ = TYPENAME impl::num; // exposition only - using _den_ = TYPENAME impl::den; // exposition only -}; +struct expr_fractions : decltype(expr_fractions_impl>()) {}; +// expr_make_spec template typename To> -[[nodiscard]] consteval auto expr_expression_impl() +[[nodiscard]] consteval auto expr_make_spec_impl() { constexpr std::size_t num = type_list_size; constexpr std::size_t den = type_list_size; @@ -325,8 +335,11 @@ template typename To> -using expr_expression = decltype(expr_expression_impl()); +using expr_make_spec = decltype(expr_make_spec_impl()); template typename Pred, template typename To> @@ -335,18 +348,18 @@ template; using den_list = expr_consolidate; using simple = expr_simplify; - using expr = expr_expression; + using expr = expr_make_spec; return expr{}; } /** - * @brief Merges 2 sorted derived dimensions into one units::normalized_dimension + * @brief Multiplies two sorted expression template specs * - * A result of a dimensional calculation may result with many exponents of the same base dimension originated - * from different parts of the equation. As the exponents lists of both operands it is enough to merge them - * into one list and consolidate duplicates. Also it is possible that final exponents list will contain only - * one element being a base dimension with exponent 1. In such a case the final dimension should be the base - * dimension itself. + * @tparam T1 lhs of the operation + * @tparam T2 rhs of the operation + * @tparam OneType type that represents the value `1` + * @tparam Pred binary less then predicate + * @tparam To destination type list to put the result to */ template typename Pred, template typename To> @@ -357,6 +370,7 @@ template) { return T1{}; } else if constexpr (is_specialization_of && is_specialization_of) { + // two derived dimensions return get_optimized_expression, type_list_merge_sorted, OneType, Pred, To>(); @@ -367,11 +381,21 @@ template, Pred>, typename T2::_den_, OneType, Pred, To>(); } else { + // two base dimensions return get_optimized_expression, type_list, Pred>, type_list<>, OneType, Pred, To>(); } } +/** + * @brief Divides two sorted expression template specs + * + * @tparam T1 lhs of the operation + * @tparam T2 rhs of the operation + * @tparam OneType type that represents the value `1` + * @tparam Pred binary less then predicate + * @tparam To destination type list to put the result to + */ template typename Pred, template typename To> [[nodiscard]] consteval auto expr_divide() @@ -381,6 +405,7 @@ template) { return T1{}; } else if constexpr (is_specialization_of && is_specialization_of) { + // two derived dimensions return get_optimized_expression, type_list_merge_sorted, OneType, Pred, To>(); @@ -391,15 +416,23 @@ template, Pred>, typename T2::_num_, OneType, Pred, To>(); } else { + // two base dimensions return To>{}; } } +/** + * @brief Inverts the expression template spec + * + * @tparam T expression template spec to invert + * @tparam OneType type that represents the value `1` + * @tparam To destination type list to put the result to + */ template typename To> [[nodiscard]] consteval auto expr_invert() { if constexpr (is_specialization_of) - return expr_expression{}; + return expr_make_spec{}; else return To>{}; }