From 1b4e8a21277f465f1e7339c919421481ca2dd09c Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sun, 1 Dec 2019 19:47:58 +0100 Subject: [PATCH] Units and dimensions redesigned --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 2 +- src/include/units/base_dimension.h | 75 +++ src/include/units/bits/type_traits.h | 60 ++- src/include/units/bits/unit_concept.h | 41 ++ src/include/units/derived_dimension.h | 403 +++++++++++++++ src/include/units/dimension.h | 292 ----------- .../units/dimensions/base_dimensions.h | 37 ++ src/include/units/dimensions/si_prefixes.h | 52 -- src/include/units/physical/si/prefixes.h | 51 ++ src/include/units/prefix.h | 78 ++- src/include/units/unit.h | 480 ++++++------------ test/CMakeLists.txt | 4 +- test/unit_test/static/CMakeLists.txt | 15 +- test/unit_test/static/ratio_test.cpp | 50 +- test/unit_test/static/unit_test.cpp | 184 +------ 16 files changed, 922 insertions(+), 904 deletions(-) create mode 100644 src/include/units/base_dimension.h create mode 100644 src/include/units/bits/unit_concept.h create mode 100644 src/include/units/derived_dimension.h delete mode 100644 src/include/units/dimension.h create mode 100644 src/include/units/dimensions/base_dimensions.h delete mode 100644 src/include/units/dimensions/si_prefixes.h create mode 100644 src/include/units/physical/si/prefixes.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 863addb5..931c79d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,4 +42,4 @@ add_subdirectory(src) add_subdirectory(test) # add usage example -add_subdirectory(example) +#add_subdirectory(example) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bcf70e5e..389aac19 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,7 +24,7 @@ cmake_minimum_required(VERSION 3.8) #cmake_policy(SET CMP0076 NEW) project(units - VERSION 0.4.0 + VERSION 0.5.0 LANGUAGES CXX ) diff --git a/src/include/units/base_dimension.h b/src/include/units/base_dimension.h new file mode 100644 index 00000000..6fbde960 --- /dev/null +++ b/src/include/units/base_dimension.h @@ -0,0 +1,75 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace units { + +/** + * @brief A dimension of a base quantity + * + * Measurement unit that is adopted by convention for a base quantity (a quantity that can not be expressed in terms of + * the other quantities within that subset) in a specific system of units. + * + * Pair of Name and Unit template parameter forms an unique identifier of the base dimension. The same identifiers can + * be multiplied and divided which will result with an adjustment of its factor in an Exponent (in case of zero the + * dimension will be simplified and removed from further analysis of current expresion). In case the Name is the same + * but the Unit differs (i.e. mixing SI and CGS length), there is no automatic simplification but is possible to force + * it with a quantity_cast. + * + * @tparam Name an unique identifier of the base dimension used to provide dimensional analysis support + * @tparam U a base unit to be used for this base dimension + */ +template +struct base_dimension { + using base_type = base_dimension; + static constexpr auto name = Name; + using coherent_unit = U; +}; + +// BaseDimension +namespace detail { + +template +inline constexpr bool is_base_dimension = false; + +template +inline constexpr bool is_base_dimension> = true; + +} // namespace detail + +template +concept BaseDimension = detail::is_base_dimension; + +// base_dimension_less +// TODO Remove the below when https://bugs.llvm.org/show_bug.cgi?id=32208 is fixed +// clang-format off +template +struct base_dimension_less : std::bool_constant {}; +// clang-format on + +} // namespace units diff --git a/src/include/units/bits/type_traits.h b/src/include/units/bits/type_traits.h index 4a9c8a50..831fad2b 100644 --- a/src/include/units/bits/type_traits.h +++ b/src/include/units/bits/type_traits.h @@ -26,23 +26,53 @@ namespace units { - namespace detail { +// conditional +namespace detail { - template - struct conditional_impl { - template - using type = F; - }; +template +struct conditional_impl { + template + using type = F; +}; - template<> - struct conditional_impl { - template - using type = T; - }; +template<> +struct conditional_impl { + template + using type = T; +}; - } +} // namespace detail - template - using conditional = detail::conditional_impl::template type; +template +using conditional = detail::conditional_impl::template type; -} +// is_instantiation +namespace detail { + +template typename Type> +inline constexpr bool is_instantiation_impl = false; + +template typename Type> +inline constexpr bool is_instantiation_impl, Type> = true; + +} // namespace detail + +template typename Type> +inline constexpr bool is_instantiation = detail::is_instantiation_impl; + +// is_derived_from_instantiation +namespace detail { + +template typename Type> +struct is_derived_from_instantiation_impl { + template + static constexpr std::true_type check_base(const Type&); + static constexpr std::true_type check_base(...); +}; + +} // namespace detail + +template typename Type> +inline constexpr bool is_derived_from_instantiation = decltype(detail::is_derived_from_instantiation_impl::check_base(std::declval()))::value; + +} // namespace units diff --git a/src/include/units/bits/unit_concept.h b/src/include/units/bits/unit_concept.h new file mode 100644 index 00000000..1f60253a --- /dev/null +++ b/src/include/units/bits/unit_concept.h @@ -0,0 +1,41 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include + +namespace units { + +namespace detail { + +template +struct reference_unit; + +} // namespace detail + +// Unit +template +concept Unit = is_derived_from_instantiation; + +} // namespace units diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h new file mode 100644 index 00000000..848ab4e0 --- /dev/null +++ b/src/include/units/derived_dimension.h @@ -0,0 +1,403 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace units { + +// Exponent +namespace detail { + +template +inline constexpr bool is_exp = false; + +// partial specialization for an exp type provided below + +} // namespace detail + +template +concept Exponent = detail::is_exp; + +/** + * @brief A derived dimension + * + * There are 2 partial specializations of this primary class template. One of them is used by the library engine + * and another one is the interface for the user to define a derived dimension. + */ +template +struct derived_dimension; + +/** + * @brief Dimensionless quantity + */ +template<> +struct derived_dimension<> : downcast_base> {}; + +/** + * @brief A dimension of a derived quantity + * + * Expression of the dependence of a quantity on the base quantities (and their base dimensions) of a system of + * quantities as a product of powers of factors corresponding to the base quantities, omitting any numerical factors. + * A power of a factor is the factor raised to an exponent. + * + * A derived dimension can be formed from multiple exponents (i.e. velocity is represented as "exp, exp"). + * It is also possible to form a derived dimension with only one exponent (i.e. frequency is represented as just + * "exp"). + * + * @note This partial class template specialization is used by the library engine and should not be directly instantiated + * by the user (see the other partial specialization). + * + * @tparam E a first exponent of a derived dimension + * @tparam ERest zero or more following exponents of a derived dimension + */ +template +struct derived_dimension : downcast_base> {}; + +// DerivedDimension +template +concept DerivedDimension = std::is_empty_v && is_instantiation, derived_dimension>; + +// Dimension +template +concept Dimension = BaseDimension || DerivedDimension; + +/** + * @brief A power of factor corresponding to the dimension of a quantity + * + * @tparam Dim component dimension of a derived quantity + * @tparam Num numinator of the factor + * @tparam Den denominator of the factor + */ +template +struct exp { + using dimension = Dim; + static constexpr int num = Num; + static constexpr int den = Den; +}; + +// is_exp +namespace detail { + +template +inline constexpr bool is_exp> = true; + +} // namespace detail + +// exp_less +template +struct exp_less : base_dimension_less {}; + +// exp_invert +namespace detail { + +template +constexpr exp exp_invert_impl(exp); + +} // namespace detail + +template +using exp_invert = decltype(detail::exp_invert_impl(E())); + +// exp_multiply +namespace detail { + +template +struct exp_multiply_impl { + using r1 = ratio; + using r2 = ratio; + using r = ratio_multiply; + using type = exp; +}; + +} // namespace detail + +template +using exp_multiply = detail::exp_multiply_impl::type; + +// make_dimension +namespace detail { + +/** + * @brief Consolidates contiguous ranges of exponents of the same dimension + * + * 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 + */ +template +struct dim_consolidate; + +template<> +struct dim_consolidate> { + using type = derived_dimension<>; +}; + +template +struct dim_consolidate> { + using type = derived_dimension; +}; + +template +struct dim_consolidate> { + using type = type_list_push_front>::type, E1>; +}; + +template +struct dim_consolidate, exp, ERest...>> { + // TODO: provide custom implementation for ratio_add + using r1 = std::ratio; + using r2 = std::ratio; + using r = std::ratio_add; + using type = conditional>::type, + typename dim_consolidate, ERest...>>::type>; +}; + +/** + * @brief Extracts the list of potentially derived dimensions to a list containing only base dimensions + * + * @tparam Es Exponents of potentially derived dimensions + */ +template +struct extract; + +template<> +struct extract<> { + using type = derived_dimension<>; +}; + +template +struct extract, ERest...> { + using type = type_list_push_front::type, exp>; +}; + +template +struct extract, ERest...> { + using type = extract, Num, Den>, ERest...>::type; +}; + +template +struct extract, Num, Den>, ERest...> { + using type = type_list_push_front::type, exp_multiply...>; +}; + +/** + * @brief Converts user provided derived dimension specification into a valid units::derived_dimension definition + * + * User provided definition of a derived dimension may contain the same base dimension repeated more than once on the + * list possibly hidden in other derived units provided by the user. The process here should: + * 1. Extract derived dimensions into exponents of base dimensions. + * 2. Sort the exponents so the same dimensions are placed next to each other. + * 3. Consolidate contiguous range of exponents of the same base dimensions to a one (or possibly zero) exponent for + * this base dimension. + */ +template +using make_dimension = dim_consolidate::type, exp_less>>::type; + +} // namespace detail + +/** + * @brief The list of exponents of dimensions (both base and derived) provided by the user + * + * This is the primary interface to create derived dimensions. Exponents list can contain powers of factors of both + * base and derived dimensions. This is called a "recipe" of the dimension and among others is used to print + * unnamed coherent units of this dimension. + * + * The implementation is responsible for unpacking all of the dimensions into a list containing only base dimensions + * and their factors and putting them to the other (private) units::derived_dimension class template partial + * specialization. + * + * @note User should always use only this partial specialization to create derived dimensions. + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + * @tparam E The list of exponents of ingredient dimensions + * @tparam ERest The list of exponents of ingredient dimensions + */ +template + requires (!Exponent) +struct derived_dimension : downcast_child> { + using recipe = derived_dimension; + using coherent_unit = U; +}; + +// same_dim +template +inline constexpr bool same_dim; + +template +inline constexpr bool same_dim = std::is_same_v; + +template +inline constexpr bool same_dim = std::is_same_v; + +// dim_invert +namespace detail { + +template +struct dim_invert_impl; + +template +struct dim_invert_impl { + using type = downcast>>; +}; + +template +struct dim_invert_impl>> { + using type = D; +}; + +template +struct dim_invert_impl> { + using type = downcast...>>; +}; + +} // namespace detail + +template +using dim_invert = detail::dim_invert_impl>::type; + +// dimension_multiply +namespace detail { + +template +struct dim_unpack { + using type = D; +}; + +template +struct dim_unpack>> { + using type = D; +}; + +/** + * @brief Merges 2 sorted derived dimensions into one units::derived_dimension + * + * A result of a dimensional calculation may result with many exponents of the same base dimension orginated + * 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. + */ +template +using merge_dimension = dim_unpack>::type>::type; + +template +struct dimension_multiply_impl; + +template +struct dimension_multiply_impl { + using type = downcast>, derived_dimension>>>; +}; + +template +struct dimension_multiply_impl { + using type = downcast>, typename D2::base_type>>; +}; + +template +struct dimension_multiply_impl { + using type = dimension_multiply_impl::type; +}; + +template +struct dimension_multiply_impl { + using type = downcast>; +}; + +} // namespace detail + +template +using dimension_multiply = detail::dimension_multiply_impl::type; + +template +using dimension_divide = detail::dimension_multiply_impl>::type; + +// dimension_sqrt +namespace detail { + +template +struct dimension_sqrt_impl; + +template +struct dimension_sqrt_impl { + using type = derived_dimension>; +}; + +template +struct dimension_sqrt_impl>> { + using type = D; +}; + +template +struct dimension_sqrt_impl { + using type = dimension_sqrt_impl; +}; + +template +struct dimension_sqrt_impl> { + using type = downcast...>>; +}; + +} // namespace detail + +template +using dimension_sqrt = detail::dimension_sqrt_impl::type; + +// dimension_pow +namespace detail { + +template +struct dimension_pow_impl; + +template +struct dimension_pow_impl { + using type = derived_dimension>; +}; + +template +struct dimension_pow_impl, N> { + using type = D; +}; + +template +struct dimension_pow_impl { + using type = dimension_pow_impl; +}; + +template +struct dimension_pow_impl, N> { + using type = downcast...>>; +}; + +} // namespace detail + +template +using dimension_pow = detail::dimension_pow_impl::type; + +} // namespace units diff --git a/src/include/units/dimension.h b/src/include/units/dimension.h deleted file mode 100644 index 2e795d14..00000000 --- a/src/include/units/dimension.h +++ /dev/null @@ -1,292 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include -#include -#include -#include - -namespace units { - - template - struct base_dimension { - static constexpr auto name = Name; - static constexpr auto symbol = Symbol; - }; - - template - concept BaseDimension = std::is_empty_v && - requires { - T::name; - T::symbol; - };// && // TODO file a bug for this gcc issue -// std::derived_from>; - - // base_dimension_less - - template - struct base_dimension_less : std::bool_constant { - }; - - // is_exp - namespace detail { - - template - inline constexpr bool is_exp = false; - - // partial specialization for an exp type provided below - - } // namespace detail - - template - concept Exponent = detail::is_exp; - - template - struct dimension; - - // is_dimension - namespace detail { - - template - inline constexpr bool is_dimension = false; - - template - inline constexpr bool is_dimension> = true; - - } // namespace detail - - template - concept Dimension = - std::is_empty_v && - detail::is_dimension>; - - // exp - template - requires BaseDimension || Dimension - struct exp { - using dimension = BaseOrDim; - static constexpr int num = Num; - static constexpr int den = Den; - }; - - // is_exp - namespace detail { - - template - inline constexpr bool is_exp> = true; - - } // namespace detail - - // exp_less - template - struct exp_less : base_dimension_less { - }; - - // exp_invert - namespace detail { - - template - struct exp_invert_impl; - - template - struct exp_invert_impl> { - using type = exp; - }; - - } - - template - using exp_invert = detail::exp_invert_impl::type; - - // exp_multiply - namespace detail { - - template - struct exp_multiply_impl { - using r1 = ratio; - using r2 = ratio; - using r = ratio_multiply; - using type = exp; - }; - - } - - template - using exp_multiply = detail::exp_multiply_impl::type; - - // dimension - template - struct dimension : downcast_base> {}; - - // same_dim - template - requires BaseDimension || Dimension - inline constexpr bool same_dim = std::is_same_v; - - template - inline constexpr bool same_dim = std::is_same_v>, typename D2::base_type>; - - // dim_invert - namespace detail { - - template - struct dim_invert_impl; - - template - struct dim_invert_impl> : std::type_identity...>>> {}; - - } - - template - using dim_invert = detail::dim_invert_impl>::type; - - // make_dimension - namespace detail { - - template - struct dim_consolidate; - - template<> - struct dim_consolidate> { - using type = dimension<>; - }; - - template - struct dim_consolidate> { - using type = dimension; - }; - - template - struct dim_consolidate> { - using type = type_list_push_front>::type, E1>; - }; - - template - struct dim_consolidate, exp, ERest...>> { - // TODO: provide custom implementation for ratio_add - using r1 = std::ratio; - using r2 = std::ratio; - using r = std::ratio_add; - using type = conditional>::type, - typename dim_consolidate, ERest...>>::type>; - }; - - template - struct extract; - - template<> - struct extract<> { - using type = dimension<>; - }; - - template - struct extract, ERest...> { - using type = type_list_push_front::type, exp>; - }; - - template - struct extract, Num, Den>, ERest...> { - using type = type_list_push_front::type, exp_multiply...>; - }; - - template - struct extract, ERest...> { - using type = extract, Num, Den>, ERest...>::type; - }; - - template - using make_dimension = dim_consolidate::type, exp_less>>::type; - - } // namespace detail - - // derived_dimension - template - struct derived_dimension : downcast_child> { - using recipe = dimension; - }; - - // merge_dimension - template - using merge_dimension = detail::dim_consolidate>::type; - - // dimension_multiply - namespace detail { - - template - struct dimension_multiply_impl; - - template - struct dimension_multiply_impl, dimension> : std::type_identity, dimension>>> {}; - - } - - template - using dimension_multiply = detail::dimension_multiply_impl::type; - - // dimension_divide - namespace detail { - - template - struct dimension_divide_impl; - - template - struct dimension_divide_impl, dimension> - : dimension_multiply_impl, dimension...>> { - }; - - } - - template - using dimension_divide = detail::dimension_divide_impl::type; - - // dimension_sqrt - namespace detail { - - template - struct dimension_sqrt_impl; - - template - struct dimension_sqrt_impl> : std::type_identity...>>> {}; - - } - - template - using dimension_sqrt = detail::dimension_sqrt_impl::type; - - // dimension_pow - namespace detail { - - template - struct dimension_pow_impl; - - template - struct dimension_pow_impl, N> : std::type_identity...>>> {}; - - } - - template - using dimension_pow = detail::dimension_pow_impl::type; - -} // namespace units diff --git a/src/include/units/dimensions/base_dimensions.h b/src/include/units/dimensions/base_dimensions.h new file mode 100644 index 00000000..ef2ea824 --- /dev/null +++ b/src/include/units/dimensions/base_dimensions.h @@ -0,0 +1,37 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include + +namespace units { + + struct base_dim_length : base_dimension<"length", "m"> {}; + struct base_dim_mass : base_dimension<"mass", "kg"> {}; + struct base_dim_time : base_dimension<"time", "s"> {}; + struct base_dim_current : base_dimension<"current", "A"> {}; + struct base_dim_temperature : base_dimension<"temperature", "K"> {}; + struct base_dim_substance : base_dimension<"substance", "mol"> {}; + struct base_dim_luminous_intensity : base_dimension<"luminous intensity", "cd"> {}; + +} // namespace units diff --git a/src/include/units/dimensions/si_prefixes.h b/src/include/units/dimensions/si_prefixes.h deleted file mode 100644 index a2d5bc5c..00000000 --- a/src/include/units/dimensions/si_prefixes.h +++ /dev/null @@ -1,52 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include -#include - -namespace units { - - // prefix tags - struct si_prefix : prefix_type {}; - - // SI prefixes - - struct atto : prefix, "a"> {}; - struct femto : prefix, "f"> {}; - struct pico : prefix, "p"> {}; - struct nano : prefix, "n"> {}; - struct micro : prefix, "\u00b5"> {}; - struct milli : prefix, "m"> {}; - struct centi : prefix, "c"> {}; - struct deci : prefix, "d"> {}; - struct deca : prefix, "da"> {}; - struct hecto : prefix, "h"> {}; - struct kilo : prefix, "k"> {}; - struct mega : prefix, "M"> {}; - struct giga : prefix, "G"> {}; - struct tera : prefix, "T"> {}; - struct peta : prefix, "P"> {}; - struct exa : prefix, "E"> {}; - -} diff --git a/src/include/units/physical/si/prefixes.h b/src/include/units/physical/si/prefixes.h new file mode 100644 index 00000000..6c09b90a --- /dev/null +++ b/src/include/units/physical/si/prefixes.h @@ -0,0 +1,51 @@ +// The MIT License (MIT) +// +// Copyright (c) 2018 Mateusz Pusz +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include + +namespace units::si { + +struct prefix : prefix_type {}; + +// TODO Remove dependency on std::ratio + +struct atto : units::prefix> {}; +struct femto : units::prefix> {}; +struct pico : units::prefix> {}; +struct nano : units::prefix> {}; +struct micro : units::prefix> {}; +struct milli : units::prefix> {}; +struct centi : units::prefix> {}; +struct deci : units::prefix> {}; +struct deca : units::prefix> {}; +struct hecto : units::prefix> {}; +struct kilo : units::prefix> {}; +struct mega : units::prefix> {}; +struct giga : units::prefix> {}; +struct tera : units::prefix> {}; +struct peta : units::prefix> {}; +struct exa : units::prefix> {}; + +} // namespace units::si diff --git a/src/include/units/prefix.h b/src/include/units/prefix.h index 1677644b..5f95b7e7 100644 --- a/src/include/units/prefix.h +++ b/src/include/units/prefix.h @@ -28,43 +28,69 @@ namespace units { - struct prefix_type {}; +/** + * @brief The base for all prefix types + * + * Every prefix type should inherit from this type to satisfy PrefixType concept. + */ +struct prefix_type {}; - template - concept PrefixType = std::derived_from; +template +concept PrefixType = std::derived_from; - namespace detail { +/** + * @brief No prefix possible for the unit + * + * This is a special prefix type tag specifying that the unit can not be scaled with any kind + * of the prefix. + */ +struct no_prefix : prefix_type {}; - template - struct prefix_base : downcast_base> { - using prefix_type = PT; - using ratio = R; - }; +namespace detail { - } +template +struct prefix_base : downcast_base> { + using prefix_type = PT; + using ratio = R; +}; - template - struct prefix : downcast_child> { - static constexpr auto symbol = Symbol; - }; +} // namespace detail +/** + * @brief A prefix used to scale units + * + * Data from a prefix class is used in two cases: + * - when defining a prefixed_unit its ratio is used to scale the reference unit and its + * symbol is used to prepend to the symbol of referenced unit + * - when printing the symbol of a scaled unit that was not predefined by the user but its + * factor matches ratio of a prefix from the specified prefix family, its symbol will be + * prepended to the symbol of the unit + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + * @tparam PT a type of prefix family + * @tparam Symbol a text representation of the prefix + * @tparam R factor to be used to scale a unit + */ +template + requires(!std::same_as) +struct prefix : downcast_child>> { + static constexpr auto symbol = Symbol; +}; - // TODO gcc:92150 - // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92150 - // namespace detail { +// TODO gcc:92150 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92150 +// namespace detail { - // template - // inline constexpr bool is_prefix = false; +// template +// inline constexpr bool is_prefix = false; - // template - // inline constexpr bool is_prefix> = true; +// template +// inline constexpr bool is_prefix> = true; - // } // namespace detail +// } // namespace detail - template +template // concept Prefix = detail::is_prefix; - concept Prefix = true; - - struct no_prefix : prefix_type {}; +concept Prefix = true; } // namespace units diff --git a/src/include/units/unit.h b/src/include/units/unit.h index ec7b3563..8c50c259 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -22,353 +22,185 @@ #pragma once -#include +#include +#include +#include +#include +#include #include #include -#include namespace units { - template - requires (R::num * R::den > 0) - struct unit : downcast_base> { - using dimension = D; - using ratio = R; - }; +namespace detail { - // is_unit +template +struct reference_unit : downcast_base> { + using reference = U; + using ratio = R; +}; - namespace detail { +} // namespace detail - template - inline constexpr bool is_unit = false; +namespace detail { - template - inline constexpr bool is_unit> = true; +// same_reference_units +template +inline constexpr bool same_reference_units = false; - } +template +inline constexpr bool same_reference_units, Us...> = + (std::same_as && ...); - template - concept Unit = - std::is_empty_v && - detail::is_unit>; +// deduced_unit +template +struct ratio_op; - // deduced_derived_unit +template +struct ratio_op { + using ratio = Result; +}; - namespace detail { +template +struct ratio_op { + using calc_ratio = + conditional<(UnitExpNum * UnitExpDen > 0), ratio_multiply, ratio_divide>; + static constexpr int value = (UnitExpNum * UnitExpDen > 0) ? (UnitExpNum - UnitExpDen) : (UnitExpNum + UnitExpDen); + using ratio = ratio_op::ratio; +}; - template - struct get_unit_base_dim; +template +struct derived_ratio; - template - struct get_unit_base_dim> { - static_assert(sizeof...(Rest) == 0, "Base unit expected"); - using dimension = E::dimension; - }; +template +struct derived_ratio, Us...> { + using ratio = ::units::ratio<1>; +}; - template - struct ratio_op; +template +struct derived_ratio, U, URest...> { + using rest_ratio = derived_ratio, URest...>::ratio; + using ratio = ratio_op::ratio; +}; - template - struct ratio_op { - using ratio = Result; - }; +template +using deduced_unit = + reference_unit::ratio>; - template - struct ratio_op { - using calc_ratio = conditional<(UnitExpNum * UnitExpDen > 0), ratio_multiply, - ratio_divide>; - static constexpr int value = (UnitExpNum * UnitExpDen > 0) ? (UnitExpNum - UnitExpDen) : (UnitExpNum + UnitExpDen); - using ratio = ratio_op::ratio; - }; +} // namespace detail - template - struct derived_ratio; +/** + * @brief A starting point for a new hierarchy of units + * + * A unit is an entity defined and adopted by convention, with which any other quantity of + * the same kind can be compared to express the ratio of the second quantity to the first + * one as a number. + * + * Coherent unit is a unit that, for a given system of quantities and for a chosen set of + * base units, is a product of powers of base units with no other proportionality factor + * than one. + * + * This class allows definition of a new unnamed (in most cases coherent) derived unit of + * a specific derived dimension and it should be passed in this dimension's definition. + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + */ +template +struct unit : downcast_child>> { + static constexpr bool is_named = false; + using prefix_type = no_prefix; +}; - template - struct derived_ratio, Us...> { - using ratio = ::units::ratio<1>; - }; +/** + * @brief A named unit + * + * Defines a named (in most cases coherent) unit that is then passed to a dimension definition. + * A named unit may be used by other units defined with the prefix of the same type, unless + * no_prefix is provided for PT template parameter (in such a case it is impossible to define + * a prefix unit based on this one). + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + * @tparam Symbol a short text representation of the unit + * @tparam PT no_prefix or a type of prefix family + */ +template +struct named_unit : downcast_child>> { + static constexpr bool is_named = true; + static constexpr auto symbol = Symbol; + using prefix_type = PT; +}; - 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; - }; +/** + * @brief A scaled unit + * + * Defines a new named unit that is a scaled version of another unit. Such unit can be used by + * other units defined with the prefix of the same type, unless no_prefix is provided for PT + * template parameter (in such a case it is impossible to define a prefix unit based on this + * one). + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + * @tparam Symbol a short text representation of the unit + * @tparam PT no_prefix or a type of prefix family + * @tparam R a scale to apply to U + * @tparam U a reference unit to scale + */ +template +struct scaled_unit : downcast_child>> { + static constexpr bool is_named = true; + static constexpr auto symbol = Symbol; + using prefix_type = PT; +}; - template - constexpr auto exp_count(dimension) - { - return sizeof...(Es); - } +/** + * @brief A prefixed unit + * + * Defines a new unit that is a scaled version of another unit by the provided prefix. It is + * only possible to create such a unit if the given prefix type matches the one defined in a + * reference unit. + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + * @tparam P prefix to be appied to the reference unit + * @tparam U reference unit + */ +template + requires std::same_as +// TODO replace with the below code when gcc will stop to crash on it ;-) +// struct prefixed_unit : scaled_unit, +// typename U::reference> {}; +struct prefixed_unit : + downcast_child>> { + static constexpr bool is_named = true; + static constexpr auto symbol = P::symbol + U::symbol; + using prefix_type = P::prefix_type; +}; - template - inline constexpr bool is_unit_of_base_dimension = (exp_count(typename U::dimension::base_type()) == 1); +/** + * @brief A unit with a deduced ratio and symbol + * + * Defines a new unit with a deduced ratio and symbol based on the recipe from the provided + * derived dimension. The number and order of provided units should match the recipe of + * dimension. + * + * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) + * @tparam Dim a derived dimension recipe to use for deduction + * @tparam U the unit of the first composite dimension from provided derived dimension's recipe + * @tparam URest the units for the rest of dimensions from the recipe + */ +template + requires detail::same_reference_units && + (U::is_named && (URest::is_named && ... && true)) +struct deduced_unit : downcast_child> { + static constexpr bool is_named = false; + static constexpr auto symbol = basic_fixed_string{""}; // detail::deduced_symbol_text(); + using prefix_type = no_prefix; +}; - template - inline constexpr bool are_units_of_base_dimension = (is_unit_of_base_dimension && ...); - - template - using deduced_derived_unit = - unit, - typename D::base_type, typename D::recipe>, Us...>::ratio>; - - template - requires (0 <= Value) && (Value < 10) - inline constexpr basic_fixed_string superscript_number = "\u2070"; - -// template<> inline constexpr basic_fixed_string superscript_number<0> = "\u2070"; - template<> inline constexpr basic_fixed_string superscript_number<1> = "\u00b9"; - template<> inline constexpr basic_fixed_string superscript_number<2> = "\u00b2"; - template<> inline constexpr basic_fixed_string superscript_number<3> = "\u00b3"; - template<> inline constexpr basic_fixed_string superscript_number<4> = "\u2074"; - template<> inline constexpr basic_fixed_string superscript_number<5> = "\u2075"; - template<> inline constexpr basic_fixed_string superscript_number<6> = "\u2076"; - template<> inline constexpr basic_fixed_string superscript_number<7> = "\u2077"; - template<> inline constexpr basic_fixed_string superscript_number<8> = "\u2078"; - template<> inline constexpr basic_fixed_string superscript_number<9> = "\u2079"; - - template - requires (Value >= 0) - constexpr auto superscript() - { - if constexpr(Value < 10) - return superscript_number; - else - return superscript() + superscript(); - } - - template - requires (Value >= 0) - constexpr auto regular() - { - if constexpr(Value < 10) - return basic_fixed_string(static_cast('0' + Value)); - else - return regular() + regular(); - } - - - template - constexpr auto ratio_text() - { - if constexpr(Ratio::num != 1 || Ratio::den != 1) { - auto txt = basic_fixed_string("[") + regular(); - if constexpr(Ratio::den == 1) { - return txt + basic_fixed_string("]"); - } - else { - return txt + basic_fixed_string("/") + regular() + basic_fixed_string("]"); - } - } - else { - return basic_fixed_string(""); - } - } - - template - constexpr auto prefix_or_ratio_text() - { - if constexpr(Ratio::num != 1 || Ratio::den != 1) { - if constexpr (!std::same_as) { - using prefix = downcast>; - - if constexpr(!std::same_as>) { - // print as a prefixed unit - return prefix::symbol; - } - else { - // print as a ratio of the coherent unit - return ratio_text(); - } - } - else { - // print as a ratio of the coherent unit - return ratio_text(); - } - } - } - - - template - constexpr auto operator_text() - { - if constexpr(Idx == 0) { - if constexpr(Divide) { - return basic_fixed_string("1/"); - } - else { - return basic_fixed_string(""); - } - } - else { - if constexpr(Divide) { - return basic_fixed_string("/"); - } - else { - return basic_fixed_string("⋅"); - } - } - } - - template - constexpr auto exp_text() - { - // get calculation operator + symbol - const auto txt = operator_text() + Symbol; - if constexpr(E::den != 1) { - // add root part - return txt + basic_fixed_string("^(") + regular() + basic_fixed_string("/") + regular() + basic_fixed_string(")"); - } - else if constexpr(abs(E::num) != 1) { - // add exponent part - return txt + superscript(); - } - else { - return txt; - } - } - - template - constexpr auto dimension_symbol() - { - if constexpr(BaseDimension) - return Dim::symbol; - else - // coherent derived unit - return downcast>>::symbol; - } - - template - constexpr auto base_symbol_text_impl(dimension, std::index_sequence) - { - return (exp_text(), Idxs>() + ...); - } - - template - constexpr auto base_symbol_text(dimension d) - { - return base_symbol_text_impl(d, std::index_sequence_for()); - } - - template - constexpr bool all_named(dimension) - { - return (downcast>>::is_named && ...); - } - - template - constexpr auto base_symbol_text() - { - using recipe = typename Dim::recipe; - if constexpr(all_named(recipe())) - return base_symbol_text(recipe()); - else - return base_symbol_text(Dim()); - } - - template - constexpr auto exp_validate_and_text() - { - 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(); - } - - template - constexpr auto deduced_symbol_text_impl(dimension, std::index_sequence) - { - return (exp_validate_and_text() + ...); - } - - template - constexpr auto deduced_symbol_text(dimension d) - { - 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() - { - if constexpr(!is_unit) { - // Unit is a downcasted derived unit child class already so just print defined symbol immediately - return Unit::symbol; - } - else { - // we are dealing with a non-user-defined unit here - using ratio = Unit::ratio; - using dim = Unit::dimension; - if constexpr(!is_dimension) { - // downcasted user-defined dimension - // print as a prefix or ratio of a coherent unit symbol defined by the user - using coherent_unit = downcast>>; - return prefix_or_ratio_text() + coherent_unit::symbol; - } - else { - // print as a ratio of a coherent unit + coherent unit dimensions and their exponents - return ratio_text() + base_symbol_text(dim{}); - } - } - } - - } // namespace detail - - - // derived_unit - - template - struct named_coherent_derived_unit : downcast_child>> { - static constexpr bool is_named = true; - static constexpr auto symbol = Symbol; - using prefix_type = PT; - }; - - template - struct coherent_derived_unit : downcast_child>> { - static constexpr bool is_named = false; - static constexpr auto symbol = detail::base_symbol_text(); - using prefix_type = no_prefix; - }; - - template - struct named_scaled_derived_unit : downcast_child> { - static constexpr bool is_named = true; - static constexpr auto symbol = Symbol; - using prefix_type = PT; - }; - - template - struct named_deduced_derived_unit : downcast_child> { - static constexpr bool is_named = true; - static constexpr auto symbol = Symbol; - using prefix_type = PT; - }; - - template - requires U::is_named && (Us::is_named && ... && true) - struct deduced_derived_unit : downcast_child> { - static constexpr bool is_named = false; - static constexpr auto symbol = detail::deduced_symbol_text(); - using prefix_type = no_prefix; - }; - - template - requires (!std::same_as) - struct prefixed_derived_unit : downcast_child>> { - static constexpr bool is_named = true; - static constexpr auto symbol = P::symbol + U::symbol; - using prefix_type = P::prefix_type; - }; +// template +// struct named_deduced_derived_unit : downcast_child> { +// static constexpr bool is_named = true; +// static constexpr auto symbol = Symbol; +// using prefix_type = PT; +// }; } // namespace units diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a68376b5..1a5b1883 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,6 +20,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -add_subdirectory(unit_test/runtime) +#add_subdirectory(unit_test/runtime) add_subdirectory(unit_test/static) -add_subdirectory(metabench) +#add_subdirectory(metabench) diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index 7aa64210..b793ab17 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -21,13 +21,16 @@ # SOFTWARE. add_library(unit_tests_static - cgs_test.cpp - custom_unit_test.cpp - dimension_test.cpp - math_test.cpp - quantity_test.cpp +# cgs_test.cpp +# custom_unit_test.cpp +# dimension_test.cpp +# fixed_string_test.cpp +# math_test.cpp +# new_design.cpp +# quantity_test.cpp ratio_test.cpp - type_list_test.cpp +# si_test.cpp +# type_list_test.cpp unit_test.cpp ) target_link_libraries(unit_tests_static diff --git a/test/unit_test/static/ratio_test.cpp b/test/unit_test/static/ratio_test.cpp index 997645c0..ec1eae7a 100644 --- a/test/unit_test/static/ratio_test.cpp +++ b/test/unit_test/static/ratio_test.cpp @@ -1,28 +1,28 @@ -// The MIT License (MIT) -// -// Copyright (c) 2018 Mateusz Pusz -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. + // The MIT License (MIT) + // + // Copyright (c) 2018 Mateusz Pusz + // + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to deal + // in the Software without restriction, including without limitation the rights + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + // + // The above copyright notice and this permission notice shall be included in all + // copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + // SOFTWARE. -#include "units/ratio.h" + #include "units/ratio.h" -namespace { + namespace { using namespace units; @@ -31,6 +31,8 @@ namespace { static_assert(same, ratio<1, 2>>); + static_assert(std::is_same_v, ratio<3, 8>>, ratio<3, 8>>); + static_assert(std::is_same_v, ratio<1>>, ratio<3, 8>>); static_assert(std::is_same_v, ratio<1, 8>>, ratio<1, 2>>); static_assert(std::is_same_v, ratio<1, 2>>, ratio<2>>); static_assert(std::is_same_v, ratio<2>>, ratio<1, 4>>); @@ -63,4 +65,4 @@ namespace { static_assert(std::is_same_v, ratio<1, 1000>>, ratio<1, 1000>>); static_assert(std::is_same_v, ratio<1>>, ratio<1, 1000>>); -} // namespace \ No newline at end of file + } // namespace \ No newline at end of file diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index 91d910ac..00172c9a 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -20,174 +20,36 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include +#include "units/unit.h" +#include "units/physical/si/prefixes.h" namespace { - using namespace units; +using namespace units; - /* ************** BASE DIMENSIONS **************** */ +struct metre : named_unit {}; +struct centimetre : prefixed_unit {}; +struct kilometre : prefixed_unit {}; +struct yard : scaled_unit, metre> {}; +struct foot : scaled_unit, yard> {}; +struct dim_length : base_dimension<"length", metre> {}; - // length +struct second : named_unit {}; +struct hour : scaled_unit, second> {}; +struct dim_time : base_dimension<"time", second> {}; - static_assert(1km == 1000m); - static_assert(1m == 100cm); - static_assert(1m == 1000mm); - static_assert(1km + 1m == 1001m); - static_assert(10km / 5km == 2); - static_assert(100mm / 5cm == 2); - static_assert(10km / 2 == 5km); +struct metre_per_second : unit {}; +struct dim_velocity : derived_dimension, exp> {}; +struct kilometre_per_hour : deduced_unit {}; - static_assert(1yd == 0.9144m); - static_assert(1yd == 3ft); - static_assert(1ft == 12in); - static_assert(1mi == 1760yd); +static_assert(std::is_same_v>>, metre>); +static_assert(std::is_same_v>>, centimetre>); +static_assert(std::is_same_v>>, yard>); +static_assert(std::is_same_v>>>, foot>); +static_assert(std::is_same_v>>, kilometre_per_hour>); - static_assert(5in + 8cm == 207mm); - - static_assert(millimetre::symbol == "mm"); - static_assert(centimetre::symbol == "cm"); - static_assert(kilometre::symbol == "km"); - - // mass - - static_assert(1kg == 1000g); - - static_assert(kilogram::symbol == "kg"); - - // time - - static_assert(1h == 3600s); - - static_assert(nanosecond::symbol == "ns"); - static_assert(microsecond::symbol == "µs"); - static_assert(millisecond::symbol == "ms"); - - // current - - // temperature - - // substance - - // luminous intensity - - - /* ************** DERIVED DIMENSIONS WITH NAMED UNITS **************** */ - - // frequency - - static_assert(2 / 1s == 2Hz); - static_assert(120 / 1min == 2Hz); - static_assert(1000 / 1s == 1kHz); - static_assert(1 / 1ms == 1kHz); - static_assert(3.2GHz == 3'200'000'000Hz); - static_assert(10Hz * 1min == 600); - - static_assert(millihertz::symbol == "mHz"); - static_assert(kilohertz::symbol == "kHz"); - static_assert(megahertz::symbol == "MHz"); - static_assert(gigahertz::symbol == "GHz"); - static_assert(terahertz::symbol == "THz"); - - // force - - static_assert(10kg * 10mps_sq == 100N); - - // pressure - - static_assert(10N / 10sq_m == 1Pa); - - // energy - - static_assert(10N * 10m == 100_J); - static_assert(10Pa * 10cub_m == 100_J); - - // power - - static_assert(10_J / 10s == 1W); - - // electric charge - - static_assert(10A * 10s == 100C); - - // voltage - - static_assert(10W / 10A == 1V); - static_assert(10_J / 10C == 1V); - - // capacitance - - static_assert(10C / 10V == 1F); - - /* ************** DERIVED DIMENSIONS IN TERMS OF BASE UNITS **************** */ - - // velocity - - static_assert(std::is_same_v>, std::int64_t>>); - - static_assert(10m / 5s == 2mps); - static_assert(10 / 5s * 1m == 2mps); - static_assert(1km / 1s == 1000mps); - // static_assert(1km / 1h == 1kmph); // should not compile - static_assert(1.0km / 1h == 1kmph); - static_assert(1000.0m / 3600.0s == 1kmph); - - static_assert(10.0mi / 2h == 5mph); - - static_assert(2kmph * 2h == 4km); - // static_assert(2kmph * 15min == 500m); // should not compile - static_assert(2kmph * 15.0min == 500m); - static_assert(2.0kmph * 15min == 500m); - - static_assert(2km / 2kmph == 1h); - // static_assert(2000m / 2kmph == 1h); // should not compile - static_assert(quantity_cast>(2000m) / 2kmph == 1h); - -// static_assert(metre_per_second::symbol == basic_fixed_string("m/s")); - // static_assert(kilometre_per_hour::symbol == basic_fixed_string("km/h")); - - - // acceleration - - static_assert(10mps / 10s == 1mps_sq); - - // area - - static_assert(1m * 1m == 1sq_m); - static_assert(10km * 10km == 100sq_km); - static_assert(1sq_m == 10'000sq_cm); - - // volume - - static_assert(1m * 1m * 1m == 1cub_m); - static_assert(10sq_m * 10m == 100cub_m); - static_assert(10km * 10km * 10km == 1000cub_km); - static_assert(1cub_m == 1'000'000cub_cm); - - - /* ************** DERIVED DIMENSIONS IN TERMS OF OTHER UNITS **************** */ - - static_assert(10N / 2m == 5Npm); +static_assert(centimetre::symbol == "cm"); +static_assert(kilometre::symbol == "km"); +static_assert(kilometre_per_hour::symbol == ""); } // namespace