From 1b4e8a21277f465f1e7339c919421481ca2dd09c Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sun, 1 Dec 2019 19:47:58 +0100 Subject: [PATCH 01/44] 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 From fa9b7db9d5b17b556240fb6b75eefc484002bcb8 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sun, 1 Dec 2019 19:56:23 +0100 Subject: [PATCH 02/44] type_list_test enabled --- test/unit_test/static/CMakeLists.txt | 2 +- test/unit_test/static/type_list_test.cpp | 107 ++++++++++++----------- 2 files changed, 57 insertions(+), 52 deletions(-) diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index b793ab17..7f4cb00d 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -30,7 +30,7 @@ add_library(unit_tests_static # quantity_test.cpp ratio_test.cpp # si_test.cpp -# type_list_test.cpp + type_list_test.cpp unit_test.cpp ) target_link_libraries(unit_tests_static diff --git a/test/unit_test/static/type_list_test.cpp b/test/unit_test/static/type_list_test.cpp index 12d89c50..6ee9688f 100644 --- a/test/unit_test/static/type_list_test.cpp +++ b/test/unit_test/static/type_list_test.cpp @@ -21,83 +21,88 @@ // SOFTWARE. #include "units/bits/type_list.h" -#include "units/dimension.h" +#include "units/derived_dimension.h" +#include "units/unit.h" #include namespace { - using namespace units; +using namespace units; - template - struct type_list; +template +struct type_list; - // type_list_push_front +// type_list_push_front - static_assert(std::is_same_v, int>, type_list>); - static_assert(std::is_same_v, int, long, double>, type_list>); - static_assert(std::is_same_v, int, long>, type_list>); +static_assert(std::is_same_v, int>, type_list>); +static_assert(std::is_same_v, int, long, double>, type_list>); +static_assert(std::is_same_v, int, long>, type_list>); - // type_list_push_back +// type_list_push_back - static_assert(std::is_same_v, int>, type_list>); - static_assert(std::is_same_v, int, long, double>, type_list>); - static_assert(std::is_same_v, int, long>, type_list>); +static_assert(std::is_same_v, int>, type_list>); +static_assert(std::is_same_v, int, long, double>, type_list>); +static_assert(std::is_same_v, int, long>, type_list>); - // type_list_split +// type_list_split - static_assert(std::is_same_v, 0>::first_list, type_list<>>); - static_assert(std::is_same_v, 0>::second_list, type_list>); +static_assert(std::is_same_v, 0>::first_list, type_list<>>); +static_assert(std::is_same_v, 0>::second_list, type_list>); - static_assert(std::is_same_v, 1>::first_list, type_list>); - static_assert(std::is_same_v, 1>::second_list, type_list<>>); +static_assert(std::is_same_v, 1>::first_list, type_list>); +static_assert(std::is_same_v, 1>::second_list, type_list<>>); - static_assert(std::is_same_v, 0>::first_list, type_list<>>); - static_assert(std::is_same_v, 0>::second_list, type_list>); +static_assert(std::is_same_v, 0>::first_list, type_list<>>); +static_assert(std::is_same_v, 0>::second_list, type_list>); - static_assert(std::is_same_v, 1>::first_list, type_list>); - static_assert(std::is_same_v, 1>::second_list, type_list>); +static_assert(std::is_same_v, 1>::first_list, type_list>); +static_assert(std::is_same_v, 1>::second_list, type_list>); - static_assert(std::is_same_v, 2>::first_list, type_list>); - static_assert(std::is_same_v, 2>::second_list, type_list<>>); +static_assert(std::is_same_v, 2>::first_list, type_list>); +static_assert(std::is_same_v, 2>::second_list, type_list<>>); - static_assert(std::is_same_v, 1>::first_list, type_list>); - static_assert(std::is_same_v, 1>::second_list, type_list>); +static_assert(std::is_same_v, 1>::first_list, type_list>); +static_assert(std::is_same_v, 1>::second_list, type_list>); - static_assert(std::is_same_v, 2>::first_list, type_list>); - static_assert(std::is_same_v, 2>::second_list, type_list>); +static_assert(std::is_same_v, 2>::first_list, type_list>); +static_assert(std::is_same_v, 2>::second_list, type_list>); - // type_list_split_half +// type_list_split_half - static_assert(std::is_same_v>::first_list, type_list>); - static_assert(std::is_same_v>::second_list, type_list<>>); +static_assert(std::is_same_v>::first_list, type_list>); +static_assert(std::is_same_v>::second_list, type_list<>>); - static_assert(std::is_same_v>::first_list, type_list>); - static_assert(std::is_same_v>::second_list, type_list>); +static_assert(std::is_same_v>::first_list, type_list>); +static_assert(std::is_same_v>::second_list, type_list>); - static_assert(std::is_same_v>::first_list, type_list>); - static_assert(std::is_same_v>::second_list, type_list>); +static_assert(std::is_same_v>::first_list, type_list>); +static_assert(std::is_same_v>::second_list, type_list>); - static_assert( - std::is_same_v>::first_list, type_list>); - static_assert( - std::is_same_v>::second_list, type_list>); +static_assert( + std::is_same_v>::first_list, type_list>); +static_assert( + std::is_same_v>::second_list, type_list>); - // type_list_merge_sorted - struct d0 : base_dimension<"d0", ""> {}; - struct d1 : base_dimension<"d1", ""> {}; +// type_list_merge_sorted +struct u0 : unit {}; +struct d0 : base_dimension<"d0", u0> {}; +struct u1 : unit {}; +struct d1 : base_dimension<"d1", u1> {}; - static_assert(std::is_same_v>, type_list>, exp_less>, - type_list, exp>>); - static_assert(std::is_same_v>, type_list>, exp_less>, - type_list, exp>>); +static_assert(std::is_same_v>, type_list>, exp_less>, + type_list, exp>>); +static_assert(std::is_same_v>, type_list>, exp_less>, + type_list, exp>>); - // type_list_sort +// type_list_sort - template - using exp_sort = type_list_sort; +template +using exp_sort = type_list_sort; - static_assert(std::is_same_v>>, dimension>>); - static_assert(std::is_same_v, exp>>, dimension, exp>>); - static_assert(std::is_same_v, exp>>, dimension, exp>>); +static_assert(std::is_same_v>>, derived_dimension>>); +static_assert( + std::is_same_v, exp>>, derived_dimension, exp>>); +static_assert( + std::is_same_v, exp>>, derived_dimension, exp>>); } // namespace From 0986d1e812f75ad898cba5738f13a42c4176490e Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sun, 1 Dec 2019 20:00:08 +0100 Subject: [PATCH 03/44] dimension_test enabled --- test/unit_test/static/CMakeLists.txt | 2 +- test/unit_test/static/dimension_test.cpp | 134 ++++++++++++++--------- 2 files changed, 82 insertions(+), 54 deletions(-) diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index 7f4cb00d..b0017fbc 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -23,7 +23,7 @@ add_library(unit_tests_static # cgs_test.cpp # custom_unit_test.cpp -# dimension_test.cpp + dimension_test.cpp # fixed_string_test.cpp # math_test.cpp # new_design.cpp diff --git a/test/unit_test/static/dimension_test.cpp b/test/unit_test/static/dimension_test.cpp index c476ce04..94cf5ea5 100644 --- a/test/unit_test/static/dimension_test.cpp +++ b/test/unit_test/static/dimension_test.cpp @@ -20,79 +20,107 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/dimension.h" +#include "units/derived_dimension.h" +#include "units/unit.h" #include using namespace units; namespace { - struct d0 : base_dimension<"d0", ""> {}; - struct d1 : base_dimension<"d1", ""> {}; - struct d2 : base_dimension<"d2", ""> {}; - struct d3 : base_dimension<"d3", ""> {}; +struct u0 : unit {}; +struct d0 : base_dimension<"d0", u0> {}; +struct u1 : unit {}; +struct d1 : base_dimension<"d1", u1> {}; +struct u2 : unit {}; +struct d2 : base_dimension<"d2", u2> {}; +struct u3 : unit {}; +struct d3 : base_dimension<"d3", u3> {}; - // exp_invert +// exp_invert - static_assert(std::is_same_v>, exp>); - static_assert(std::is_same_v>, exp>); +static_assert(std::is_same_v>, exp>); +static_assert(std::is_same_v>, exp>); - // extract +// extract - template - struct typeinfo; +template +struct typeinfo; - template - using extract = detail::extract::type; +template +using extract = detail::extract::type; - static_assert(std::is_same_v, dimension<>>); - static_assert(std::is_same_v>, dimension>>); - static_assert(std::is_same_v, exp>, dimension, exp>>); - using dim0 = dimension<>; - using dim1 = dimension>; - using dim2 = dimension, exp>; - static_assert(std::is_same_v, exp>, dimension>>); - static_assert(std::is_same_v, exp>, dimension, exp>>); - static_assert(std::is_same_v, exp, exp>, dimension, exp, exp, exp>>); +static_assert(std::is_same_v, derived_dimension<>>); +static_assert(std::is_same_v>, derived_dimension>>); +static_assert(std::is_same_v, exp>, derived_dimension, exp>>); +using dim0 = derived_dimension<>; +using dim1 = derived_dimension>; +using dim2 = derived_dimension, exp>; +static_assert(std::is_same_v, exp>, derived_dimension>>); +static_assert(std::is_same_v, exp>, derived_dimension, exp>>); +static_assert(std::is_same_v, exp, exp>, + derived_dimension, exp, exp, exp>>); - // make_dimension +// dim_invert +static_assert(std::is_same_v>>, d0>); +static_assert(std::is_same_v>>, derived_dimension>>); +static_assert( + std::is_same_v, exp>>, derived_dimension, exp>>); - template - using make_dimension = detail::make_dimension; +// make_dimension - static_assert(std::is_same_v>, dimension>>); - static_assert(std::is_same_v, exp>, dimension, exp>>); - static_assert(std::is_same_v, exp>, dimension, exp>>); - static_assert(std::is_same_v, exp>, dimension>>); - static_assert(std::is_same_v, exp>, dimension<>>); - static_assert(std::is_same_v, exp>, dimension>>); - static_assert(std::is_same_v, exp>, dimension>>); - static_assert(std::is_same_v, exp>, dimension>>); +template +using make_dimension = detail::make_dimension; - static_assert(std::is_same_v, exp, exp, exp>, dimension, exp>>); - static_assert( - std::is_same_v, exp, exp, exp>, dimension, exp>>); +static_assert(std::is_same_v>, derived_dimension>>); +static_assert(std::is_same_v, exp>, derived_dimension, exp>>); +static_assert(std::is_same_v, exp>, derived_dimension, exp>>); +static_assert(std::is_same_v, exp>, derived_dimension>>); +static_assert(std::is_same_v, exp>, derived_dimension<>>); +static_assert(std::is_same_v, exp>, derived_dimension>>); +static_assert(std::is_same_v, exp>, derived_dimension>>); +static_assert(std::is_same_v, exp>, derived_dimension>>); - static_assert(std::is_same_v, exp, exp>, dimension>>); - static_assert(std::is_same_v, exp, exp>, dimension>>); - static_assert(std::is_same_v, exp, exp>, dimension>>); - static_assert(std::is_same_v, exp, exp, exp>, dimension<>>); +static_assert(std::is_same_v, exp, exp, exp>, + derived_dimension, exp>>); +static_assert(std::is_same_v, exp, exp, exp>, + derived_dimension, exp>>); - // dimension_multiply +static_assert(std::is_same_v, exp, exp>, derived_dimension>>); +static_assert(std::is_same_v, exp, exp>, derived_dimension>>); +static_assert(std::is_same_v, exp, exp>, derived_dimension>>); +static_assert(std::is_same_v, exp, exp, exp>, derived_dimension<>>); - static_assert( - std::is_same_v>, dimension>>, dimension, exp>>); - static_assert(std::is_same_v, exp, exp>, dimension>>, - dimension, exp, exp, exp>>); - static_assert(std::is_same_v, exp, exp>, dimension>>, - dimension, exp, exp>>); - static_assert(std::is_same_v, exp, exp>, dimension>>, - dimension, exp>>); +// dimension_multiply - // dimension_divide +static_assert(std::is_same_v>, derived_dimension>>, + derived_dimension, exp>>); +static_assert( + std::is_same_v>, d1>, derived_dimension, exp>>); +static_assert( + std::is_same_v>>, derived_dimension, exp>>); +static_assert(std::is_same_v, derived_dimension, exp>>); +static_assert(std::is_same_v< + dimension_multiply, exp, exp>, derived_dimension>>, + derived_dimension, exp, exp, exp>>); +static_assert(std::is_same_v< + dimension_multiply, exp, exp>, derived_dimension>>, + derived_dimension, exp, exp>>); +static_assert(std::is_same_v< + dimension_multiply, exp, exp>, derived_dimension>>, + derived_dimension, exp>>); +static_assert(std::is_same_v>, derived_dimension>>, + derived_dimension<>>); +static_assert(std::is_same_v>, derived_dimension>>, d0>); - static_assert( - std::is_same_v>, dimension>>, dimension, exp>>); - static_assert(std::is_same_v>, dimension>>, dimension<>>); +// dimension_divide + +static_assert(std::is_same_v>, derived_dimension>>, + derived_dimension, exp>>); +static_assert(std::is_same_v>, derived_dimension>>, + derived_dimension<>>); +static_assert(std::is_same_v>, derived_dimension>>, + derived_dimension<>>); +static_assert(std::is_same_v>, derived_dimension>>, d0>); } // namespace From c48bfe2098a9d410308cc892feacf7aa1615c3cc Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 4 Dec 2019 17:46:19 +0100 Subject: [PATCH 04/44] quantity refactored and quantity_test enabled --- src/include/units/base_dimension.h | 5 +- src/include/units/bits/customization_points.h | 115 --- src/include/units/{bits => }/concepts.h | 2 +- src/include/units/customization_points.h | 115 +++ src/include/units/derived_dimension.h | 10 +- src/include/units/dimensions/area.h | 64 -- src/include/units/dimensions/frequency.h | 71 -- src/include/units/dimensions/length.h | 88 -- src/include/units/dimensions/physical.h | 124 +++ .../units/dimensions/si_base_dimensions.h | 37 - src/include/units/dimensions/time.h | 71 -- src/include/units/dimensions/velocity.h | 55 -- src/include/units/math.h | 18 +- src/include/units/physical/dimensions.h | 151 +++ src/include/units/physical/si/area.h | 65 ++ src/include/units/physical/si/frequency.h | 70 ++ src/include/units/physical/si/length.h | 86 ++ src/include/units/physical/si/time.h | 70 ++ src/include/units/physical/si/velocity.h | 56 ++ src/include/units/quantity.h | 930 ++++++++++-------- src/include/units/unit.h | 10 +- test/unit_test/static/CMakeLists.txt | 2 +- test/unit_test/static/quantity_test.cpp | 458 ++++----- 23 files changed, 1499 insertions(+), 1174 deletions(-) delete mode 100644 src/include/units/bits/customization_points.h rename src/include/units/{bits => }/concepts.h (97%) create mode 100644 src/include/units/customization_points.h delete mode 100644 src/include/units/dimensions/area.h delete mode 100644 src/include/units/dimensions/frequency.h delete mode 100644 src/include/units/dimensions/length.h create mode 100644 src/include/units/dimensions/physical.h delete mode 100644 src/include/units/dimensions/si_base_dimensions.h delete mode 100644 src/include/units/dimensions/time.h delete mode 100644 src/include/units/dimensions/velocity.h create mode 100644 src/include/units/physical/dimensions.h create mode 100644 src/include/units/physical/si/area.h create mode 100644 src/include/units/physical/si/frequency.h create mode 100644 src/include/units/physical/si/length.h create mode 100644 src/include/units/physical/si/time.h create mode 100644 src/include/units/physical/si/velocity.h diff --git a/src/include/units/base_dimension.h b/src/include/units/base_dimension.h index 6fbde960..3e6b03a7 100644 --- a/src/include/units/base_dimension.h +++ b/src/include/units/base_dimension.h @@ -24,7 +24,6 @@ #include #include -#include #include namespace units { @@ -46,7 +45,7 @@ namespace units { */ template struct base_dimension { - using base_type = base_dimension; + using base_type_workaround = base_dimension; // TODO Replace with is_derived_from_instantiation when fixed static constexpr auto name = Name; using coherent_unit = U; }; @@ -63,7 +62,7 @@ inline constexpr bool is_base_dimension> = true; } // namespace detail template -concept BaseDimension = detail::is_base_dimension; +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 diff --git a/src/include/units/bits/customization_points.h b/src/include/units/bits/customization_points.h deleted file mode 100644 index acc33c3d..00000000 --- a/src/include/units/bits/customization_points.h +++ /dev/null @@ -1,115 +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 { - - // treat_as_floating_point - - template // TODO Conceptify that - inline constexpr bool treat_as_floating_point = std::is_floating_point_v; - - // isnan - namespace isnan_impl { - - // non-ADL lookup block - void isnan(); // undefined - - template - inline constexpr bool has_customization = false; - - template - requires requires(const T& t) { - { isnan(t) } -> bool; - } - inline constexpr bool has_customization = true; - - struct fn { - template - constexpr bool operator()(const T&) const - { - return false; - } - - template - requires treat_as_floating_point - constexpr bool operator()(const T& value) const - { - return std::isnan(value); - } - - template - requires treat_as_floating_point && has_customization - constexpr bool operator()(const T& value) const - { - return isnan(value); // uses ADL - } - }; - } - - inline constexpr isnan_impl::fn isnan{}; - - // isfinite - namespace isfinite_impl { - - // non-ADL lookup block - void isfinite(); // undefined - - template - inline constexpr bool has_customization = false; - - template - requires requires(const T& t) { - { isfinite(t) } -> bool; - } - inline constexpr bool has_customization = true; - - struct fn { - template - constexpr bool operator()(const T&) const - { - return true; - } - - template - requires treat_as_floating_point - constexpr bool operator()(const T& value) const - { - return std::isfinite(value); - } - - template - requires treat_as_floating_point && has_customization - constexpr bool operator()(const T& value) const - { - return isfinite(value); // uses ADL - } - }; - } - - inline constexpr isfinite_impl::fn isfinite{}; - -} diff --git a/src/include/units/bits/concepts.h b/src/include/units/concepts.h similarity index 97% rename from src/include/units/bits/concepts.h rename to src/include/units/concepts.h index 44b07f31..fcc16f35 100644 --- a/src/include/units/bits/concepts.h +++ b/src/include/units/concepts.h @@ -24,7 +24,7 @@ #include #include -#include +#include #include namespace units { diff --git a/src/include/units/customization_points.h b/src/include/units/customization_points.h new file mode 100644 index 00000000..9613d554 --- /dev/null +++ b/src/include/units/customization_points.h @@ -0,0 +1,115 @@ +// 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 { + + // treat_as_floating_point + + template // TODO Conceptify that + inline constexpr bool treat_as_floating_point = std::is_floating_point_v; + + // // isnan + // namespace isnan_impl { + + // // non-ADL lookup block + // void isnan(); // undefined + + // template + // inline constexpr bool has_customization = false; + + // template + // requires requires(const T& t) { + // { isnan(t) } -> bool; + // } + // inline constexpr bool has_customization = true; + + // struct fn { + // template + // constexpr bool operator()(const T&) const + // { + // return false; + // } + + // template + // requires treat_as_floating_point + // constexpr bool operator()(const T& value) const + // { + // return std::isnan(value); + // } + + // template + // requires treat_as_floating_point && has_customization + // constexpr bool operator()(const T& value) const + // { + // return isnan(value); // uses ADL + // } + // }; + // } + + // inline constexpr isnan_impl::fn isnan{}; + + // // isfinite + // namespace isfinite_impl { + + // // non-ADL lookup block + // void isfinite(); // undefined + + // template + // inline constexpr bool has_customization = false; + + // template + // requires requires(const T& t) { + // { isfinite(t) } -> bool; + // } + // inline constexpr bool has_customization = true; + + // struct fn { + // template + // constexpr bool operator()(const T&) const + // { + // return true; + // } + + // template + // requires treat_as_floating_point + // constexpr bool operator()(const T& value) const + // { + // return std::isfinite(value); + // } + + // template + // requires treat_as_floating_point && has_customization + // constexpr bool operator()(const T& value) const + // { + // return isfinite(value); // uses ADL + // } + // }; + // } + + // inline constexpr isfinite_impl::fn isfinite{}; + +} diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h index 848ab4e0..78f109ec 100644 --- a/src/include/units/derived_dimension.h +++ b/src/include/units/derived_dimension.h @@ -277,10 +277,14 @@ struct dim_invert_impl> { using type = downcast...>>; }; +template +struct dim_invert_impl : dim_invert_impl> { +}; + } // namespace detail template -using dim_invert = detail::dim_invert_impl>::type; +using dim_invert = detail::dim_invert_impl::type; // dimension_multiply namespace detail { @@ -377,7 +381,7 @@ struct dimension_pow_impl; template struct dimension_pow_impl { - using type = derived_dimension>; + using type = downcast>>; }; template @@ -387,7 +391,7 @@ struct dimension_pow_impl, N> { template struct dimension_pow_impl { - using type = dimension_pow_impl; + using type = dimension_pow_impl, N>; }; template diff --git a/src/include/units/dimensions/area.h b/src/include/units/dimensions/area.h deleted file mode 100644 index 1a8e8830..00000000 --- a/src/include/units/dimensions/area.h +++ /dev/null @@ -1,64 +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 - -namespace units { - - struct area : derived_dimension> {}; - - template - concept Area = QuantityOf; - - struct square_metre : coherent_derived_unit {}; - struct square_millimetre : deduced_derived_unit {}; - struct square_centimetre : deduced_derived_unit {}; - struct square_kilometre : deduced_derived_unit {}; - struct square_foot : deduced_derived_unit {}; - - inline namespace literals { - - // sq_m - constexpr auto operator""sq_m(unsigned long long l) { return quantity(l); } - constexpr auto operator""sq_m(long double l) { return quantity(l); } - - // sq_mm - constexpr auto operator""sq_mm(unsigned long long l) { return quantity(l); } - constexpr auto operator""sq_mm(long double l) { return quantity(l); } - - // sq_cm - constexpr auto operator""sq_cm(unsigned long long l) { return quantity(l); } - constexpr auto operator""sq_cm(long double l) { return quantity(l); } - - // sq_km - constexpr auto operator""sq_km(unsigned long long l) { return quantity(l); } - constexpr auto operator""sq_km(long double l) { return quantity(l); } - - // sq_ft - constexpr auto operator""sq_ft(unsigned long long l) { return quantity(l); } - constexpr auto operator""sq_ft(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/frequency.h b/src/include/units/dimensions/frequency.h deleted file mode 100644 index 3594a22b..00000000 --- a/src/include/units/dimensions/frequency.h +++ /dev/null @@ -1,71 +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 - -namespace units { - - struct frequency : derived_dimension> {}; - - template - concept Frequency = QuantityOf; - - struct hertz : named_coherent_derived_unit {}; - struct millihertz : prefixed_derived_unit {}; - struct kilohertz : prefixed_derived_unit {}; - struct megahertz : prefixed_derived_unit {}; - struct gigahertz : prefixed_derived_unit {}; - struct terahertz : prefixed_derived_unit {}; - - inline namespace literals { - - // Hz - constexpr auto operator""Hz(unsigned long long l) { return quantity(l); } - constexpr auto operator""Hz(long double l) { return quantity(l); } - - // mHz - constexpr auto operator""mHz(unsigned long long l) { return quantity(l); } - constexpr auto operator""mHz(long double l) { return quantity(l); } - - // kHz - constexpr auto operator""kHz(unsigned long long l) { return quantity(l); } - constexpr auto operator""kHz(long double l) { return quantity(l); } - - // MHz - constexpr auto operator""MHz(unsigned long long l) { return quantity(l); } - constexpr auto operator""MHz(long double l) { return quantity(l); } - - // GHz - constexpr auto operator""GHz(unsigned long long l) { return quantity(l); } - constexpr auto operator""GHz(long double l) { return quantity(l); } - - // THz - constexpr auto operator""THz(unsigned long long l) { return quantity(l); } - constexpr auto operator""THz(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/length.h b/src/include/units/dimensions/length.h deleted file mode 100644 index d5ebb34a..00000000 --- a/src/include/units/dimensions/length.h +++ /dev/null @@ -1,88 +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 - -namespace units { - - struct length : derived_dimension> {}; - - template - concept Length = QuantityOf; - - // SI units - struct metre : named_coherent_derived_unit {}; - struct millimetre : prefixed_derived_unit {}; - struct centimetre : prefixed_derived_unit {}; - struct kilometre : prefixed_derived_unit {}; - - inline namespace literals { - - // m - constexpr auto operator""m(unsigned long long l) { return quantity(l); } - constexpr auto operator""m(long double l) { return quantity(l); } - - // mm - constexpr auto operator""mm(unsigned long long l) { return quantity(l); } - constexpr auto operator""mm(long double l) { return quantity(l); } - - // cm - constexpr auto operator""cm(unsigned long long l) { return quantity(l); } - constexpr auto operator""cm(long double l) { return quantity(l); } - - // km - constexpr auto operator""km(unsigned long long l) { return quantity(l); } - constexpr auto operator""km(long double l) { return quantity(l); } - - } // namespace literals - - // US customary units - struct yard : named_scaled_derived_unit> {}; - struct foot : named_scaled_derived_unit>> {}; - struct inch : named_scaled_derived_unit>> {}; - struct mile : named_scaled_derived_unit, yard::ratio>> {}; - - inline namespace literals { - - // yd - constexpr auto operator""yd(unsigned long long l) { return quantity(l); } - constexpr auto operator""yd(long double l) { return quantity(l); } - - // ft - constexpr auto operator""ft(unsigned long long l) { return quantity(l); } - constexpr auto operator""ft(long double l) { return quantity(l); } - - // in - constexpr auto operator""in(unsigned long long l) { return quantity(l); } - constexpr auto operator""in(long double l) { return quantity(l); } - - // mi - constexpr auto operator""mi(unsigned long long l) { return quantity(l); } - constexpr auto operator""mi(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/physical.h b/src/include/units/dimensions/physical.h new file mode 100644 index 00000000..d92228a1 --- /dev/null +++ b/src/include/units/dimensions/physical.h @@ -0,0 +1,124 @@ +// 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 { + + // base dimension + + template + struct base_dimension { + static constexpr auto name = Name; + }; + + template + concept BaseDimension = true; + + // base dimension + + template> + struct base_unit { + static constexpr auto symbol = Symbol; + using dimension = Dim; + using prefix_type = PT; + using ratio = R; + }; + + template + concept BaseUnit = true; + + template + concept BaseUnitOf = BaseUnit && BaseDimension && std::same_as; + + + + namespace physical { + + // base dimensions + struct base_dim_length : base_dimension<"length"> {}; + struct base_dim_mass : base_dimension<"mass"> {}; + struct base_dim_time : base_dimension<"time"> {}; + struct base_dim_current : base_dimension<"current"> {}; + struct base_dim_temperature : base_dimension<"temperature"> {}; + struct base_dim_substance : base_dimension<"substance"> {}; + struct base_dim_luminous_intensity : base_dimension<"luminous intensity"> {}; + + + // dimensions + template L> + struct length : derived_dimension> {}; + + template M> + struct mass : derived_dimension> {}; + + template T> + struct time : derived_dimension> {}; + + template L, UnitOf T> + struct velocity : derived_dimension, exp> {}; + + template L, UnitOf T> + struct acceleration : derived_dimension, exp> {}; + + template M, UnitOf A> + struct force : derived_dimension, exp> {}; + + } // physical + + // SI + namespace si { + struct si_prefix; + + // length + struct metre : base_unit<"m", base_dim_length, si_prefix> {}; + struct length : physical::length {}; + + // mass + struct kilogram : base_unit<"kg", base_dim_mass, si_prefix> {}; + struct mass : physical::mass {}; + + // time + struct second : base_unit<"s", base_dim_time, si_prefix> {}; + struct time : physical::time {}; + + struct nanosecond : prefixed_derived_unit {}; + struct microsecond : prefixed_derived_unit {}; + struct millisecond : prefixed_derived_unit {}; + struct minute : named_derived_unit> {}; + struct hour : named_derived_unit> {}; + + // velocity + struct velocity : physical::velocity; + + // acceleration + struct acceleration : physical::acceleration; + + // acceleration + struct acceleration : physical::acceleration; + + } + +} // namespace units diff --git a/src/include/units/dimensions/si_base_dimensions.h b/src/include/units/dimensions/si_base_dimensions.h deleted file mode 100644 index ef2ea824..00000000 --- a/src/include/units/dimensions/si_base_dimensions.h +++ /dev/null @@ -1,37 +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 - -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/time.h b/src/include/units/dimensions/time.h deleted file mode 100644 index 3c60e384..00000000 --- a/src/include/units/dimensions/time.h +++ /dev/null @@ -1,71 +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 - -namespace units { - - struct time : derived_dimension> {}; - - template - concept Time = QuantityOf; - - struct second : named_coherent_derived_unit {}; - struct nanosecond : prefixed_derived_unit {}; - struct microsecond : prefixed_derived_unit {}; - struct millisecond : prefixed_derived_unit {}; - struct minute : named_scaled_derived_unit> {}; - struct hour : named_scaled_derived_unit> {}; - - inline namespace literals { - - // ns - constexpr auto operator""ns(unsigned long long l) { return quantity(l); } - constexpr auto operator""ns(long double l) { return quantity(l); } - - // us - constexpr auto operator""us(unsigned long long l) { return quantity(l); } - constexpr auto operator""us(long double l) { return quantity(l); } - - // ms - constexpr auto operator""ms(unsigned long long l) { return quantity(l); } - constexpr auto operator""ms(long double l) { return quantity(l); } - - // s - constexpr auto operator""s(unsigned long long l) { return quantity(l); } - constexpr auto operator""s(long double l) { return quantity(l); } - - // min - constexpr auto operator""min(unsigned long long l) { return quantity(l); } - constexpr auto operator""min(long double l) { return quantity(l); } - - // h - constexpr auto operator""h(unsigned long long l) { return quantity(l); } - constexpr auto operator""h(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/velocity.h b/src/include/units/dimensions/velocity.h deleted file mode 100644 index e583fba9..00000000 --- a/src/include/units/dimensions/velocity.h +++ /dev/null @@ -1,55 +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 { - - struct velocity : derived_dimension, exp> {}; - - template - concept Velocity = QuantityOf; - - struct metre_per_second : coherent_derived_unit {}; - struct kilometre_per_hour : deduced_derived_unit {}; - struct mile_per_hour : deduced_derived_unit {}; - - inline namespace literals { - - // mps - constexpr auto operator""mps(unsigned long long l) { return quantity(l); } - constexpr auto operator""mps(long double l) { return quantity(l); } - - // kmph - constexpr auto operator""kmph(unsigned long long l) { return quantity(l); } - constexpr auto operator""kmph(long double l) { return quantity(l); } - - // mph - constexpr auto operator""mph(unsigned long long l) { return quantity(l); } - constexpr auto operator""mph(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/math.h b/src/include/units/math.h index 1fd79d17..be9274cd 100644 --- a/src/include/units/math.h +++ b/src/include/units/math.h @@ -27,27 +27,27 @@ namespace units { - template + template requires N == 0 - inline Rep AUTO pow(const quantity&) noexcept + inline Rep AUTO pow(const quantity&) noexcept { return 1; } - template - inline Quantity AUTO pow(const quantity& q) noexcept + template + inline Quantity AUTO pow(const quantity& q) noexcept { - using dim = dimension_pow; + 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 - inline Quantity AUTO sqrt(const quantity& q) noexcept + template + inline Quantity AUTO sqrt(const quantity& q) noexcept { 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/physical/dimensions.h b/src/include/units/physical/dimensions.h new file mode 100644 index 00000000..7945dc8b --- /dev/null +++ b/src/include/units/physical/dimensions.h @@ -0,0 +1,151 @@ +// 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 { + +inline namespace physical { + +template typename DimTemplate> +concept DimensionOf = (Dimension || BaseDimension) && is_derived_from_instantiation; + +template typename DimTemplate> +concept QuantityOf = Quantity && is_derived_from_instantiation; + +// ------------------------ base dimensions ----------------------------- + +// length +template +struct dim_length : base_dimension<"length", U> {}; + +template +concept Length = QuantityOf; + +// mass +template +struct dim_mass : base_dimension<"mass", U> {}; + +template +concept Mass = QuantityOf; + +// time +template +struct dim_time : base_dimension<"time", U> {}; + +template +concept Time = QuantityOf; + +// current +template +struct dim_current : base_dimension<"current", U> {}; + +template +concept Current = QuantityOf; + +// temperature +template +struct dim_temperature : base_dimension<"temperature", U> {}; + +template +concept Temperature = QuantityOf; + +// substance +template +struct dim_substance : base_dimension<"substance", U> {}; + +template +concept Substance = QuantityOf; + +// luminous intensity +template +struct dim_luminous_intensity : base_dimension<"luminous intensity", U> {}; + +template +concept LuminousIntensity = QuantityOf; + +// ------------------------ derived dimensions ----------------------------- + +// frequency +template T> +struct dim_frequency : derived_dimension> {}; + +template +concept Frequency = QuantityOf; + +// area +template L> +struct dim_area : derived_dimension> {}; + +template +concept Area = QuantityOf; + +// velocity +template L, DimensionOf T> +struct dim_velocity : derived_dimension, exp> {}; + +template +concept Velocity = QuantityOf; + +// acceleration +template L, DimensionOf T> +struct dim_acceleration : derived_dimension, exp> {}; + +template +concept Acceleration = QuantityOf; + +// force +template M, DimensionOf A> +struct dim_force : derived_dimension, exp> {}; + +template +concept Force = QuantityOf; + +// energy +template F, DimensionOf L> +struct dim_energy : derived_dimension, exp> {}; + +template +concept Energy = QuantityOf; + +// power +template E, DimensionOf T> +struct dim_power : derived_dimension, exp> {}; + +template +concept Power = QuantityOf; + +// pressure +template F, DimensionOf A> +struct dim_pressure : derived_dimension, exp> {}; + +template +concept Pressure = QuantityOf; + +} // namespace physical + +} // namespace units diff --git a/src/include/units/physical/si/area.h b/src/include/units/physical/si/area.h new file mode 100644 index 00000000..52a915ed --- /dev/null +++ b/src/include/units/physical/si/area.h @@ -0,0 +1,65 @@ +// 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 square_metre : unit {}; +struct dim_area : physical::dim_area {}; + +struct square_millimetre : deduced_unit {}; +struct square_centimetre : deduced_unit {}; +struct square_kilometre : deduced_unit {}; +struct square_foot : deduced_unit {}; + +template +using area = quantity; + +inline namespace literals { + +// sq_m +constexpr auto operator"" sq_m(unsigned long long l) { return area(l); } +constexpr auto operator"" sq_m(long double l) { return area(l); } + +// sq_mm +constexpr auto operator"" sq_mm(unsigned long long l) { return area(l); } +constexpr auto operator"" sq_mm(long double l) { return area(l); } + +// sq_cm +constexpr auto operator"" sq_cm(unsigned long long l) { return area(l); } +constexpr auto operator"" sq_cm(long double l) { return area(l); } + +// sq_km +constexpr auto operator"" sq_km(unsigned long long l) { return area(l); } +constexpr auto operator"" sq_km(long double l) { return area(l); } + +// sq_ft +constexpr auto operator"" sq_ft(unsigned long long l) { return area(l); } +constexpr auto operator"" sq_ft(long double l) { return area(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/frequency.h b/src/include/units/physical/si/frequency.h new file mode 100644 index 00000000..902641d2 --- /dev/null +++ b/src/include/units/physical/si/frequency.h @@ -0,0 +1,70 @@ +// 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 hertz : named_unit {}; +struct millihertz : prefixed_unit {}; +struct kilohertz : prefixed_unit {}; +struct megahertz : prefixed_unit {}; +struct gigahertz : prefixed_unit {}; +struct terahertz : prefixed_unit {}; + +struct dim_frequency : physical::dim_frequency {}; + +template +using frequency = quantity; + +inline namespace literals { + +// Hz +constexpr auto operator"" Hz(unsigned long long l) { return frequency(l); } +constexpr auto operator"" Hz(long double l) { return frequency(l); } + +// mHz +constexpr auto operator"" mHz(unsigned long long l) { return frequency(l); } +constexpr auto operator"" mHz(long double l) { return frequency(l); } + +// kHz +constexpr auto operator"" kHz(unsigned long long l) { return frequency(l); } +constexpr auto operator"" kHz(long double l) { return frequency(l); } + +// MHz +constexpr auto operator"" MHz(unsigned long long l) { return frequency(l); } +constexpr auto operator"" MHz(long double l) { return frequency(l); } + +// GHz +constexpr auto operator"" GHz(unsigned long long l) { return frequency(l); } +constexpr auto operator"" GHz(long double l) { return frequency(l); } + +// THz +constexpr auto operator"" THz(unsigned long long l) { return frequency(l); } +constexpr auto operator"" THz(long double l) { return frequency(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/length.h b/src/include/units/physical/si/length.h new file mode 100644 index 00000000..bd9c27df --- /dev/null +++ b/src/include/units/physical/si/length.h @@ -0,0 +1,86 @@ +// 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 metre : named_unit {}; +struct millimetre : prefixed_unit {}; +struct centimetre : prefixed_unit {}; +struct kilometre : prefixed_unit {}; + +struct dim_length : physical::dim_length {}; + +template +using length = quantity; + +inline namespace literals { + +// m +constexpr auto operator"" m(unsigned long long l) { return length(l); } +constexpr auto operator"" m(long double l) { return length(l); } + +// mm +constexpr auto operator"" mm(unsigned long long l) { return length(l); } +constexpr auto operator"" mm(long double l) { return length(l); } + +// cm +constexpr auto operator"" cm(unsigned long long l) { return length(l); } +constexpr auto operator"" cm(long double l) { return length(l); } + +// km +constexpr auto operator"" km(unsigned long long l) { return length(l); } +constexpr auto operator"" km(long double l) { return length(l); } + +} // namespace literals + +// US customary units +struct yard : scaled_unit, metre> {}; +struct foot : scaled_unit, yard> {}; +struct inch : scaled_unit, foot> {}; +struct mile : scaled_unit, yard> {}; + +inline namespace literals { + +// yd +constexpr auto operator"" yd(unsigned long long l) { return length(l); } +constexpr auto operator"" yd(long double l) { return length(l); } + +// ft +constexpr auto operator"" ft(unsigned long long l) { return length(l); } +constexpr auto operator"" ft(long double l) { return length(l); } + +// in +constexpr auto operator"" in(unsigned long long l) { return length(l); } +constexpr auto operator"" in(long double l) { return length(l); } + +// mi +constexpr auto operator"" mi(unsigned long long l) { return length(l); } +constexpr auto operator"" mi(long double l) { return length(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/time.h b/src/include/units/physical/si/time.h new file mode 100644 index 00000000..0e1b409e --- /dev/null +++ b/src/include/units/physical/si/time.h @@ -0,0 +1,70 @@ +// 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 second : named_unit {}; +struct nanosecond : prefixed_unit {}; +struct microsecond : prefixed_unit {}; +struct millisecond : prefixed_unit {}; +struct minute : scaled_unit, second> {}; +struct hour : scaled_unit, second> {}; + +struct dim_time : physical::dim_time {}; + +template +using time = quantity; + +inline namespace literals { + +// ns +constexpr auto operator""ns(unsigned long long l) { return time(l); } +constexpr auto operator""ns(long double l) { return time(l); } + +// us +constexpr auto operator""us(unsigned long long l) { return time(l); } +constexpr auto operator""us(long double l) { return time(l); } + +// ms +constexpr auto operator""ms(unsigned long long l) { return time(l); } +constexpr auto operator""ms(long double l) { return time(l); } + +// s +constexpr auto operator""s(unsigned long long l) { return time(l); } +constexpr auto operator""s(long double l) { return time(l); } + +// min +constexpr auto operator""min(unsigned long long l) { return time(l); } +constexpr auto operator""min(long double l) { return time(l); } + +// h +constexpr auto operator""h(unsigned long long l) { return time(l); } +constexpr auto operator""h(long double l) { return time(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/velocity.h b/src/include/units/physical/si/velocity.h new file mode 100644 index 00000000..bb551919 --- /dev/null +++ b/src/include/units/physical/si/velocity.h @@ -0,0 +1,56 @@ +// 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 + +namespace units::si { + +struct metre_per_second : unit {}; +struct dim_velocity : physical::dim_velocity {}; + +struct kilometre_per_hour : deduced_unit {}; +struct mile_per_hour : deduced_unit {}; + +template +using velocity = quantity; + +inline namespace literals { + +// mps +constexpr auto operator"" mps(unsigned long long l) { return velocity(l); } +constexpr auto operator"" mps(long double l) { return velocity(l); } + +// kmph +constexpr auto operator"" kmph(unsigned long long l) { return velocity(l); } +constexpr auto operator"" kmph(long double l) { return velocity(l); } + +// mph +constexpr auto operator"" mph(unsigned long long l) { return velocity(l); } +constexpr auto operator"" mph(long double l) { return velocity(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index b80ecc83..7b62210b 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -22,518 +22,578 @@ #pragma once -#include +#include #include #include #include namespace units { - // is_quantity - namespace detail { +// Quantity +namespace detail { - template - inline constexpr bool is_quantity = false; +template +inline constexpr bool is_quantity = false; - // partial specialization below after the first quantity forward declaration +// partial specialization below after the first quantity forward declaration - } // namespace detail +} // namespace detail - template - concept Quantity = detail::is_quantity; +template +concept Quantity = detail::is_quantity; - template - concept QuantityOf = Quantity && Dimension && same_dim; +// QuantityOf +template +concept QuantityOf = Quantity && Dimension && same_dim; - // Scalar - template - concept Scalar = - (!Quantity) && - std::regular && - std::totally_ordered && - detail::basic_arithmetic; +// Scalar +template +concept Scalar = (!Quantity) && std::regular && std::totally_ordered && detail::basic_arithmetic; - template - class quantity; +template U, Scalar Rep> +class quantity; - namespace detail { +namespace detail { - template - inline constexpr bool is_quantity> = true; +template +inline constexpr bool is_quantity> = true; - } // namespace detail +} // namespace detail - // common_quantity - namespace detail { +// common_quantity +namespace detail { - template - struct common_quantity_impl; +template +struct common_quantity_impl; - template - struct common_quantity_impl, quantity, Rep> { - using type = quantity; - }; +template +struct common_quantity_impl, quantity, Rep> { + using type = quantity; +}; - template - requires same_dim - struct common_quantity_impl, quantity, Rep> { - using type = - quantity>>, - Rep>; - }; +template +struct common_quantity_impl, quantity, Rep> { + using type = quantity< + D, downcast>>, + Rep>; +}; - } // namespace detail +} // namespace detail - template> - using common_quantity = detail::common_quantity_impl::type; +template> +using common_quantity = detail::common_quantity_impl::type; - // quantity_cast +// quantity_cast +namespace detail { - namespace detail { - - template - struct quantity_cast_impl { - template - static constexpr To cast(const Q& q) - { - if constexpr(treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * - (static_cast(CRatio::num) / static_cast(CRatio::den)))); - } - else { - return To( - static_cast(static_cast(q.count()) * static_cast(CRatio::num) / static_cast(CRatio::den))); - } - } - }; - - template - struct quantity_cast_impl { - template - static constexpr To cast(const Q& q) - { - return To(static_cast(q.count())); - } - }; - - template - struct quantity_cast_impl { - template - static constexpr To cast(const Q& q) - { - if constexpr(treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * (CRep{1} / static_cast(CRatio::den)))); - } - else { - return To(static_cast(static_cast(q.count()) / static_cast(CRatio::den))); - } - } - }; - - template - struct quantity_cast_impl { - template - static constexpr To cast(const Q& q) - { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num))); - } - }; - - } // namespace detail - - template - [[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires same_dim && - detail::basic_arithmetic> +template +struct quantity_cast_impl { + template + static constexpr To cast(const Q& q) { - using c_ratio = ratio_divide; - using c_rep = std::common_type_t; - using ret_dim = downcast; - using ret_unit = downcast>; - using ret = quantity; - using cast = detail::quantity_cast_impl; - return cast::cast(q); - } - - template - [[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires same_dim && - detail::basic_arithmetic> - { - return quantity_cast>(q); - } - - template - [[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires same_dim - { - return quantity_cast>(q); - } - - template - [[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires detail::basic_arithmetic> - { - return quantity_cast>(q); - } - - // quantity_values - - template - struct quantity_values { - static constexpr Rep zero() noexcept { return Rep(0); } - static constexpr Rep one() noexcept { return Rep(1); } - static constexpr Rep max() noexcept { return std::numeric_limits::max(); } - static constexpr Rep min() noexcept { return std::numeric_limits::lowest(); } - }; - - - // quantity - - template - class quantity { - Rep value_; - - public: - using unit = U; - using rep = Rep; - using dimension = U::dimension; - - quantity() = default; - quantity(const quantity&) = default; - quantity(quantity&&) = default; - - template - requires detail::safe_convertible - constexpr explicit quantity(const Value& v): value_{static_cast(v)} - { + if constexpr (treat_as_floating_point) { + return To(static_cast(static_cast(q.count()) * + (static_cast(CRatio::num) / static_cast(CRatio::den)))); + } else { + return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num) / + static_cast(CRatio::den))); } + } +}; - template - requires same_dim && - detail::safe_convertible && - detail::safe_divisible - constexpr quantity(const Q2& q): value_{quantity_cast(q).count()} - { +template +struct quantity_cast_impl { + template + static constexpr To cast(const Q& q) + { + return To(static_cast(q.count())); + } +}; + +template +struct quantity_cast_impl { + template + static constexpr To cast(const Q& q) + { + if constexpr (treat_as_floating_point) { + return To(static_cast(static_cast(q.count()) * (CRep{1} / static_cast(CRatio::den)))); + } else { + return To(static_cast(static_cast(q.count()) / static_cast(CRatio::den))); } + } +}; - quantity& operator=(const quantity&) = default; - quantity& operator=(quantity&&) = default; - - [[nodiscard]] constexpr rep count() const noexcept { return value_; } - - template - [[nodiscard]] static constexpr quantity zero() noexcept - requires requires { quantity_values::zero(); } - // requires requires { quantity_values::zero(); } // TODO gated by gcc-9 (fixed in gcc-10) - { - return quantity(quantity_values::zero()); - } - - template - [[nodiscard]] static constexpr quantity one() noexcept - requires requires { quantity_values::one(); } -// requires requires { quantity_values::one(); } // TODO gated by gcc-9 (fixed in gcc-10) - { - return quantity(quantity_values::one()); - } - - template - [[nodiscard]] static constexpr quantity min() noexcept - requires requires { quantity_values::min(); } -// requires requires { quantity_values::min(); } // TODO gated by gcc-9 (fixed in gcc-10) - { - return quantity(quantity_values::min()); - } - - template - [[nodiscard]] static constexpr quantity max() noexcept - requires requires { quantity_values::max(); } -// requires requires { quantity_values::max(); } // TODO gated by gcc-9 (fixed in gcc-10) - { - return quantity(quantity_values::max()); - } - - [[nodiscard]] constexpr quantity operator+() const { return *this; } - - template - [[nodiscard]] constexpr quantity operator-() const - requires std::regular_invocable -// requires std::regular_invocable // TODO gated by gcc-9 (fixed in gcc-10) - { - return quantity(-count()); - } - - template - constexpr quantity& operator++() - requires requires(T v) { { ++v } -> SAME_AS(T&); } -// requires requires(rep v) { { ++v } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - ++value_; - return *this; - } - - template - requires requires(T v) { { v++ } -> SAME_AS(T); } - constexpr quantity operator++(int) - // requires requires(rep v) { { v++ } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { return quantity(value_++); } - - template - requires requires(T v) { { --v } -> SAME_AS(T&); } - constexpr quantity& operator--() - // requires requires(rep v) { { --v } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - --value_; - return *this; - } - - template - requires requires(T v) { { v-- } -> SAME_AS(T); } - constexpr quantity operator--(int) - // requires requires(rep v) { { v-- } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { return quantity(value_--); } - - template - requires requires(T v1, T v2) { { v1 += v2 } -> SAME_AS(T&); } - constexpr quantity& operator+=(const quantity& q) - // requires requires(rep v1, rep v2) { { v1 += v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - value_ += q.count(); - return *this; - } - - template - requires requires(T v1, T v2) { { v1 -= v2 } -> SAME_AS(T&); } - constexpr quantity& operator-=(const quantity& q) - // requires requires(rep v1, rep v2) { { v1 -= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - value_ -= q.count(); - return *this; - } - - template - requires requires(T v1, T v2) { { v1 *= v2 } -> SAME_AS(T&); } - constexpr quantity& operator*=(const rep& rhs) - // requires requires(rep v1, rep v2) { { v1 *= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - value_ *= rhs; - return *this; - } - - template - requires requires(T v1, T v2) { { v1 /= v2 } -> SAME_AS(T&); } - constexpr quantity& operator/=(const rep& rhs) - // requires requires(rep v1, rep v2) { { v1 /= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - value_ /= rhs; - return *this; - } - - template - constexpr quantity& operator%=(const Value& rhs) - requires (!treat_as_floating_point) && - (!treat_as_floating_point) && - requires(T v1, Value v2) { { v1 %= v2 } -> SAME_AS(T&); } - // requires(rep v1, Value v2) { { v1 %= v2 } -> SAME_AS(rep&); } // TODO gated by gcc-9 (fixed in gcc-10) - { - value_ %= rhs; - return *this; - } - - template - constexpr quantity& operator%=(const quantity& q) - requires (!treat_as_floating_point) && - requires(T v1, T v2) { { v1 %= v2 } -> SAME_AS(T&); } - // requires(rep v1, rep v2) { { v1 %= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) - { - value_ %= q.count(); - return *this; - } - - template - friend std::basic_ostream& operator<<(std::basic_ostream& os, const quantity& q) - { - return os << q.count() << " " << detail::unit_text(); - } - }; - - template - [[nodiscard]] constexpr Quantity AUTO operator+(const quantity& lhs, const quantity& rhs) - requires same_dim && detail::basic_arithmetic +template +struct quantity_cast_impl { + template + static constexpr To cast(const Q& q) { - using common_rep = decltype(lhs.count() + rhs.count()); - using ret = common_quantity, quantity, common_rep>; - return ret(ret(lhs).count() + ret(rhs).count()); + return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num))); + } +}; + +} // namespace detail + +/** + * @brief Explcit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets the target quantity type to cast to. For example: + * + * using seconds = units::time; + * auto q1 = units::quantity_cast(1ms); + * + * @tparam To a target quantity type to cast to + */ +template +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires QuantityOf && + detail::basic_arithmetic> +{ + using c_ratio = ratio_divide; + using c_rep = std::common_type_t; + using ret_unit = downcast>; + using ret = quantity; + using cast = detail::quantity_cast_impl; + return cast::cast(q); +} + +/** + * @brief Explcit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets both the target unit and representation to cast to. For example: + * + * auto q1 = units::quantity_cast(1ms); + * + * @tparam ToU a unit type to use for a target quantity + * @tparam ToRep a representation type to use for a target quantity + */ +template +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires UnitOf && + detail::basic_arithmetic> +{ + return quantity_cast>(q); +} + +/** + * @brief Explcit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets only the target unit to cast to. For example: + * + * auto q1 = units::quantity_cast(1ms); + * + * @tparam ToU a unit type to use for a target quantity + */ +template +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires UnitOf +{ + return quantity_cast>(q); +} + +/** + * @brief Explcit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets only representation to cast to. For example: + * + * auto q1 = units::quantity_cast(1ms); + * + * @tparam ToRep a representation type to use for a target quantity + */ +template +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires detail::basic_arithmetic> +{ + return quantity_cast>(q); +} + +/** + * @brief A type trait that defines zero, one, min, and max for a representation type + * + * The zero, one, min, and max member functions in units::quantity forward their work to + * these methods. This type can be specialized if the representation Rep requires a specific + * implementation to return these quantity objects. + * + * @tparam Rep a representation type for which a type trait is defined + */ +template +struct quantity_values { + static constexpr Rep zero() noexcept { return Rep(0); } + static constexpr Rep one() noexcept { return Rep(1); } + static constexpr Rep min() noexcept { return std::numeric_limits::lowest(); } + static constexpr Rep max() noexcept { return std::numeric_limits::max(); } +}; + +/** + * @brief A quantity + * + * Property of a phenomenon, body, or substance, where the property has a magnitude that can be + * expressed by means of a number and a measurement unit. + * + * @tparam D a dimension of the quantity (can be either a BaseDimension or a DerivedDimension) + * @tparam U a measurement unit of the quantity + * @tparam Rep a type to be used to represent values of a quantity + */ +template U, Scalar Rep = double> +class quantity { + Rep value_; + +public: + using dimension = D; + using unit = U; + using rep = Rep; + + quantity() = default; + quantity(const quantity&) = default; + quantity(quantity&&) = default; + + template + requires detail::safe_convertible + constexpr explicit quantity(const Value& v) : value_{static_cast(v)} {} + + template + requires same_dim && + detail::safe_convertible && + detail::safe_divisible + constexpr quantity(const Q2& q) : value_{quantity_cast(q).count()} {} + + quantity& operator=(const quantity&) = default; + quantity& operator=(quantity&&) = default; + + [[nodiscard]] constexpr rep count() const noexcept { return value_; } + + template + [[nodiscard]] static constexpr quantity zero() noexcept + requires requires { quantity_values::zero(); } + // requires requires { quantity_values::zero(); } // TODO gated by gcc-9 (fixed in gcc-10) + { + return quantity(quantity_values::zero()); } - template - [[nodiscard]] constexpr Quantity AUTO operator-(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic + template + [[nodiscard]] static constexpr quantity one() noexcept + requires requires { quantity_values::one(); } + // requires requires { quantity_values::one(); } // TODO gated by gcc-9 (fixed in gcc-10) { - using common_rep = decltype(lhs.count() - rhs.count()); - using ret = common_quantity, quantity, common_rep>; - return ret(ret(lhs).count() - ret(rhs).count()); + return quantity(quantity_values::one()); } - template - [[nodiscard]] constexpr Quantity AUTO operator*(const quantity& q, const Value& v) - requires std::magma + template + [[nodiscard]] static constexpr quantity min() noexcept + requires requires { quantity_values::min(); } + // requires requires { quantity_values::min(); } // TODO gated by gcc-9 (fixed in gcc-10) { - using common_rep = decltype(q.count() * v); - using ret = quantity; - return ret(q.count() * v); + return quantity(quantity_values::min()); } - template - [[nodiscard]] constexpr Quantity AUTO operator*(const Value& v, const quantity& q) - requires std::magma + template + [[nodiscard]] static constexpr quantity max() noexcept + requires requires { quantity_values::max(); } + // requires requires { quantity_values::max(); } // TODO gated by gcc-9 (fixed in gcc-10) { - return q * v; + return quantity(quantity_values::max()); } - template - [[nodiscard]] constexpr Scalar AUTO operator*(const quantity& lhs, const quantity& rhs) - requires same_dim> && - detail::basic_arithmetic + [[nodiscard]] constexpr quantity operator+() const { return *this; } + + template + [[nodiscard]] constexpr quantity operator-() const + requires std::regular_invocable + // requires std::regular_invocable // TODO gated by gcc-9 (fixed in gcc-10) { - using common_rep = decltype(lhs.count() * rhs.count()); - using ratio = ratio_multiply; - return common_rep(lhs.count()) * common_rep(rhs.count()) * common_rep(ratio::num) / common_rep(ratio::den); + return quantity(-count()); } - template - [[nodiscard]] constexpr Quantity AUTO operator*(const quantity& lhs, const quantity& rhs) - requires (!same_dim>) && - (treat_as_floating_point || - (std::ratio_multiply::den == 1)) && - detail::basic_arithmetic + template + constexpr quantity& operator++() + requires requires(T v) { { ++v } -> SAME_AS(T&); } + // requires requires(rep v) { { ++v } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - using dim = dimension_multiply; - using common_rep = decltype(lhs.count() * rhs.count()); - using ret = quantity>>, common_rep>; - return ret(lhs.count() * rhs.count()); + ++value_; + return *this; } - template - [[nodiscard]] constexpr Quantity AUTO operator/(const Value& v, const quantity& q) - requires std::magma + template + requires requires(T v) { { v++ } -> SAME_AS(T); } + constexpr quantity operator++(int) + // requires requires(rep v) { { v++ } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - Expects(q != std::remove_cvref_t(0)); - - using dim = dim_invert; - using common_rep = decltype(v / q.count()); - using ret = quantity>>, common_rep>; - return ret(v / q.count()); + return quantity(value_++); } - template - [[nodiscard]] constexpr Quantity AUTO operator/(const quantity& q, const Value& v) - requires std::magma + template + requires requires(T v) { { --v } -> SAME_AS(T&); } + constexpr quantity& operator--() + // requires requires(rep v) { { --v } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - Expects(v != Value{0}); - - using common_rep = decltype(q.count() / v); - using ret = quantity; - return ret(q.count() / v); + --value_; + return *this; } - template - [[nodiscard]] constexpr Scalar AUTO operator/(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic + template + requires requires(T v) { { v-- } -> SAME_AS(T); } + constexpr quantity operator--(int) + // requires requires(rep v) { { v-- } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - Expects(rhs != std::remove_cvref_t(0)); - - using common_rep = decltype(lhs.count() / rhs.count()); - using cq = common_quantity, quantity, common_rep>; - return cq(lhs).count() / cq(rhs).count(); + return quantity(value_--); } - template - [[nodiscard]] constexpr Quantity AUTO operator/(const quantity& lhs, const quantity& rhs) - requires (!same_dim) && - (treat_as_floating_point || - (ratio_divide::den == 1)) && - detail::basic_arithmetic + template + requires requires(T v1, T v2) { { v1 += v2 } -> SAME_AS(T&); } + constexpr quantity& operator+=(const quantity& q) + // requires requires(rep v1, rep v2) { { v1 += v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - Expects(rhs != std::remove_cvref_t(0)); - - using common_rep = decltype(lhs.count() / rhs.count()); - using dim = dimension_divide; - using ret = quantity>>, common_rep>; - return ret(lhs.count() / rhs.count()); + value_ += q.count(); + return *this; } - template - [[nodiscard]] constexpr Quantity AUTO operator%(const quantity& q, const Value& v) - requires (!treat_as_floating_point) && (!treat_as_floating_point) && - std::magma + template + requires requires(T v1, T v2) { { v1 -= v2 } -> SAME_AS(T&); } + constexpr quantity& operator-=(const quantity& q) + // requires requires(rep v1, rep v2) { { v1 -= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - using common_rep = decltype(q.count() % v); - using ret = quantity; - return ret(q.count() % v); + value_ -= q.count(); + return *this; } - template - [[nodiscard]] constexpr Quantity AUTO operator%(const quantity& lhs, const quantity& rhs) - requires (!treat_as_floating_point) && (!treat_as_floating_point) && - std::magma + template + requires requires(T v1, T v2) { { v1 *= v2 } -> SAME_AS(T&); } + constexpr quantity& operator*=(const rep& rhs) + // requires requires(rep v1, rep v2) { { v1 *= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - using common_rep = decltype(lhs.count() % rhs.count()); - using ret = common_quantity, quantity, common_rep>; - return ret(ret(lhs).count() % ret(rhs).count()); + value_ *= rhs; + return *this; } - template - [[nodiscard]] constexpr bool operator==(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic && std::equality_comparable_with + template + requires requires(T v1, T v2) { { v1 /= v2 } -> SAME_AS(T&); } + constexpr quantity& operator/=(const rep& rhs) + // requires requires(rep v1, rep v2) { { v1 /= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - using cq = common_quantity, quantity>; - return cq(lhs).count() == cq(rhs).count(); + value_ /= rhs; + return *this; } - template - [[nodiscard]] constexpr bool operator!=(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic && std::equality_comparable_with + template + constexpr quantity& operator%=(const Value& rhs) + requires (!treat_as_floating_point) && + (!treat_as_floating_point) && + requires(T v1, Value v2) { { v1 %= v2 } -> SAME_AS(T&); } + // requires(rep v1, Value v2) { { v1 %= v2 } -> SAME_AS(rep&); } // TODO gated by gcc-9 (fixed in gcc-10) { - return !(lhs == rhs); + value_ %= rhs; + return *this; } - template - [[nodiscard]] constexpr bool operator<(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic && std::totally_ordered_with + template + constexpr quantity& operator%=(const quantity& q) + requires (!treat_as_floating_point) && + requires(T v1, T v2) { { v1 %= v2 } -> SAME_AS(T&); } + // requires(rep v1, rep v2) { { v1 %= v2 } -> std::same_as; } // TODO gated by gcc-9 (fixed in gcc-10) { - using cq = common_quantity, quantity>; - return cq(lhs).count() < cq(rhs).count(); + value_ %= q.count(); + return *this; } - template - [[nodiscard]] constexpr bool operator<=(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic && std::totally_ordered_with + template + friend std::basic_ostream& operator<<(std::basic_ostream& os, const quantity& q) { - return !(rhs < lhs); + return os; // << q.count() << " " << detail::unit_text(); } +}; - template - [[nodiscard]] constexpr bool operator>(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic && std::totally_ordered_with - { - return rhs < lhs; - } +template +[[nodiscard]] constexpr Quantity AUTO operator+(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic +{ + using common_rep = decltype(lhs.count() + rhs.count()); + using ret = common_quantity, quantity, common_rep>; + return ret(ret(lhs).count() + ret(rhs).count()); +} - template - [[nodiscard]] constexpr bool operator>=(const quantity& lhs, const quantity& rhs) - requires same_dim && - detail::basic_arithmetic && std::totally_ordered_with - { - return !(lhs < rhs); - } +template +[[nodiscard]] constexpr Quantity AUTO operator-(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic +{ + using common_rep = decltype(lhs.count() - rhs.count()); + using ret = common_quantity, quantity, common_rep>; + return ret(ret(lhs).count() - ret(rhs).count()); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator*(const quantity& q, const Value& v) + requires std::magma +{ + using common_rep = decltype(q.count() * v); + using ret = quantity; + return ret(q.count() * v); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator*(const Value& v, const quantity& q) + requires std::magma +{ + return q * v; +} + +template +[[nodiscard]] constexpr Scalar AUTO operator*(const quantity& lhs, const quantity& rhs) + requires same_dim>&& detail::basic_arithmetic +{ + using common_rep = decltype(lhs.count() * rhs.count()); + using ratio = ratio_multiply; + return common_rep(lhs.count()) * common_rep(rhs.count()) * common_rep(ratio::num) / common_rep(ratio::den); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator*(const quantity& lhs, const quantity& rhs) + requires (!same_dim>) && + (treat_as_floating_point || + (std::ratio_multiply::den == 1)) && + detail::basic_arithmetic +{ + using dim = dimension_multiply; + using ratio = ratio_multiply; + using unit = downcast>; + using common_rep = decltype(lhs.count() * rhs.count()); + using ret = quantity; + return ret(lhs.count() * rhs.count()); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator/(const Value& v, const quantity& q) + requires std::magma +{ + Expects(q != std::remove_cvref_t(0)); + + using dim = dim_invert; + using ratio = ratio; + using unit = downcast>; + using common_rep = decltype(v / q.count()); + using ret = quantity; + return ret(v / q.count()); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator/(const quantity& q, const Value& v) + requires std::magma +{ + Expects(v != Value{0}); + + using common_rep = decltype(q.count() / v); + using ret = quantity; + return ret(q.count() / v); +} + +template +[[nodiscard]] constexpr Scalar AUTO operator/(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic +{ + Expects(rhs != std::remove_cvref_t(0)); + + using common_rep = decltype(lhs.count() / rhs.count()); + using cq = common_quantity, quantity, common_rep>; + return cq(lhs).count() / cq(rhs).count(); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator/(const quantity& lhs, const quantity& rhs) + requires (treat_as_floating_point || + (ratio_divide::den == 1)) && + detail::basic_arithmetic +{ + Expects(rhs != std::remove_cvref_t(0)); + + using common_rep = decltype(lhs.count() / rhs.count()); + using dim = dimension_divide; + using unit = downcast>>; + using ret = quantity; + return ret(lhs.count() / rhs.count()); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator%(const quantity& q, const Value& v) + requires (!treat_as_floating_point) && + (!treat_as_floating_point) && + std::magma +{ + using common_rep = decltype(q.count() % v); + using ret = quantity; + return ret(q.count() % v); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator%(const quantity& lhs, const quantity& rhs) + requires (!treat_as_floating_point) && + (!treat_as_floating_point) && + std::magma +{ + using common_rep = decltype(lhs.count() % rhs.count()); + using ret = common_quantity, quantity, common_rep>; + return ret(ret(lhs).count() % ret(rhs).count()); +} + +template +[[nodiscard]] constexpr bool operator==(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic && + std::equality_comparable_with +{ + using cq = common_quantity, quantity>; + return cq(lhs).count() == cq(rhs).count(); +} + +template +[[nodiscard]] constexpr bool operator!=(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic && + std::equality_comparable_with +{ + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic && + std::totally_ordered_with +{ + using cq = common_quantity, quantity>; + return cq(lhs).count() < cq(rhs).count(); +} + +template +[[nodiscard]] constexpr bool operator<=(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic && + std::totally_ordered_with +{ + return !(rhs < lhs); +} + +template +[[nodiscard]] constexpr bool operator>(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic && + std::totally_ordered_with +{ + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator>=(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic && + std::totally_ordered_with +{ + return !(lhs < rhs); +} } // namespace units diff --git a/src/include/units/unit.h b/src/include/units/unit.h index 8c50c259..09ab02f8 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -42,6 +42,13 @@ struct reference_unit : downcast_base> { } // namespace detail +// UnitOf +template +concept UnitOf = + Unit && + Dimension && + std::same_as; + namespace detail { // same_reference_units @@ -49,8 +56,7 @@ template inline constexpr bool same_reference_units = false; template -inline constexpr bool same_reference_units, Us...> = - (std::same_as && ...); +inline constexpr bool same_reference_units, Us...> = (UnitOf && ...); // deduced_unit template diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index b0017fbc..0eb0c108 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -27,7 +27,7 @@ add_library(unit_tests_static # fixed_string_test.cpp # math_test.cpp # new_design.cpp -# quantity_test.cpp + quantity_test.cpp ratio_test.cpp # si_test.cpp type_list_test.cpp diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index a0d55d13..ff029a5f 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -20,299 +20,319 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/dimensions/velocity.h" -#include "units/dimensions/frequency.h" -#include "units/dimensions/area.h" #include "units/math.h" -#include +#include "units/physical/si/area.h" +#include "units/physical/si/frequency.h" +#include "units/physical/si/velocity.h" #include +#include using namespace units; namespace { - template - class my_value { - T value_{}; - public: - my_value() = default; - constexpr my_value(T v) : value_(std::move(v)) {} - [[nodiscard]] constexpr my_value operator-() const { return my_value(-value_); } +template +class my_value { + T value_{}; - [[nodiscard]] friend constexpr my_value operator+(my_value lhs, my_value rhs) { return my_value(lhs.value_ + rhs.value_); } - [[nodiscard]] friend constexpr my_value operator-(my_value lhs, my_value rhs) { return my_value(lhs.value_ - rhs.value_); } - [[nodiscard]] friend constexpr my_value operator*(my_value lhs, my_value rhs) { return my_value(lhs.value_ * rhs.value_); } - [[nodiscard]] friend constexpr my_value operator/(my_value lhs, my_value rhs) { return my_value(lhs.value_ / rhs.value_); } +public: + my_value() = default; + constexpr my_value(T v) : value_(std::move(v)) {} - [[nodiscard]] friend constexpr bool operator==(my_value lhs, my_value rhs) { return lhs.value_ == rhs.value_; } - [[nodiscard]] friend constexpr bool operator!=(my_value lhs, my_value rhs) { return !(lhs == rhs); } - [[nodiscard]] friend constexpr bool operator<(my_value lhs, my_value rhs) { return lhs.value_ < rhs.value_; } - [[nodiscard]] friend constexpr bool operator>(my_value lhs, my_value rhs) { return rhs < lhs; } - [[nodiscard]] friend constexpr bool operator<=(my_value lhs, my_value rhs) { return !(rhs < lhs); } - [[nodiscard]] friend constexpr bool operator>=(my_value lhs, my_value rhs) { return !(lhs < rhs); } + // constexpr my_value& operator+=(my_value other) { value_ += other.value_; return *this; } + // constexpr my_value& operator-=(my_value other) { value_ -= other.value_; return *this; } + // constexpr my_value& operator*=(my_value other) { value_ *= other.value_; return *this; } + // constexpr my_value& operator/=(my_value other) { value_ /= other.value_; return *this; } - constexpr operator const T&() const & { return value_; } - }; + [[nodiscard]] constexpr my_value operator-() const { return my_value(-value_); } + + [[nodiscard]] friend constexpr my_value operator+(my_value lhs, my_value rhs) { + return my_value(lhs.value_ + rhs.value_); + } + [[nodiscard]] friend constexpr my_value operator-(my_value lhs, my_value rhs) { + return my_value(lhs.value_ - rhs.value_); + } + [[nodiscard]] friend constexpr my_value operator*(my_value lhs, my_value rhs) { + return my_value(lhs.value_ * rhs.value_); + } + [[nodiscard]] friend constexpr my_value operator/(my_value lhs, my_value rhs) { + return my_value(lhs.value_ / rhs.value_); + } + + [[nodiscard]] friend constexpr bool operator==(my_value lhs, my_value rhs) { return lhs.value_ == rhs.value_; } + [[nodiscard]] friend constexpr bool operator!=(my_value lhs, my_value rhs) { return !(lhs == rhs); } + [[nodiscard]] friend constexpr bool operator<(my_value lhs, my_value rhs) { return lhs.value_ < rhs.value_; } + [[nodiscard]] friend constexpr bool operator>(my_value lhs, my_value rhs) { return rhs < lhs; } + [[nodiscard]] friend constexpr bool operator<=(my_value lhs, my_value rhs) { return !(rhs < lhs); } + [[nodiscard]] friend constexpr bool operator>=(my_value lhs, my_value rhs) { return !(lhs < rhs); } + + constexpr operator const T&() const& { return value_; } +}; } // namespace namespace units { - template - inline constexpr bool treat_as_floating_point> = std::is_floating_point_v; +template +inline constexpr bool treat_as_floating_point> = std::is_floating_point_v; - template - struct quantity_values> { - static constexpr my_value zero() { return my_value(0); } - static constexpr my_value max() { return std::numeric_limits::max(); } - static constexpr my_value min() { return std::numeric_limits::lowest(); } - }; +template +struct quantity_values> { + static constexpr my_value zero() { return my_value(0); } + static constexpr my_value max() { return std::numeric_limits::max(); } + static constexpr my_value min() { return std::numeric_limits::lowest(); } +}; } // namespace units namespace std { - template - struct common_type, my_value> : std::type_identity>> { - }; +template +struct common_type, my_value> : std::type_identity>> {}; - template - struct common_type, U> : common_type { - }; +template +struct common_type, U> : common_type {}; - template - struct common_type> : common_type { - }; +template +struct common_type> : common_type {}; } // namespace std namespace { - static_assert(units::Scalar>); - static_assert(std::convertible_to, float>); - static_assert(std::convertible_to>); +static_assert(units::Scalar>); +static_assert(std::convertible_to, float>); +static_assert(std::convertible_to>); - using namespace units; +using namespace units; +using namespace units::si; - // class invariants +// class invariants - // constexpr quantity q; // should a static_assert - // constexpr quantity> error(0m); // should trigger a static_assert - // constexpr quantity error(0); // should trigger a static_assert - // constexpr quantity>, int> error(0); // should trigger a static_assert +// constexpr quantity q(1); // should not compile +// constexpr quantity> error(0m); // should trigger a static_assert +// constexpr quantity error(0); // should trigger a static_assert +// constexpr quantity>, int> error(0); // should trigger a static_assert - // member types +// member types - static_assert(std::is_same_v::rep, int>); - static_assert(std::is_same_v::rep, double>); - static_assert(std::is_same_v::unit, metre>); - static_assert(std::is_same_v::unit, kilometre>); +static_assert(std::is_same_v::rep, int>); +static_assert(std::is_same_v::rep, double>); +static_assert(std::is_same_v::unit, metre>); +static_assert(std::is_same_v::unit, kilometre>); - // constructors +// constructors - using my_int = my_value; - using my_double = my_value; +using my_int = my_value; +using my_double = my_value; - static_assert(quantity().count() == 0); - constexpr quantity km{1000}; - static_assert(km.count() == 1000); - static_assert(quantity(km).count() == km.count()); +static_assert(length().count() == 0); +constexpr length km{1000}; +static_assert(km.count() == 1000); +static_assert(length(km).count() == km.count()); - static_assert(quantity(1).count() == 1); - static_assert(quantity(my_value(1)).count() == 1); - static_assert(quantity(1).count() == my_int{1}); - // static_assert(quantity(1.0).count() == 1); // should not compile - // static_assert(quantity(my_value(1.0)).count() == 1); // should not compile - // static_assert(quantity(1.0).count() == 1); // should not compile - static_assert(quantity(1.0).count() == 1.0); - static_assert(quantity(my_value(1.0)).count() == 1.0); - static_assert(quantity(1).count() == 1.0); - static_assert(quantity(my_value(1)).count() == 1.0); - static_assert(quantity(3.14).count() == 3.14); - static_assert(quantity(1.0).count() == my_double{1.0}); - static_assert(quantity(1).count() == my_double{1.0}); - static_assert(quantity(3.14).count() == my_double{3.14}); +static_assert(length(1).count() == 1); +static_assert(length(my_value(1)).count() == 1); +static_assert(length(1).count() == my_int{1}); +// static_assert(length(1.0).count() == 1); // should not compile +// static_assert(length(my_value(1.0)).count() == 1); // should not compile +// static_assert(length(1.0).count() == 1); // should not compile +static_assert(length(1.0).count() == 1.0); +static_assert(length(my_value(1.0)).count() == 1.0); +static_assert(length(1).count() == 1.0); +static_assert(length(my_value(1)).count() == 1.0); +static_assert(length(3.14).count() == 3.14); +static_assert(length(1.0).count() == my_double{1.0}); +static_assert(length(1).count() == my_double{1.0}); +static_assert(length(3.14).count() == my_double{3.14}); - static_assert(quantity(km).count() == 1000); - // static_assert(quantity(quantity(3.14)).count() == 3); // should not compile - static_assert(quantity(quantity_cast>(3.14m)).count() == 3); - // static_assert(quantity(quantity(1000.0)).count() == 1000); // should not compile - // static_assert(quantity(1000.0m).count() == 1000); // should not compile - static_assert(quantity(1000.0m).count() == 1000.0); - static_assert(quantity(quantity(1000.0)).count() == 1000.0); - static_assert(quantity(1000.0m).count() == my_double{1000.0}); - static_assert(quantity(km).count() == 1000.0); - static_assert(quantity(km).count() == my_double{1000.0}); - static_assert(quantity(1km).count() == 1000); - // static_assert(quantity(1_s).count() == 1); // should not compile - // static_assert(quantity(1010m).count() == 1); // should not compile - static_assert(quantity(quantity_cast>(1010m)).count() == 1); +static_assert(length(km).count() == 1000); +// static_assert(length(length(3.14)).count() == 3); // should not compile +static_assert(length(quantity_cast>(3.14m)).count() == 3); +// static_assert(length(length(1000.0)).count() == 1000); // should not compile +// static_assert(length(1000.0m).count() == 1000); // should not compile +static_assert(length(1000.0m).count() == 1000.0); +static_assert(length(length(1000.0)).count() == 1000.0); +static_assert(length(1000.0m).count() == my_double{1000.0}); +static_assert(length(km).count() == 1000.0); +static_assert(length(km).count() == my_double{1000.0}); +static_assert(length(1km).count() == 1000); +// static_assert(length(1_s).count() == 1); // should not compile +// static_assert(length(1010m).count() == 1); // should not compile +static_assert(length(quantity_cast>(1010m)).count() == 1); - // assignment operator +// assignment operator - static_assert([]() { - quantity l1(1), l2(2); - return l2 = l1; - }().count() == 1); +static_assert([]() { + length l1(1), l2(2); + return l2 = l1; +}() + .count() == 1); - // static member functions +// static member functions - static_assert(quantity::zero().count() == 0); - static_assert(quantity::min().count() == std::numeric_limits::lowest()); - static_assert(quantity::max().count() == std::numeric_limits::max()); - static_assert(quantity::zero().count() == 0.0); - static_assert(quantity::min().count() == std::numeric_limits::lowest()); - static_assert(quantity::max().count() == std::numeric_limits::max()); - static_assert(quantity::zero().count() == my_int{0}); - static_assert(quantity::min().count() == my_int{std::numeric_limits::lowest()}); - static_assert(quantity::max().count() == my_int{std::numeric_limits::max()}); - static_assert(quantity::zero().count() == my_double{0.0}); - static_assert(quantity::min().count() == my_double{std::numeric_limits::lowest()}); - static_assert(quantity::max().count() == my_double{std::numeric_limits::max()}); +static_assert(length::zero().count() == 0); +static_assert(length::min().count() == std::numeric_limits::lowest()); +static_assert(length::max().count() == std::numeric_limits::max()); +static_assert(length::zero().count() == 0.0); +static_assert(length::min().count() == std::numeric_limits::lowest()); +static_assert(length::max().count() == std::numeric_limits::max()); +static_assert(length::zero().count() == my_int{0}); +static_assert(length::min().count() == my_int{std::numeric_limits::lowest()}); +static_assert(length::max().count() == my_int{std::numeric_limits::max()}); +static_assert(length::zero().count() == my_double{0.0}); +static_assert(length::min().count() == my_double{std::numeric_limits::lowest()}); +static_assert(length::max().count() == my_double{std::numeric_limits::max()}); - // unary member operators +// unary member operators - static_assert((+km).count() == 1000); - static_assert((-km).count() == -1000); - static_assert((+(-km)).count() == -1000); - static_assert((-(-km)).count() == 1000); +static_assert((+km).count() == 1000); +static_assert((-km).count() == -1000); +static_assert((+(-km)).count() == -1000); +static_assert((-(-km)).count() == 1000); - // binary member operators +// binary member operators - static_assert([](auto v) { - auto vv = v++; - return std::make_pair(v, vv); - }(km) == std::make_pair(quantity(1001), quantity(1000))); - static_assert([](auto v) { - auto vv = ++v; - return std::make_pair(v, vv); - }(km) == std::make_pair(quantity(1001), quantity(1001))); - static_assert([](auto v) { - auto vv = v--; - return std::make_pair(v, vv); - }(km) == std::make_pair(quantity(999), quantity(1000))); - static_assert([](auto v) { - auto vv = --v; - return std::make_pair(v, vv); - }(km) == std::make_pair(quantity(999), quantity(999))); +static_assert([](auto v) { + auto vv = v++; + return std::make_pair(v, vv); +}(km) == std::make_pair(length(1001), length(1000))); +static_assert([](auto v) { + auto vv = ++v; + return std::make_pair(v, vv); +}(km) == std::make_pair(length(1001), length(1001))); +static_assert([](auto v) { + auto vv = v--; + return std::make_pair(v, vv); +}(km) == std::make_pair(length(999), length(1000))); +static_assert([](auto v) { + auto vv = --v; + return std::make_pair(v, vv); +}(km) == std::make_pair(length(999), length(999))); - // compound assignment +// compound assignment - static_assert((1m += 1m).count() == 2); - static_assert((2m -= 1m).count() == 1); - static_assert((1m *= 2).count() == 2); - static_assert((2m /= 2).count() == 1); - static_assert((7m %= 2).count() == 1); - static_assert((7m %= 2m).count() == 1); +static_assert((1m += 1m).count() == 2); +static_assert((2m -= 1m).count() == 1); +static_assert((1m *= 2).count() == 2); +static_assert((2m /= 2).count() == 1); +static_assert((7m %= 2).count() == 1); +static_assert((7m %= 2m).count() == 1); // static_assert((7.m %= 2.).count() == 1); // should not compile // static_assert((7.m %= 2).count() == 1); // should not compile // static_assert((7m %= 2.).count() == 1); // should not compile - static_assert((7m %= 2m).count() == 1); +static_assert((7m %= 2m).count() == 1); // static_assert((7.m %= 2.m).count() == 1); // should not compile // static_assert((7.m %= 2m).count() == 1); // should not compile // static_assert((7m %= 2.m).count() == 1); // should not compile - // non-member arithmetic operators +// non-member arithmetic operators - static_assert(std::is_same_v() + quantity()), quantity>); - static_assert(std::is_same_v() + quantity()), quantity>); - static_assert(std::is_same_v() + quantity()), quantity>); - static_assert(std::is_same_v() - quantity()), quantity>); - static_assert(std::is_same_v() - quantity()), quantity>); - static_assert(std::is_same_v() * 1.0), quantity>); - static_assert(std::is_same_v()), quantity>); - static_assert(std::is_same_v() * quantity()), quantity>); - static_assert(std::is_same_v()), quantity>); - static_assert(std::is_same_v() / 1.0), quantity>); - static_assert(std::is_same_v() / quantity()), double>); - static_assert(std::is_same_v() / quantity()), double>); - static_assert(std::is_same_v() / quantity()), quantity>); - static_assert(std::is_same_v() % short(1)), quantity>); - static_assert(std::is_same_v() % quantity(1)), quantity>); +static_assert(std::is_same_v() + length()), length>); +static_assert(std::is_same_v() + length()), length>); +static_assert( + std::is_same_v() + length()), length>); +static_assert(std::is_same_v() - length()), length>); +static_assert( + std::is_same_v() - length()), length>); +static_assert(std::is_same_v() * 1.0), length>); +static_assert(std::is_same_v()), length>); +static_assert( + std::is_same_v() * si::time()), length>); +static_assert(std::is_same_v()), frequency>); +static_assert(std::is_same_v()), si::time>); +static_assert(std::is_same_v() / 1.0), length>); +static_assert(std::is_same_v() / length()), double>); +static_assert(std::is_same_v() / length()), double>); +static_assert( + std::is_same_v() / si::time()), velocity>); +static_assert(std::is_same_v() % short(1)), length>); +static_assert(std::is_same_v() % length(1)), length>); - static_assert((1m + km).count() == 1001); - static_assert((1m + 1km).count() == 1001); - static_assert((km - 1m).count() == 999); - static_assert((1km - 1m).count() == 999); - static_assert((2m * 2).count() == 4); - static_assert((3 * 3m).count() == 9); - static_assert((4m / 2).count() == 2); - static_assert(4m / 2m == 2); - static_assert(4km / 2000m == 2); - static_assert((7m % 2).count() == 1); - static_assert((7m % 2m).count() == 1); - static_assert((7km % 2000m).count() == 1000); +static_assert((1m + km).count() == 1001); +static_assert((1m + 1km).count() == 1001); +static_assert((km - 1m).count() == 999); +static_assert((1km - 1m).count() == 999); +static_assert((2m * 2).count() == 4); +static_assert((3 * 3m).count() == 9); +static_assert((4m / 2).count() == 2); +static_assert(4m / 2m == 2); +static_assert(4km / 2000m == 2); +static_assert((7m % 2).count() == 1); +static_assert((7m % 2m).count() == 1); +static_assert((7km % 2000m).count() == 1000); - // comparators +// comparators - static_assert(2m + 1m == 3m); - static_assert(!(2m + 2m == 3m)); - static_assert(2m + 2m != 3m); - static_assert(!(2m + 2m != 4m)); - static_assert(2m > 1m); - static_assert(!(1m > 1m)); - static_assert(1m < 2m); - static_assert(!(2m < 2m)); - static_assert(2m >= 1m); - static_assert(2m >= 2m); - static_assert(!(2m >= 3m)); - static_assert(1m <= 2m); - static_assert(2m <= 2m); - static_assert(!(3m <= 2m)); +static_assert(2m + 1m == 3m); +static_assert(!(2m + 2m == 3m)); +static_assert(2m + 2m != 3m); +static_assert(!(2m + 2m != 4m)); +static_assert(2m > 1m); +static_assert(!(1m > 1m)); +static_assert(1m < 2m); +static_assert(!(2m < 2m)); +static_assert(2m >= 1m); +static_assert(2m >= 2m); +static_assert(!(2m >= 3m)); +static_assert(1m <= 2m); +static_assert(2m <= 2m); +static_assert(!(3m <= 2m)); - static_assert(3m == 3.0m); - static_assert(3m != 3.14m); - static_assert(2m > 1.0m); - static_assert(1.0m < 2m); - static_assert(2.0m >= 1m); - static_assert(1m <= 2.0m); +static_assert(3m == 3.0m); +static_assert(3m != 3.14m); +static_assert(2m > 1.0m); +static_assert(1.0m < 2m); +static_assert(2.0m >= 1m); +static_assert(1m <= 2.0m); - static_assert(1000m == 1km); - static_assert(1001m != 1km); - static_assert(1001m > 1km); - static_assert(999m < 1km); - static_assert(1000m >= 1km); - static_assert(1000m <= 1km); +static_assert(1000m == 1km); +static_assert(1001m != 1km); +static_assert(1001m > 1km); +static_assert(999m < 1km); +static_assert(1000m >= 1km); +static_assert(1000m <= 1km); - // is_quantity +// is_quantity - static_assert(Quantity>); +static_assert(Quantity>); - // common_quantity +// common_quantity - static_assert(std::is_same_v, quantity>, quantity>); - static_assert(std::is_same_v, quantity>, quantity>); - static_assert(std::is_same_v, quantity>, quantity>); +static_assert(std::is_same_v, length>, length>); +static_assert( + std::is_same_v, length>, length>); +static_assert(std::is_same_v, length>, + length>); - // quantity_cast +// quantity_cast - static_assert(std::is_same_v>>(2km))::unit, metre>); - static_assert(std::is_same_v>, ratio<1>>>(2km))::unit, metre>); +static_assert(std::is_same_v>>(2km))::unit, metre>); - // static_assert(quantity_cast(2km).count() == 2000); // should not compile - static_assert(quantity_cast>(2km).count() == 2000); - static_assert(quantity_cast>(2000m).count() == 2); +// static_assert(quantity_cast(2km).count() == 2000); // should not compile +static_assert(quantity_cast>(2km).count() == 2000); +static_assert(quantity_cast>(2000m).count() == 2); - // time +// time - // static_assert(1s == 1m); // should not compile - static_assert(1h == 3600s); +// static_assert(1s == 1m); // should not compile +static_assert(1h == 3600s); - // length +// length - static_assert(1km == 1000m); - static_assert(1km + 1m == 1001m); - static_assert(10km / 5km == 2); - static_assert(10km / 2 == 5km); +static_assert(1km == 1000m); +static_assert(1km + 1m == 1001m); +static_assert(10km / 5km == 2); +static_assert(10km / 2 == 5km); - // velocity +// velocity - static_assert(10m / 5s == 2mps); - static_assert(10 / 5s * 1m == 2mps); - static_assert(1km / 1s == 1000mps); - static_assert(2kmph * 2h == 4km); - static_assert(2km / 2kmph == 1h); +static_assert(10m / 5s == 2mps); +static_assert(10 / 5s * 1m == 2mps); +static_assert(1km / 1s == 1000mps); +static_assert(2kmph * 2h == 4km); +static_assert(2km / 2kmph == 1h); - static_assert(std::is_same_v(2m)), decltype(4sq_m)>); +static_assert(std::is_same_v(2m)), decltype(4sq_m)>); } // namespace From f31b26b5e5e2741cd5b09efbae61afbc7bbd35a8 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 6 Dec 2019 12:18:39 +0100 Subject: [PATCH 05/44] Design cleanup - unknown_unit added - examples refactored - base_type renamed to downcast_base_type - scaled_unit renamed to named_scaled_unit - detail::reference_unit renamed to scaled_unit - quantity_test cleanup --- CMakeLists.txt | 2 +- example/CMakeLists.txt | 18 +-- example/avg_velocity.cpp | 147 ++++++++++++++++++ example/measurement.cpp | 43 +++-- .../{example.cpp => unknown_dimension.cpp} | 40 ++--- src/include/units/bits/downcasting.h | 8 +- src/include/units/bits/unit_concept.h | 11 +- src/include/units/derived_dimension.h | 36 ++++- src/include/units/math.h | 4 +- src/include/units/physical/dimensions.h | 2 +- .../si}/acceleration.h | 26 ++-- src/include/units/physical/si/length.h | 8 +- src/include/units/physical/si/time.h | 4 +- src/include/units/quantity.h | 14 +- src/include/units/unit.h | 40 ++--- .../make_dimension/dimension_concepts_all.h | 6 +- .../make_dimension/dimension_concepts_iface.h | 6 +- .../make_dimension/dimension_no_concepts.h | 6 +- .../make_dimension/downcasting_concepts_all.h | 8 +- .../make_dimension/downcasting_no_concepts.h | 4 +- test/unit_test/static/quantity_test.cpp | 65 ++++---- test/unit_test/static/unit_test.cpp | 16 +- 22 files changed, 354 insertions(+), 160 deletions(-) create mode 100644 example/avg_velocity.cpp rename example/{example.cpp => unknown_dimension.cpp} (65%) rename src/include/units/{dimensions => physical/si}/acceleration.h (62%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 931c79d1..863addb5 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/example/CMakeLists.txt b/example/CMakeLists.txt index fb500daf..d9f229c5 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -20,15 +20,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -# example app -add_executable(example example.cpp) -target_link_libraries(example - PRIVATE - mp::units -) +function(add_example target) + add_executable(${target} ${target}.cpp) + target_link_libraries(${target} PRIVATE mp::units) +endfunction() -add_executable(measurement measurement.cpp) -target_link_libraries(measurement - PRIVATE - mp::units -) +add_example(avg_velocity) +add_example(measurement) +add_example(unknown_dimension) diff --git a/example/avg_velocity.cpp b/example/avg_velocity.cpp new file mode 100644 index 00000000..5c34003a --- /dev/null +++ b/example/avg_velocity.cpp @@ -0,0 +1,147 @@ +// 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 +#include + +namespace { + +constexpr units::si::velocity +fixed_int_si_avg_speed(units::si::length d, + units::si::time t) +{ + return d / t; +} + +constexpr units::si::velocity +fixed_double_si_avg_speed(units::si::length d, + units::si::time t) +{ + return d / t; +} + +template +constexpr units::Velocity AUTO si_avg_speed(units::si::length d, + units::si::time t) +{ + return d / t; +} + +constexpr units::Velocity AUTO avg_speed(units::Length AUTO d, units::Time AUTO t) +{ + return d / t; +} + +template +void print_result(D distance, T duration, V velocity) +{ + const auto result_in_kmph = units::quantity_cast(velocity); + std::cout << "Average speed of a car that makes " << distance << " in " + << duration << " is " << result_in_kmph << ".\n"; +} + +void example() +{ + using namespace units; + using namespace units::si::literals; + + // SI (int) + { + constexpr Length AUTO distance = 220km; // constructed from a UDL + constexpr si::time duration(2); // constructed from a value + + std::cout << "SI units with 'int' as representation\n"; + + print_result(distance, duration, fixed_int_si_avg_speed(distance, duration)); + print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); + + // the framework will not allow a division (and multiplication) of different dimensions + // with two integral representation (at least one of them have to ba floating-point one) + print_result(distance, duration, si_avg_speed(quantity_cast(distance), duration)); + print_result(distance, duration, avg_speed(quantity_cast(distance), duration)); + } + + // SI (double) + { + constexpr Length AUTO distance = 220.km; // constructed from a UDL + constexpr si::time duration(2); // constructed from a value + + std::cout << "\nSI units with 'double' as representation\n"; + + // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed + print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast(distance), quantity_cast(duration))); + + print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); + print_result(distance, duration, si_avg_speed(distance, duration)); + print_result(distance, duration, avg_speed(distance, duration)); + } + + // Customary Units (int) + { + constexpr Length AUTO distance = 140mi; // constructed from a UDL + constexpr si::time duration(2); // constructed from a value + + std::cout << "\nUS Customary Units with 'int' as representation\n"; + + // it is not possible to make a lossless conversion of miles to meters on an integral type + // (explicit cast needed) + print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast(distance), duration)); + print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); + + // the framework will not allow a division (and multiplication) of different dimensions + // with two integral representation (at least one of them have to ba floating-point one) + print_result(distance, duration, si_avg_speed(quantity_cast(distance), duration)); + print_result(distance, duration, avg_speed(quantity_cast(distance), duration)); + } + + // Customary Units (double) + { + constexpr Length AUTO distance = 140.mi; // constructed from a UDL + constexpr si::time duration(2); // constructed from a value + + std::cout << "\nUS Customary Units with 'double' as representation\n"; + + // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed + // also it is not possible to make a lossless conversion of miles to meters on an integral type + // (explicit cast needed) + print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast(distance), quantity_cast(duration))); + + print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); + print_result(distance, duration, si_avg_speed(distance, duration)); + print_result(distance, duration, avg_speed(distance, duration)); + } +} + +} // namespace + +int main() +{ + try { + example(); + } + catch (const std::exception& ex) { + std::cerr << "Unhandled std exception caught: " << ex.what() << '\n'; + } + catch (...) { + std::cerr << "Unhandled unknown exception caught\n"; + } +} diff --git a/example/measurement.cpp b/example/measurement.cpp index 09c6b6e1..db7163a5 100644 --- a/example/measurement.cpp +++ b/example/measurement.cpp @@ -20,14 +20,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include +#include #include namespace { - template typename Trait> - concept Satisfies = Trait::value; - // root sum of squares template T rss(const T& v1, const T& v2) @@ -118,24 +115,38 @@ namespace { value_type uncertainty_{}; }; - template - using m_quantity = units::quantity>; - } // namespace template inline constexpr bool units::treat_as_floating_point> = std::is_floating_point_v; +namespace { + +void example() +{ + using namespace units; + + const auto a = si::acceleration>(measurement(9.8, 0.1)); + const auto t = si::time>(measurement(1.2, 0.1)); + + const Velocity AUTO v1 = a * t; + std::cout << a << " * " << t << " = " << v1 << " = " << quantity_cast(v1) << '\n'; + + si::length> length(measurement(123., 1.)); + std::cout << "10 * " << length << " = " << 10 * length << '\n'; +} + +} // namespace int main() { - const auto a = m_quantity(measurement(9.8, 0.1)); - const auto t = m_quantity(measurement(1.2, 0.1)); - - units::Velocity AUTO v1 = a * t; - m_quantity v2(v1); - std::cout << a << " * " << t << " = " << v1 << " = " << v2 << '\n'; - - m_quantity length(measurement(123., 1.)); - std::cout << "10 * " << length << " = " << 10 * length << '\n'; + try { + example(); + } + catch (const std::exception& ex) { + std::cerr << "Unhandled std exception caught: " << ex.what() << '\n'; + } + catch (...) { + std::cerr << "Unhandled unknown exception caught\n"; + } } diff --git a/example/example.cpp b/example/unknown_dimension.cpp similarity index 65% rename from example/example.cpp rename to example/unknown_dimension.cpp index c1716594..9cd31340 100644 --- a/example/example.cpp +++ b/example/unknown_dimension.cpp @@ -20,43 +20,43 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include +#include #include namespace { -using namespace units::literals; - template constexpr units::Velocity AUTO avg_speed(D d, T t) { return d / t; } -template -void example_1(V v, T t) +void example() { - const units::Length AUTO distance = v * t; - std::cout << "A car driving " << v << " in a time of " << t << " will pass " - << units::quantity_cast>(distance) << ".\n"; + using namespace units::si::literals; + + units::Length AUTO d1 = 123m; + units::Time AUTO t1 = 10s; + units::Velocity AUTO v1 = avg_speed(d1, t1); + + auto temp1 = v1 * 50m; // produces intermediate unknown dimension with 'unknown_unit' as its 'coherent_unit' + units::Velocity AUTO v2 = temp1 / 100m; // back to known dimensions again + units::Length AUTO d2 = v2 * 60s; + + std::cout << "d1 = " << d1 << '\n'; + std::cout << "t1 = " << t1 << '\n'; + std::cout << "v1 = " << v1 << '\n'; + std::cout << "temp1 = " << temp1 << '\n'; + std::cout << "v2 = " << v2 << '\n'; + std::cout << "d2 = " << d2 << '\n'; } -void example_2(double distance_v, double duration_v) -{ - units::quantity distance(distance_v); - units::quantity duration(duration_v); - const auto kmph = quantity_cast(avg_speed(distance, duration)); - std::cout << "Average speed of a car that makes " << distance << " in " - << duration << " is " << kmph << ".\n"; -} - -} +} // namespace int main() { try { - example_1(60kmph, 10.0min); - example_2(220, 2); + example(); } catch (const std::exception& ex) { std::cerr << "Unhandled std exception caught: " << ex.what() << '\n'; diff --git a/src/include/units/bits/downcasting.h b/src/include/units/bits/downcasting.h index 043b1522..822bf909 100644 --- a/src/include/units/bits/downcasting.h +++ b/src/include/units/bits/downcasting.h @@ -29,16 +29,16 @@ namespace units { template struct downcast_base { - using base_type = BaseType; + using downcast_base_type = BaseType; friend auto downcast_guide(downcast_base); }; template concept Downcastable = requires { - typename T::base_type; + typename T::downcast_base_type; } && - std::derived_from>; + std::derived_from>; template struct downcast_child : T { @@ -67,6 +67,6 @@ namespace units { using downcast = decltype(detail::downcast_impl()); template - using downcast_base_t = T::base_type; + using downcast_base_t = T::downcast_base_type; } // namespace units diff --git a/src/include/units/bits/unit_concept.h b/src/include/units/bits/unit_concept.h index 1f60253a..d8e675b3 100644 --- a/src/include/units/bits/unit_concept.h +++ b/src/include/units/bits/unit_concept.h @@ -27,15 +27,14 @@ namespace units { -namespace detail { +template +concept UnitRatio = Ratio && (R::num * R::den > 0); -template -struct reference_unit; - -} // namespace detail +template +struct scaled_unit; // Unit template -concept Unit = is_derived_from_instantiation; +concept Unit = is_derived_from_instantiation; } // namespace units diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h index 78f109ec..35e969db 100644 --- a/src/include/units/derived_dimension.h +++ b/src/include/units/derived_dimension.h @@ -246,15 +246,39 @@ struct derived_dimension : downcast_child +concept PredefinedDimension = Dimension && requires { typename T::coherent_unit; }; + +template +auto unit_for_dimension_impl() +{ + if constexpr(PredefinedDimension) { + return downcast>{}; + } + else { + return scaled_unit{}; + } +} + +template +using unit_for_dimension = decltype(unit_for_dimension_impl()); + +} + // same_dim template -inline constexpr bool same_dim; +inline constexpr bool same_dim = false; template inline constexpr bool same_dim = std::is_same_v; template -inline constexpr bool same_dim = std::is_same_v; +inline constexpr bool same_dim = std::is_same_v; // dim_invert namespace detail { @@ -321,7 +345,7 @@ struct dimension_multiply_impl { template struct dimension_multiply_impl { - using type = downcast>, typename D2::base_type>>; + using type = downcast>, typename D2::downcast_base_type>>; }; template @@ -331,7 +355,7 @@ struct dimension_multiply_impl { template struct dimension_multiply_impl { - using type = downcast>; + using type = downcast>; }; } // namespace detail @@ -360,7 +384,7 @@ struct dimension_sqrt_impl>> { template struct dimension_sqrt_impl { - using type = dimension_sqrt_impl; + using type = dimension_sqrt_impl; }; template @@ -371,7 +395,7 @@ struct dimension_sqrt_impl> { } // namespace detail template -using dimension_sqrt = detail::dimension_sqrt_impl::type; +using dimension_sqrt = detail::dimension_sqrt_impl::type; // dimension_pow namespace detail { diff --git a/src/include/units/math.h b/src/include/units/math.h index be9274cd..ae13cf2a 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/physical/dimensions.h b/src/include/units/physical/dimensions.h index 7945dc8b..26959f89 100644 --- a/src/include/units/physical/dimensions.h +++ b/src/include/units/physical/dimensions.h @@ -35,7 +35,7 @@ template typename DimTemplate> concept DimensionOf = (Dimension || BaseDimension) && is_derived_from_instantiation; template typename DimTemplate> -concept QuantityOf = Quantity && is_derived_from_instantiation; +concept QuantityOf = Quantity && is_derived_from_instantiation; // ------------------------ base dimensions ----------------------------- diff --git a/src/include/units/dimensions/acceleration.h b/src/include/units/physical/si/acceleration.h similarity index 62% rename from src/include/units/dimensions/acceleration.h rename to src/include/units/physical/si/acceleration.h index 891ad786..9a31f537 100644 --- a/src/include/units/dimensions/acceleration.h +++ b/src/include/units/physical/si/acceleration.h @@ -22,23 +22,23 @@ #pragma once -#include +#include +#include -namespace units { +namespace units::si { - struct acceleration : derived_dimension, exp> {}; +struct metre_per_second_sq : unit {}; +struct dim_acceleration : physical::dim_acceleration {}; - template - concept Acceleration = QuantityOf; +template +using acceleration = quantity; - struct metre_per_second_sq : coherent_derived_unit {}; +inline namespace literals { - inline namespace literals { +// mps_sq +constexpr auto operator""mps_sq(unsigned long long l) { return acceleration(l); } +constexpr auto operator""mps_sq(long double l) { return acceleration(l); } - // mps_sq - constexpr auto operator""mps_sq(unsigned long long l) { return quantity(l); } - constexpr auto operator""mps_sq(long double l) { return quantity(l); } +} // namespace literals - } // namespace literals - -} // namespace units +} // namespace units::si diff --git a/src/include/units/physical/si/length.h b/src/include/units/physical/si/length.h index bd9c27df..5143ba10 100644 --- a/src/include/units/physical/si/length.h +++ b/src/include/units/physical/si/length.h @@ -58,10 +58,10 @@ constexpr auto operator"" km(long double l) { return length, metre> {}; -struct foot : scaled_unit, yard> {}; -struct inch : scaled_unit, foot> {}; -struct mile : scaled_unit, yard> {}; +struct yard : named_scaled_unit, metre> {}; +struct foot : named_scaled_unit, yard> {}; +struct inch : named_scaled_unit, foot> {}; +struct mile : named_scaled_unit, yard> {}; inline namespace literals { diff --git a/src/include/units/physical/si/time.h b/src/include/units/physical/si/time.h index 0e1b409e..56534eba 100644 --- a/src/include/units/physical/si/time.h +++ b/src/include/units/physical/si/time.h @@ -31,8 +31,8 @@ struct second : named_unit {}; struct nanosecond : prefixed_unit {}; struct microsecond : prefixed_unit {}; struct millisecond : prefixed_unit {}; -struct minute : scaled_unit, second> {}; -struct hour : scaled_unit, second> {}; +struct minute : named_scaled_unit, second> {}; +struct hour : named_scaled_unit, second> {}; struct dim_time : physical::dim_time {}; diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 7b62210b..b9d64bf5 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -74,7 +74,7 @@ struct common_quantity_impl, quantity, Rep> { template struct common_quantity_impl, quantity, Rep> { using type = quantity< - D, downcast>>, + D, downcast>>, Rep>; }; @@ -154,7 +154,7 @@ template { using c_ratio = ratio_divide; using c_rep = std::common_type_t; - using ret_unit = downcast>; + using ret_unit = downcast>; using ret = quantity; using cast = detail::quantity_cast_impl; return cast::cast(q); @@ -410,7 +410,7 @@ public: template friend std::basic_ostream& operator<<(std::basic_ostream& os, const quantity& q) { - return os; // << q.count() << " " << detail::unit_text(); + return os << q.count(); // << " " << detail::unit_text(); TODO add support } }; @@ -466,7 +466,7 @@ template; using ratio = ratio_multiply; - using unit = downcast>; + using unit = detail::unit_for_dimension; using common_rep = decltype(lhs.count() * rhs.count()); using ret = quantity; return ret(lhs.count() * rhs.count()); @@ -480,7 +480,7 @@ template using dim = dim_invert; using ratio = ratio; - using unit = downcast>; + using unit = detail::unit_for_dimension; using common_rep = decltype(v / q.count()); using ret = quantity; return ret(v / q.count()); @@ -518,8 +518,8 @@ template; - using unit = downcast>>; + using ratio = ratio_divide; + using unit = detail::unit_for_dimension; using ret = quantity; return ret(lhs.count() / rhs.count()); } diff --git a/src/include/units/unit.h b/src/include/units/unit.h index 09ab02f8..6f7342ca 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -32,31 +32,35 @@ namespace units { -namespace detail { - -template -struct reference_unit : downcast_base> { +// scaled_unit +template +struct scaled_unit : downcast_base> { using reference = U; using ratio = R; }; -} // namespace detail - // UnitOf +namespace detail { + +template +concept SameReference = std::same_as; + +} + template concept UnitOf = Unit && Dimension && - std::same_as; + (std::same_as || detail::SameReference); namespace detail { -// same_reference_units +// same_scaled_units template -inline constexpr bool same_reference_units = false; +inline constexpr bool same_scaled_units = false; template -inline constexpr bool same_reference_units, Us...> = (UnitOf && ...); +inline constexpr bool same_scaled_units, Us...> = (UnitOf && ...); // deduced_unit template @@ -91,7 +95,7 @@ struct derived_ratio, U, URest...> { template using deduced_unit = - reference_unit::ratio>; + scaled_unit::ratio>; } // namespace detail @@ -112,7 +116,7 @@ using deduced_unit = * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) */ template -struct unit : downcast_child>> { +struct unit : downcast_child>> { static constexpr bool is_named = false; using prefix_type = no_prefix; }; @@ -130,7 +134,7 @@ struct unit : downcast_child>> { * @tparam PT no_prefix or a type of prefix family */ template -struct named_unit : downcast_child>> { +struct named_unit : downcast_child>> { static constexpr bool is_named = true; static constexpr auto symbol = Symbol; using prefix_type = PT; @@ -150,8 +154,8 @@ struct named_unit : downcast_child * @tparam R a scale to apply to U * @tparam U a reference unit to scale */ -template -struct scaled_unit : downcast_child>> { +template +struct named_scaled_unit : downcast_child>> { static constexpr bool is_named = true; static constexpr auto symbol = Symbol; using prefix_type = PT; @@ -171,11 +175,11 @@ struct scaled_unit : downcast_child 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>> { + downcast_child>> { static constexpr bool is_named = true; static constexpr auto symbol = P::symbol + U::symbol; using prefix_type = P::prefix_type; @@ -194,7 +198,7 @@ struct prefixed_unit : * @tparam URest the units for the rest of dimensions from the recipe */ template - requires detail::same_reference_units && + requires detail::same_scaled_units && (U::is_named && (URest::is_named && ... && true)) struct deduced_unit : downcast_child> { static constexpr bool is_named = false; diff --git a/test/metabench/make_dimension/dimension_concepts_all.h b/test/metabench/make_dimension/dimension_concepts_all.h index f3293ccd..755da1e1 100644 --- a/test/metabench/make_dimension/dimension_concepts_all.h +++ b/test/metabench/make_dimension/dimension_concepts_all.h @@ -131,7 +131,7 @@ namespace units { struct dim_invert> : std::type_identity...>>> {}; template - using dim_invert_t = dim_invert::type; + using dim_invert_t = dim_invert::type; // make_dimension @@ -196,7 +196,7 @@ namespace units { struct dimension_multiply, dimension> : std::type_identity, dimension>>> {}; template - using dimension_multiply_t = dimension_multiply::type; + using dimension_multiply_t = dimension_multiply::type; // dimension_divide @@ -209,6 +209,6 @@ namespace units { }; template - using dimension_divide_t = dimension_divide::type; + using dimension_divide_t = dimension_divide::type; } // namespace units diff --git a/test/metabench/make_dimension/dimension_concepts_iface.h b/test/metabench/make_dimension/dimension_concepts_iface.h index d262e562..ca104c0d 100644 --- a/test/metabench/make_dimension/dimension_concepts_iface.h +++ b/test/metabench/make_dimension/dimension_concepts_iface.h @@ -131,7 +131,7 @@ namespace units { struct dim_invert> : std::type_identity...>>> {}; template - using dim_invert_t = dim_invert::type; + using dim_invert_t = dim_invert::type; // make_dimension @@ -196,7 +196,7 @@ namespace units { struct dimension_multiply, dimension> : std::type_identity, dimension>>> {}; template - using dimension_multiply_t = dimension_multiply::type; + using dimension_multiply_t = dimension_multiply::type; // dimension_divide @@ -209,6 +209,6 @@ namespace units { }; template - using dimension_divide_t = dimension_divide::type; + using dimension_divide_t = dimension_divide::type; } // namespace units diff --git a/test/metabench/make_dimension/dimension_no_concepts.h b/test/metabench/make_dimension/dimension_no_concepts.h index 188f92f8..ac8f8688 100644 --- a/test/metabench/make_dimension/dimension_no_concepts.h +++ b/test/metabench/make_dimension/dimension_no_concepts.h @@ -102,7 +102,7 @@ namespace units { struct dim_invert> : std::type_identity...>>> {}; template - using dim_invert_t = dim_invert::type; + using dim_invert_t = dim_invert::type; // make_dimension @@ -167,7 +167,7 @@ namespace units { struct dimension_multiply, dimension> : std::type_identity, dimension>>> {}; template - using dimension_multiply_t = dimension_multiply::type; + using dimension_multiply_t = dimension_multiply::type; // dimension_divide @@ -180,6 +180,6 @@ namespace units { }; template - using dimension_divide_t = dimension_divide::type; + using dimension_divide_t = dimension_divide::type; } // namespace units diff --git a/test/metabench/make_dimension/downcasting_concepts_all.h b/test/metabench/make_dimension/downcasting_concepts_all.h index db0c3b3b..68bd5230 100644 --- a/test/metabench/make_dimension/downcasting_concepts_all.h +++ b/test/metabench/make_dimension/downcasting_concepts_all.h @@ -29,18 +29,18 @@ namespace units { template struct downcast_base { - using base_type = BaseType; + using downcast_base_type = BaseType; }; template concept Downcastable = requires { - typename T::base_type; + typename T::downcast_base_type; } && - std::derived_from>; + std::derived_from>; template - using downcast_base_t = T::base_type; + using downcast_base_t = T::downcast_base_type; template struct downcast_traits : std::type_identity {}; diff --git a/test/metabench/make_dimension/downcasting_no_concepts.h b/test/metabench/make_dimension/downcasting_no_concepts.h index 519c469d..051b0ff5 100644 --- a/test/metabench/make_dimension/downcasting_no_concepts.h +++ b/test/metabench/make_dimension/downcasting_no_concepts.h @@ -29,11 +29,11 @@ namespace units { template struct downcast_base { - using base_type = BaseType; + using downcast_base_type = BaseType; }; template - using downcast_base_t = T::base_type; + using downcast_base_t = T::downcast_base_type; template struct downcast_traits : std::type_identity {}; diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index ff029a5f..3636cf32 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -109,10 +109,10 @@ using namespace units::si; // class invariants -// constexpr quantity q(1); // should not compile -// constexpr quantity> error(0m); // should trigger a static_assert -// constexpr quantity error(0); // should trigger a static_assert -// constexpr quantity>, int> error(0); // should trigger a static_assert +// constexpr quantity error(0); // should not compile (unit of a different dimension) +// constexpr quantity> error(0); // should not compile (quantity used as Rep) +// constexpr quantity error(0); // should not compile (reordered arguments) +// constexpr quantity>, int> error(0); // should not compile (negative unit ratio) // member types @@ -134,9 +134,9 @@ static_assert(length(km).count() == km.count()); static_assert(length(1).count() == 1); static_assert(length(my_value(1)).count() == 1); static_assert(length(1).count() == my_int{1}); -// static_assert(length(1.0).count() == 1); // should not compile -// static_assert(length(my_value(1.0)).count() == 1); // should not compile -// static_assert(length(1.0).count() == 1); // should not compile +// static_assert(length(1.0).count() == 1); // should not compile (truncating conversion) +// static_assert(length(my_value(1.0)).count() == 1); // should not compile (truncating conversion) +// static_assert(length(1.0).count() == my_int{1}); // should not compile (truncating conversion) static_assert(length(1.0).count() == 1.0); static_assert(length(my_value(1.0)).count() == 1.0); static_assert(length(1).count() == 1.0); @@ -147,27 +147,23 @@ static_assert(length(1).count() == my_double{1.0}); static_assert(length(3.14).count() == my_double{3.14}); static_assert(length(km).count() == 1000); -// static_assert(length(length(3.14)).count() == 3); // should not compile +// static_assert(length(length(3.14)).count() == 3); // should not compile (truncating conversion) static_assert(length(quantity_cast>(3.14m)).count() == 3); -// static_assert(length(length(1000.0)).count() == 1000); // should not compile -// static_assert(length(1000.0m).count() == 1000); // should not compile +// static_assert(length(length(1000.0)).count() == 1000); // should not compile (truncating conversion) +// static_assert(length(1000.0m).count() == my_int{1000}); // should not compile (truncating conversion) static_assert(length(1000.0m).count() == 1000.0); static_assert(length(length(1000.0)).count() == 1000.0); static_assert(length(1000.0m).count() == my_double{1000.0}); static_assert(length(km).count() == 1000.0); static_assert(length(km).count() == my_double{1000.0}); static_assert(length(1km).count() == 1000); -// static_assert(length(1_s).count() == 1); // should not compile -// static_assert(length(1010m).count() == 1); // should not compile +// static_assert(length(1s).count() == 1); // should not compile (different dimensions) +//static_assert(length(1010m).count() == 1); // should not compile (truncating conversion) static_assert(length(quantity_cast>(1010m)).count() == 1); // assignment operator -static_assert([]() { - length l1(1), l2(2); - return l2 = l1; -}() - .count() == 1); +static_assert([]() { length l1(1), l2(2); return l2 = l1; }().count() == 1); // static member functions @@ -218,13 +214,13 @@ static_assert((1m *= 2).count() == 2); static_assert((2m /= 2).count() == 1); static_assert((7m %= 2).count() == 1); static_assert((7m %= 2m).count() == 1); -// static_assert((7.m %= 2.).count() == 1); // should not compile -// static_assert((7.m %= 2).count() == 1); // should not compile -// static_assert((7m %= 2.).count() == 1); // should not compile +// static_assert((7.m %= 2.).count() == 1); // should not compile (operation not allowed for floating-point types) +// static_assert((7.m %= 2).count() == 1); // should not compile (operation not allowed for floating-point types) +// static_assert((7m %= 2.).count() == 1); // should not compile (operation not allowed for floating-point types) static_assert((7m %= 2m).count() == 1); -// static_assert((7.m %= 2.m).count() == 1); // should not compile -// static_assert((7.m %= 2m).count() == 1); // should not compile -// static_assert((7m %= 2.m).count() == 1); // should not compile +// static_assert((7.m %= 2.m).count() == 1); // should not compile (operation not allowed for floating-point types) +// static_assert((7.m %= 2m).count() == 1); // should not compile (operation not allowed for floating-point types) +// static_assert((7m %= 2.m).count() == 1); // should not compile (operation not allowed for floating-point types) // non-member arithmetic operators @@ -239,13 +235,27 @@ static_assert(std::is_same_v() * 1.0), length()), length>); static_assert( std::is_same_v() * si::time()), length>); +static_assert( + std::is_same_v() * si::time()), length>, int>>); +// TODO uncomment below when fixed in gcc +// static_assert(std::is_same_v() * si::time()), +// quantity, exp>, scaled_unit>>>); static_assert(std::is_same_v()), frequency>); +static_assert(std::is_same_v()), frequency>, int>>); static_assert(std::is_same_v()), si::time>); +// TODO uncomment below when fixed in gcc +// static_assert(std::is_same_v()), +// quantity>, scaled_unit>>>); static_assert(std::is_same_v() / 1.0), length>); static_assert(std::is_same_v() / length()), double>); static_assert(std::is_same_v() / length()), double>); static_assert( std::is_same_v() / si::time()), velocity>); +static_assert( + std::is_same_v() / si::time()), velocity>>>); +// TODO uncomment below when fixed in gcc +// static_assert(std::is_same_v() / length()), +// quantity, exp>, scaled_unit>>>); static_assert(std::is_same_v() % short(1)), length>); static_assert(std::is_same_v() % length(1)), length>); @@ -307,15 +317,18 @@ static_assert(std::is_same_v, lengt // quantity_cast -static_assert(std::is_same_v>>(2km))::unit, metre>); +static_assert(std::is_same_v>>(2km))::unit, metre>); -// static_assert(quantity_cast(2km).count() == 2000); // should not compile static_assert(quantity_cast>(2km).count() == 2000); static_assert(quantity_cast>(2000m).count() == 2); +static_assert(quantity_cast>(1.23m).count() == 1); +static_assert(quantity_cast(2km).count() == 2000); +static_assert(quantity_cast(2000m).count() == 2); +static_assert(quantity_cast(1.23m).count() == 1); // time -// static_assert(1s == 1m); // should not compile +// static_assert(1s == 1m); // should not compile (different dimensions) static_assert(1h == 3600s); // length diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index 00172c9a..58eb8e9c 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -30,23 +30,23 @@ using namespace units; struct metre : named_unit {}; struct centimetre : prefixed_unit {}; struct kilometre : prefixed_unit {}; -struct yard : scaled_unit, metre> {}; -struct foot : scaled_unit, yard> {}; +struct yard : named_scaled_unit, metre> {}; +struct foot : named_scaled_unit, yard> {}; struct dim_length : base_dimension<"length", metre> {}; struct second : named_unit {}; -struct hour : scaled_unit, second> {}; +struct hour : named_scaled_unit, second> {}; struct dim_time : base_dimension<"time", second> {}; struct metre_per_second : unit {}; struct dim_velocity : derived_dimension, exp> {}; struct kilometre_per_hour : deduced_unit {}; -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(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(centimetre::symbol == "cm"); static_assert(kilometre::symbol == "km"); From 71eec182f9a9650fe32237f9a9f0671a530cdc3a Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 6 Dec 2019 12:19:11 +0100 Subject: [PATCH 06/44] clang-format updated --- .clang-format | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.clang-format b/.clang-format index ef4261c8..6b47c3fc 100644 --- a/.clang-format +++ b/.clang-format @@ -25,12 +25,12 @@ AccessModifierOffset: -2 # AlwaysBreakTemplateDeclarations: Yes # BinPackArguments: true # BinPackParameters: true -# BraceWrapping: +BraceWrapping: # AfterCaseLabel: false # AfterClass: false # AfterControlStatement: false # AfterEnum: false -# AfterFunction: false + AfterFunction: true # AfterNamespace: false # AfterObjCDeclaration: false # AfterStruct: false @@ -44,9 +44,9 @@ AccessModifierOffset: -2 # SplitEmptyNamespace: true # BreakAfterJavaFieldAnnotations: false # BreakBeforeBinaryOperators: None -BreakBeforeBraces: Stroustrup +BreakBeforeBraces: Custom # BreakBeforeInheritanceComma: false -BreakBeforeTernaryOperators: false +# BreakBeforeTernaryOperators: true BreakConstructorInitializers: AfterColon BreakConstructorInitializersBeforeComma: true BreakInheritanceList: AfterColon @@ -93,7 +93,7 @@ IncludeCategories: # MacroBlockBegin: '' # MacroBlockEnd: '' # MaxEmptyLinesToKeep: 1 -NamespaceIndentation: All +# NamespaceIndentation: None # ObjCBinPackProtocolList: Never # ObjCBlockIndentWidth: 2 # ObjCSpaceAfterProperty: false @@ -143,9 +143,9 @@ NamespaceIndentation: All SpaceAfterTemplateKeyword: false # SpaceBeforeAssignmentOperators: true # SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: false +# SpaceBeforeCtorInitializerColon: true # SpaceBeforeInheritanceColon: true -SpaceBeforeParens: Never +# SpaceBeforeParens: ControlStatements # SpaceBeforeRangeBasedForLoopColon: true # SpaceInEmptyBlock: false # SpaceInEmptyParentheses: false From 33f14f39f3f72cfefd8b2575ea561fac01b59df1 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Fri, 6 Dec 2019 12:56:44 +0100 Subject: [PATCH 07/44] math_test enabled --- src/include/units/derived_dimension.h | 7 ++++++- src/include/units/math.h | 12 +++++++----- test/unit_test/static/CMakeLists.txt | 2 +- test/unit_test/static/math_test.cpp | 6 +++--- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h index 35e969db..57323161 100644 --- a/src/include/units/derived_dimension.h +++ b/src/include/units/derived_dimension.h @@ -408,8 +408,13 @@ struct dimension_pow_impl { using type = downcast>>; }; +template +struct dimension_pow_impl { + using type = D; +}; + template -struct dimension_pow_impl, N> { +struct dimension_pow_impl>, N> { using type = D; }; diff --git a/src/include/units/math.h b/src/include/units/math.h index ae13cf2a..c6bb2bc4 100644 --- a/src/include/units/math.h +++ b/src/include/units/math.h @@ -38,16 +38,18 @@ namespace units { inline Quantity AUTO pow(const quantity& q) noexcept { using dim = dimension_pow; - using r = ratio_pow; - return quantity>, Rep>(static_cast(std::pow(q.count(), N))); + using ratio = ratio_pow; + using unit = detail::unit_for_dimension; + return quantity(static_cast(std::pow(q.count(), N))); } template inline Quantity AUTO sqrt(const quantity& q) noexcept { - using dim = dimension_sqrt; - using r = ratio_sqrt; - return quantity>, Rep>(static_cast(std::sqrt(q.count()))); + using dim = dimension_sqrt; + using ratio = ratio_sqrt; + using unit = detail::unit_for_dimension; + return quantity(static_cast(std::sqrt(q.count()))); } } // namespace units diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index 0eb0c108..0babe95d 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -25,7 +25,7 @@ add_library(unit_tests_static # custom_unit_test.cpp dimension_test.cpp # fixed_string_test.cpp -# math_test.cpp + math_test.cpp # new_design.cpp quantity_test.cpp ratio_test.cpp diff --git a/test/unit_test/static/math_test.cpp b/test/unit_test/static/math_test.cpp index 09a4759c..752c58b7 100644 --- a/test/unit_test/static/math_test.cpp +++ b/test/unit_test/static/math_test.cpp @@ -20,13 +20,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/dimensions/area.h" +#include "units/physical/si/area.h" #include "units/math.h" -using namespace units; - namespace { + using namespace units::si::literals; + static_assert(std::is_same_v(2m)), std::int64_t>); static_assert(std::is_same_v(2m)), decltype(2m)>); static_assert(std::is_same_v(2m)), decltype(4sq_m)>); From 22eda11beac0f86e9b25e25a3d161f4b41e921e5 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 7 Dec 2019 16:30:40 +0100 Subject: [PATCH 08/44] More refactoring - unknown_unit and unknown_dimension added - the support for remaining SI units added --- src/include/units/derived_dimension.h | 180 +-------------- src/include/units/dimension_op.h | 215 ++++++++++++++++++ src/include/units/dimensions/capacitance.h | 46 ---- src/include/units/dimensions/energy.h | 67 ------ src/include/units/dimensions/force.h | 46 ---- src/include/units/dimensions/mass.h | 50 ---- src/include/units/dimensions/physical.h | 124 ---------- src/include/units/dimensions/power.h | 66 ------ src/include/units/dimensions/pressure.h | 46 ---- .../units/dimensions/surface_tension.h | 45 ---- src/include/units/dimensions/voltage.h | 48 ---- src/include/units/dimensions/volume.h | 64 ------ src/include/units/math.h | 4 +- src/include/units/physical/dimensions.h | 35 +++ src/include/units/physical/si/acceleration.h | 1 + src/include/units/physical/si/area.h | 1 + src/include/units/physical/si/capacitance.h | 48 ++++ .../{dimensions => physical/si}/current.h | 25 +- .../units/physical/si/electric_charge.h | 47 ++++ src/include/units/physical/si/energy.h | 67 ++++++ src/include/units/physical/si/force.h | 48 ++++ src/include/units/physical/si/frequency.h | 1 + src/include/units/physical/si/length.h | 1 + .../si}/luminous_intensity.h | 25 +- .../electric_charge.h => physical/si/mass.h} | 32 +-- src/include/units/physical/si/power.h | 67 ++++++ src/include/units/physical/si/pressure.h | 48 ++++ .../{dimensions => physical/si}/substance.h | 25 +- .../units/physical/si/surface_tension.h | 46 ++++ .../si/temperature.h} | 28 ++- src/include/units/physical/si/time.h | 1 + src/include/units/physical/si/velocity.h | 1 + .../temperature.h => physical/si/voltage.h} | 27 ++- src/include/units/physical/si/volume.h | 66 ++++++ src/include/units/quantity.h | 22 +- src/include/units/unit.h | 151 ++++++------ test/unit_test/static/CMakeLists.txt | 5 +- ...mension_test.cpp => dimension_op_test.cpp} | 32 +-- test/unit_test/static/si_test.cpp | 193 ++++++++++++++++ test/unit_test/static/unit_test.cpp | 3 + 40 files changed, 1094 insertions(+), 953 deletions(-) create mode 100644 src/include/units/dimension_op.h delete mode 100644 src/include/units/dimensions/capacitance.h delete mode 100644 src/include/units/dimensions/energy.h delete mode 100644 src/include/units/dimensions/force.h delete mode 100644 src/include/units/dimensions/mass.h delete mode 100644 src/include/units/dimensions/physical.h delete mode 100644 src/include/units/dimensions/power.h delete mode 100644 src/include/units/dimensions/pressure.h delete mode 100644 src/include/units/dimensions/surface_tension.h delete mode 100644 src/include/units/dimensions/voltage.h delete mode 100644 src/include/units/dimensions/volume.h create mode 100644 src/include/units/physical/si/capacitance.h rename src/include/units/{dimensions => physical/si}/current.h (68%) create mode 100644 src/include/units/physical/si/electric_charge.h create mode 100644 src/include/units/physical/si/energy.h create mode 100644 src/include/units/physical/si/force.h rename src/include/units/{dimensions => physical/si}/luminous_intensity.h (64%) rename src/include/units/{dimensions/electric_charge.h => physical/si/mass.h} (59%) create mode 100644 src/include/units/physical/si/power.h create mode 100644 src/include/units/physical/si/pressure.h rename src/include/units/{dimensions => physical/si}/substance.h (67%) create mode 100644 src/include/units/physical/si/surface_tension.h rename src/include/units/{dimensions/base_dimensions.h => physical/si/temperature.h} (65%) rename src/include/units/{dimensions/temperature.h => physical/si/voltage.h} (64%) create mode 100644 src/include/units/physical/si/volume.h rename test/unit_test/static/{dimension_test.cpp => dimension_op_test.cpp} (87%) create mode 100644 test/unit_test/static/si_test.cpp diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h index 57323161..cf35b99d 100644 --- a/src/include/units/derived_dimension.h +++ b/src/include/units/derived_dimension.h @@ -229,6 +229,9 @@ using make_dimension = dim_consolidate::t * 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. * + * 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. + * * 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. @@ -246,30 +249,6 @@ struct derived_dimension : downcast_child -concept PredefinedDimension = Dimension && requires { typename T::coherent_unit; }; - -template -auto unit_for_dimension_impl() -{ - if constexpr(PredefinedDimension) { - return downcast>{}; - } - else { - return scaled_unit{}; - } -} - -template -using unit_for_dimension = decltype(unit_for_dimension_impl()); - -} - // same_dim template inline constexpr bool same_dim = false; @@ -280,157 +259,4 @@ 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...>>; -}; - -template -struct dim_invert_impl : dim_invert_impl> { -}; - -} // 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::downcast_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 = downcast>>; -}; - -template -struct dimension_pow_impl { - using type = D; -}; - -template -struct dimension_pow_impl>, N> { - using type = D; -}; - -template -struct dimension_pow_impl { - using type = dimension_pow_impl, N>; -}; - -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_op.h b/src/include/units/dimension_op.h new file mode 100644 index 00000000..294de4b4 --- /dev/null +++ b/src/include/units/dimension_op.h @@ -0,0 +1,215 @@ +// 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 { + +/** + * @brief Unknown dimension + * + * Sometimes a temporary partial result of a complex calculation may not result in a predefined + * dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_unit` + * and ratio<1>. + * + * @tparam Es zero or more exponents of a derived dimension + */ +template +struct unknown_dimension : derived_dimension, scaled_unit>, Es...> { + using coherent_unit = scaled_unit>; +}; + +namespace detail { + +template +struct downcast_dimension_impl { + using type = D; +}; + +// downcast did not find user predefined type +template +struct downcast_dimension_impl> { + using type = unknown_dimension; +}; + +} // namespace detail + +template +using downcast_dimension = detail::downcast_dimension_impl>::type; + +// dim_invert +namespace detail { + +template +struct dim_invert_impl; + +template +struct dim_invert_impl { + using type = downcast_dimension>>; +}; + +template +struct dim_invert_impl>> { + using type = D; +}; + +template +struct dim_invert_impl> { + using type = downcast_dimension...>>; +}; + +template +struct dim_invert_impl : dim_invert_impl> { +}; + +} // 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_dimension>, derived_dimension>>>; +}; + +template +struct dimension_multiply_impl { + using type = downcast_dimension>, typename D2::downcast_base_type>>; +}; + +template +struct dimension_multiply_impl { + using type = dimension_multiply_impl::type; +}; + +template +struct dimension_multiply_impl { + using type = downcast_dimension>; +}; + +} // 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_dimension...>>; +}; + +} // 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 = downcast_dimension>>; +}; + +template +struct dimension_pow_impl { + using type = D; +}; + +template +struct dimension_pow_impl>, N> { + using type = D; +}; + +template +struct dimension_pow_impl { + using type = dimension_pow_impl, N>; +}; + +template +struct dimension_pow_impl, N> { + using type = downcast_dimension...>>; +}; + +} // namespace detail + +template +using dimension_pow = detail::dimension_pow_impl::type; + +} // namespace units diff --git a/src/include/units/dimensions/capacitance.h b/src/include/units/dimensions/capacitance.h deleted file mode 100644 index 736fc9a6..00000000 --- a/src/include/units/dimensions/capacitance.h +++ /dev/null @@ -1,46 +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 - -namespace units { - - struct capacitance : derived_dimension, exp> {}; - - template - concept Capacitance = QuantityOf; - - struct farad : named_coherent_derived_unit {}; - - inline namespace literals { - - // F - constexpr auto operator""F(unsigned long long l) { return quantity(l); } - constexpr auto operator""_F(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/energy.h b/src/include/units/dimensions/energy.h deleted file mode 100644 index 709a1b14..00000000 --- a/src/include/units/dimensions/energy.h +++ /dev/null @@ -1,67 +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 - -namespace units { - - struct energy : derived_dimension, exp> {}; - - template - concept Energy = QuantityOf; - - struct joule : named_coherent_derived_unit {}; - struct millijoule : prefixed_derived_unit {}; - struct kilojoule : prefixed_derived_unit {}; - struct megajoule : prefixed_derived_unit {}; - struct gigajoule : prefixed_derived_unit {}; - - inline namespace literals { - - // J - constexpr auto operator""_J(unsigned long long l) { return quantity(l); } - constexpr auto operator""_J(long double l) { return quantity(l); } - - // mJ - constexpr auto operator""mJ(unsigned long long l) { return quantity(l); } - constexpr auto operator""mJ(long double l) { return quantity(l); } - - // kJ - constexpr auto operator""kJ(unsigned long long l) { return quantity(l); } - constexpr auto operator""kJ(long double l) { return quantity(l); } - - // MJ - constexpr auto operator""MJ(unsigned long long l) { return quantity(l); } - constexpr auto operator""MJ(long double l) { return quantity(l); } - - // GJ - constexpr auto operator""GJ(unsigned long long l) { return quantity(l); } - constexpr auto operator""GJ(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/force.h b/src/include/units/dimensions/force.h deleted file mode 100644 index 5ec11f3d..00000000 --- a/src/include/units/dimensions/force.h +++ /dev/null @@ -1,46 +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 - -namespace units { - - struct force : derived_dimension, exp> {}; - - template - concept Force = QuantityOf; - - struct newton : named_coherent_derived_unit {}; - - inline namespace literals { - - // N - constexpr auto operator""N(unsigned long long l) { return quantity(l); } - constexpr auto operator""N(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/mass.h b/src/include/units/dimensions/mass.h deleted file mode 100644 index d5da2587..00000000 --- a/src/include/units/dimensions/mass.h +++ /dev/null @@ -1,50 +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 { - - struct mass : derived_dimension> {}; - - template - concept Mass = QuantityOf; - - struct gram : named_scaled_derived_unit, si_prefix> {}; - struct kilogram : prefixed_derived_unit {}; - - inline namespace literals { - - // g - constexpr auto operator""g(unsigned long long l) { return quantity(l); } - constexpr auto operator""g(long double l) { return quantity(l); } - - // kg - constexpr auto operator""kg(unsigned long long l) { return quantity(l); } - constexpr auto operator""kg(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/physical.h b/src/include/units/dimensions/physical.h deleted file mode 100644 index d92228a1..00000000 --- a/src/include/units/dimensions/physical.h +++ /dev/null @@ -1,124 +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 { - - // base dimension - - template - struct base_dimension { - static constexpr auto name = Name; - }; - - template - concept BaseDimension = true; - - // base dimension - - template> - struct base_unit { - static constexpr auto symbol = Symbol; - using dimension = Dim; - using prefix_type = PT; - using ratio = R; - }; - - template - concept BaseUnit = true; - - template - concept BaseUnitOf = BaseUnit && BaseDimension && std::same_as; - - - - namespace physical { - - // base dimensions - struct base_dim_length : base_dimension<"length"> {}; - struct base_dim_mass : base_dimension<"mass"> {}; - struct base_dim_time : base_dimension<"time"> {}; - struct base_dim_current : base_dimension<"current"> {}; - struct base_dim_temperature : base_dimension<"temperature"> {}; - struct base_dim_substance : base_dimension<"substance"> {}; - struct base_dim_luminous_intensity : base_dimension<"luminous intensity"> {}; - - - // dimensions - template L> - struct length : derived_dimension> {}; - - template M> - struct mass : derived_dimension> {}; - - template T> - struct time : derived_dimension> {}; - - template L, UnitOf T> - struct velocity : derived_dimension, exp> {}; - - template L, UnitOf T> - struct acceleration : derived_dimension, exp> {}; - - template M, UnitOf A> - struct force : derived_dimension, exp> {}; - - } // physical - - // SI - namespace si { - struct si_prefix; - - // length - struct metre : base_unit<"m", base_dim_length, si_prefix> {}; - struct length : physical::length {}; - - // mass - struct kilogram : base_unit<"kg", base_dim_mass, si_prefix> {}; - struct mass : physical::mass {}; - - // time - struct second : base_unit<"s", base_dim_time, si_prefix> {}; - struct time : physical::time {}; - - struct nanosecond : prefixed_derived_unit {}; - struct microsecond : prefixed_derived_unit {}; - struct millisecond : prefixed_derived_unit {}; - struct minute : named_derived_unit> {}; - struct hour : named_derived_unit> {}; - - // velocity - struct velocity : physical::velocity; - - // acceleration - struct acceleration : physical::acceleration; - - // acceleration - struct acceleration : physical::acceleration; - - } - -} // namespace units diff --git a/src/include/units/dimensions/power.h b/src/include/units/dimensions/power.h deleted file mode 100644 index afffb2d2..00000000 --- a/src/include/units/dimensions/power.h +++ /dev/null @@ -1,66 +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 - -namespace units { - - struct power : derived_dimension, exp> {}; - - template - concept Power = QuantityOf; - - struct watt : named_coherent_derived_unit {}; - struct milliwatt : prefixed_derived_unit {}; - struct kilowatt : prefixed_derived_unit {}; - struct megawatt : prefixed_derived_unit {}; - struct gigawatt : prefixed_derived_unit {}; - - inline namespace literals { - - // W - constexpr auto operator""W(unsigned long long l) { return quantity(l); } - constexpr auto operator""_W(long double l) { return quantity(l); } - - // mW - constexpr auto operator""mW(unsigned long long l) { return quantity(l); } - constexpr auto operator""mW(long double l) { return quantity(l); } - - // kW - constexpr auto operator""kW(unsigned long long l) { return quantity(l); } - constexpr auto operator""kW(long double l) { return quantity(l); } - - // MW - constexpr auto operator""MW(unsigned long long l) { return quantity(l); } - constexpr auto operator""MW(long double l) { return quantity(l); } - - // GW - constexpr auto operator""GW(unsigned long long l) { return quantity(l); } - constexpr auto operator""GW(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/pressure.h b/src/include/units/dimensions/pressure.h deleted file mode 100644 index 4302ed26..00000000 --- a/src/include/units/dimensions/pressure.h +++ /dev/null @@ -1,46 +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 - -namespace units { - - struct pressure : derived_dimension, exp> {}; - - template - concept Pressure = QuantityOf; - - struct pascal : named_coherent_derived_unit {}; - - inline namespace literals { - - // Pa - constexpr auto operator""Pa(unsigned long long l) { return quantity(l); } - constexpr auto operator""Pa(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/surface_tension.h b/src/include/units/dimensions/surface_tension.h deleted file mode 100644 index 8a106301..00000000 --- a/src/include/units/dimensions/surface_tension.h +++ /dev/null @@ -1,45 +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 { - - struct surface_tension : derived_dimension, exp> {}; - - template - concept SurfaceTension = QuantityOf; - - struct newton_per_metre : coherent_derived_unit {}; - - inline namespace literals { - - // Nm - constexpr auto operator""Npm(unsigned long long l) { return quantity(l); } - constexpr auto operator""Npm(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/voltage.h b/src/include/units/dimensions/voltage.h deleted file mode 100644 index f862a873..00000000 --- a/src/include/units/dimensions/voltage.h +++ /dev/null @@ -1,48 +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 { - - struct voltage : derived_dimension, exp> {}; - - template - concept Voltage = QuantityOf; - - struct volt : named_coherent_derived_unit {}; - - inline namespace literals { - - // V - constexpr auto operator""V(unsigned long long l) { return quantity(l); } - constexpr auto operator""V(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/dimensions/volume.h b/src/include/units/dimensions/volume.h deleted file mode 100644 index 9cf72b53..00000000 --- a/src/include/units/dimensions/volume.h +++ /dev/null @@ -1,64 +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 - -namespace units { - - struct volume : derived_dimension> {}; - - template - concept Volume = QuantityOf; - - 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_foot : deduced_derived_unit {}; - - inline namespace literals { - - // cub_mm - constexpr auto operator""cub_mm(unsigned long long l) { return quantity(l); } - constexpr auto operator""cub_mm(long double l) { return quantity(l); } - - // cub_cm - constexpr auto operator""cub_cm(unsigned long long l) { return quantity(l); } - constexpr auto operator""cub_cm(long double l) { return quantity(l); } - - // cub_m - constexpr auto operator""cub_m(unsigned long long l) { return quantity(l); } - constexpr auto operator""cub_m(long double l) { return quantity(l); } - - // cub_km - constexpr auto operator""cub_km(unsigned long long l) { return quantity(l); } - constexpr auto operator""cub_km(long double l) { return quantity(l); } - - // cub_ft - constexpr auto operator""cub_ft(unsigned long long l) { return quantity(l); } - constexpr auto operator""cub_ft(long double l) { return quantity(l); } - - } // namespace literals - -} // namespace units diff --git a/src/include/units/math.h b/src/include/units/math.h index c6bb2bc4..4632b693 100644 --- a/src/include/units/math.h +++ b/src/include/units/math.h @@ -39,7 +39,7 @@ namespace units { { using dim = dimension_pow; using ratio = ratio_pow; - using unit = detail::unit_for_dimension; + using unit = downcast_unit; return quantity(static_cast(std::pow(q.count(), N))); } @@ -48,7 +48,7 @@ namespace units { { using dim = dimension_sqrt; using ratio = ratio_sqrt; - using unit = detail::unit_for_dimension; + using unit = downcast_unit; return quantity(static_cast(std::sqrt(q.count()))); } diff --git a/src/include/units/physical/dimensions.h b/src/include/units/physical/dimensions.h index 26959f89..89a18f53 100644 --- a/src/include/units/physical/dimensions.h +++ b/src/include/units/physical/dimensions.h @@ -104,6 +104,13 @@ struct dim_area : derived_dimension> {}; template concept Area = QuantityOf; +// volume +template L> +struct dim_volume : derived_dimension> {}; + +template +concept Volume = QuantityOf; + // velocity template L, DimensionOf T> struct dim_velocity : derived_dimension, exp> {}; @@ -139,6 +146,34 @@ struct dim_power : derived_dimension, exp> {}; template concept Power = QuantityOf; +// voltage +template P, DimensionOf C> +struct dim_voltage : derived_dimension, exp> {}; + +template +concept Voltage = QuantityOf; + +// electric charge +template T, DimensionOf C> +struct dim_electric_charge : derived_dimension, exp> {}; + +template +concept ElectricCharge = QuantityOf; + +// capacitance +template C, DimensionOf V> +struct dim_capacitance : derived_dimension, exp> {}; + +template +concept Capacitance = QuantityOf; + +// surface tension +template F, DimensionOf L> +struct dim_surface_tension : derived_dimension, exp> {}; + +template +concept SurfaceTension = QuantityOf; + // pressure template F, DimensionOf A> struct dim_pressure : derived_dimension, exp> {}; diff --git a/src/include/units/physical/si/acceleration.h b/src/include/units/physical/si/acceleration.h index 9a31f537..b7d34653 100644 --- a/src/include/units/physical/si/acceleration.h +++ b/src/include/units/physical/si/acceleration.h @@ -24,6 +24,7 @@ #include #include +#include namespace units::si { diff --git a/src/include/units/physical/si/area.h b/src/include/units/physical/si/area.h index 52a915ed..4a53594d 100644 --- a/src/include/units/physical/si/area.h +++ b/src/include/units/physical/si/area.h @@ -24,6 +24,7 @@ #include #include +#include namespace units::si { diff --git a/src/include/units/physical/si/capacitance.h b/src/include/units/physical/si/capacitance.h new file mode 100644 index 00000000..eb71780f --- /dev/null +++ b/src/include/units/physical/si/capacitance.h @@ -0,0 +1,48 @@ +// 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::si { + +struct farad : named_unit {}; + +struct dim_capacitance : physical::dim_capacitance {}; + +template +using capacitance = quantity; + +inline namespace literals { + +// F +constexpr auto operator""F(unsigned long long l) { return capacitance(l); } +constexpr auto operator""_F(long double l) { return capacitance(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/dimensions/current.h b/src/include/units/physical/si/current.h similarity index 68% rename from src/include/units/dimensions/current.h rename to src/include/units/physical/si/current.h index 072f9a8e..57580451 100644 --- a/src/include/units/dimensions/current.h +++ b/src/include/units/physical/si/current.h @@ -22,24 +22,25 @@ #pragma once -#include +#include +#include #include -namespace units { +namespace units::si { - struct current : derived_dimension> {}; +struct ampere : named_unit {}; - template - concept Current = QuantityOf; +struct dim_current : physical::dim_current {}; - struct ampere : named_coherent_derived_unit {}; +template +using current = quantity; - inline namespace literals { +inline namespace literals { - // A - constexpr auto operator""A(unsigned long long l) { return quantity(l); } - constexpr auto operator""A(long double l) { return quantity(l); } +// A +constexpr auto operator""A(unsigned long long l) { return current(l); } +constexpr auto operator""A(long double l) { return current(l); } - } +} // namespace literals -} // namespace units +} // namespace units::si diff --git a/src/include/units/physical/si/electric_charge.h b/src/include/units/physical/si/electric_charge.h new file mode 100644 index 00000000..3b3c5de0 --- /dev/null +++ b/src/include/units/physical/si/electric_charge.h @@ -0,0 +1,47 @@ +// 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::si { + +struct coulomb : named_unit {}; + +struct dim_electric_charge : physical::dim_electric_charge {}; + +template +using electric_charge = quantity; + +inline namespace literals { + +// C +constexpr auto operator""C(unsigned long long l) { return electric_charge(l); } +constexpr auto operator""C(long double l) { return electric_charge(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/energy.h b/src/include/units/physical/si/energy.h new file mode 100644 index 00000000..719d4539 --- /dev/null +++ b/src/include/units/physical/si/energy.h @@ -0,0 +1,67 @@ +// 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::si { + +struct joule : named_unit {}; +struct millijoule : prefixed_unit {}; +struct kilojoule : prefixed_unit {}; +struct megajoule : prefixed_unit {}; +struct gigajoule : prefixed_unit {}; + +struct dim_energy : physical::dim_energy {}; + +template +using energy = quantity; + +inline namespace literals { + +// J +constexpr auto operator""_J(unsigned long long l) { return energy(l); } +constexpr auto operator""_J(long double l) { return energy(l); } + +// mJ +constexpr auto operator""mJ(unsigned long long l) { return energy(l); } +constexpr auto operator""mJ(long double l) { return energy(l); } + +// kJ +constexpr auto operator""kJ(unsigned long long l) { return energy(l); } +constexpr auto operator""kJ(long double l) { return energy(l); } + +// MJ +constexpr auto operator""MJ(unsigned long long l) { return energy(l); } +constexpr auto operator""MJ(long double l) { return energy(l); } + +// GJ +constexpr auto operator""GJ(unsigned long long l) { return energy(l); } +constexpr auto operator""GJ(long double l) { return energy(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/force.h b/src/include/units/physical/si/force.h new file mode 100644 index 00000000..e3f7cf19 --- /dev/null +++ b/src/include/units/physical/si/force.h @@ -0,0 +1,48 @@ +// 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::si { + +struct newton : named_unit {}; + +struct dim_force : physical::dim_force {}; + +template +using force = quantity; + +inline namespace literals { + +// N +constexpr auto operator""N(unsigned long long l) { return force(l); } +constexpr auto operator""N(long double l) { return force(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/frequency.h b/src/include/units/physical/si/frequency.h index 902641d2..be896849 100644 --- a/src/include/units/physical/si/frequency.h +++ b/src/include/units/physical/si/frequency.h @@ -24,6 +24,7 @@ #include #include +#include namespace units::si { diff --git a/src/include/units/physical/si/length.h b/src/include/units/physical/si/length.h index 5143ba10..6bf17294 100644 --- a/src/include/units/physical/si/length.h +++ b/src/include/units/physical/si/length.h @@ -24,6 +24,7 @@ #include #include +#include namespace units::si { diff --git a/src/include/units/dimensions/luminous_intensity.h b/src/include/units/physical/si/luminous_intensity.h similarity index 64% rename from src/include/units/dimensions/luminous_intensity.h rename to src/include/units/physical/si/luminous_intensity.h index 736da752..d3bcd5e2 100644 --- a/src/include/units/dimensions/luminous_intensity.h +++ b/src/include/units/physical/si/luminous_intensity.h @@ -22,24 +22,25 @@ #pragma once -#include +#include +#include #include -namespace units { +namespace units::si { - struct luminous_intensity : derived_dimension> {}; +struct candela : named_unit {}; - template - concept LuminousIntensity = QuantityOf; +struct dim_luminous_intensity : physical::dim_luminous_intensity {}; - struct candela : named_coherent_derived_unit {}; +template +using luminous_intensity = quantity; - inline namespace literals { +inline namespace literals { - // cd - constexpr auto operator""cd(unsigned long long l) { return quantity(l); } - constexpr auto operator""cd(long double l) { return quantity(l); } +// cd +constexpr auto operator""cd(unsigned long long l) { return luminous_intensity(l); } +constexpr auto operator""cd(long double l) { return luminous_intensity(l); } - } // namespace literals +} // namespace literals -} // namespace units +} // namespace units::si diff --git a/src/include/units/dimensions/electric_charge.h b/src/include/units/physical/si/mass.h similarity index 59% rename from src/include/units/dimensions/electric_charge.h rename to src/include/units/physical/si/mass.h index 31cd410f..cd3fc4e8 100644 --- a/src/include/units/dimensions/electric_charge.h +++ b/src/include/units/physical/si/mass.h @@ -22,25 +22,29 @@ #pragma once -#include -#include -#include +#include +#include -namespace units { +namespace units::si { - struct electric_charge : derived_dimension, exp> {}; +struct gram : named_unit {}; +struct kilogram : prefixed_unit {}; - template - concept ElectricCharge = QuantityOf; +struct dim_mass : physical::dim_mass {}; - struct coulomb : named_coherent_derived_unit {}; +template +using mass = quantity; - inline namespace literals { +inline namespace literals { - // C - constexpr auto operator""C(unsigned long long l) { return quantity(l); } - constexpr auto operator""C(long double l) { return quantity(l); } + // g + constexpr auto operator""g(unsigned long long l) { return mass(l); } + constexpr auto operator""g(long double l) { return mass(l); } - } // namespace literals + // kg + constexpr auto operator""kg(unsigned long long l) { return mass(l); } + constexpr auto operator""kg(long double l) { return mass(l); } -} // namespace units +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/power.h b/src/include/units/physical/si/power.h new file mode 100644 index 00000000..072ede21 --- /dev/null +++ b/src/include/units/physical/si/power.h @@ -0,0 +1,67 @@ +// 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::si { + +struct watt : named_unit {}; +struct milliwatt : prefixed_unit {}; +struct kilowatt : prefixed_unit {}; +struct megawatt : prefixed_unit {}; +struct gigawatt : prefixed_unit {}; + +struct dim_power : physical::dim_power {}; + +template +using power = quantity; + +inline namespace literals { + +// W +constexpr auto operator""W(unsigned long long l) { return power(l); } +constexpr auto operator""_W(long double l) { return power(l); } + +// mW +constexpr auto operator""mW(unsigned long long l) { return power(l); } +constexpr auto operator""mW(long double l) { return power(l); } + +// kW +constexpr auto operator""kW(unsigned long long l) { return power(l); } +constexpr auto operator""kW(long double l) { return power(l); } + +// MW +constexpr auto operator""MW(unsigned long long l) { return power(l); } +constexpr auto operator""MW(long double l) { return power(l); } + +// GW +constexpr auto operator""GW(unsigned long long l) { return power(l); } +constexpr auto operator""GW(long double l) { return power(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/pressure.h b/src/include/units/physical/si/pressure.h new file mode 100644 index 00000000..a4256305 --- /dev/null +++ b/src/include/units/physical/si/pressure.h @@ -0,0 +1,48 @@ +// 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::si { + +struct pascal : named_unit {}; + +struct dim_pressure : physical::dim_pressure {}; + +template +using pressure = quantity; + +inline namespace literals { + +// Pa +constexpr auto operator""Pa(unsigned long long l) { return pressure(l); } +constexpr auto operator""Pa(long double l) { return pressure(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/dimensions/substance.h b/src/include/units/physical/si/substance.h similarity index 67% rename from src/include/units/dimensions/substance.h rename to src/include/units/physical/si/substance.h index 54b15fa4..285fc33f 100644 --- a/src/include/units/dimensions/substance.h +++ b/src/include/units/physical/si/substance.h @@ -22,24 +22,25 @@ #pragma once -#include +#include +#include #include -namespace units { +namespace units::si { - struct substance : derived_dimension> {}; +struct mole : named_unit {}; - template - concept Substance = QuantityOf; +struct dim_substance : physical::dim_substance {}; - struct mole : named_coherent_derived_unit {}; +template +using substance = quantity; - inline namespace literals { +inline namespace literals { - // mol - constexpr auto operator""mol(unsigned long long l) { return quantity(l); } - constexpr auto operator""mol(long double l) { return quantity(l); } +// mol +constexpr auto operator"" mol(unsigned long long l) { return substance(l); } +constexpr auto operator"" mol(long double l) { return substance(l); } - } // namespace literals +} // namespace literals -} // namespace units +} // namespace units::si diff --git a/src/include/units/physical/si/surface_tension.h b/src/include/units/physical/si/surface_tension.h new file mode 100644 index 00000000..3e4b01b2 --- /dev/null +++ b/src/include/units/physical/si/surface_tension.h @@ -0,0 +1,46 @@ +// 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 + +namespace units::si { + +struct newton_per_metre : unit {}; + +struct dim_surface_tension : physical::dim_surface_tension {}; + +template +using surface_tension = quantity; + +inline namespace literals { + + // Nm + constexpr auto operator""Npm(unsigned long long l) { return surface_tension(l); } + constexpr auto operator""Npm(long double l) { return surface_tension(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/dimensions/base_dimensions.h b/src/include/units/physical/si/temperature.h similarity index 65% rename from src/include/units/dimensions/base_dimensions.h rename to src/include/units/physical/si/temperature.h index ef2ea824..27e6ab33 100644 --- a/src/include/units/dimensions/base_dimensions.h +++ b/src/include/units/physical/si/temperature.h @@ -22,16 +22,24 @@ #pragma once -#include +#include +#include -namespace units { +namespace units::si { - 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"> {}; +struct kelvin : named_unit {}; -} // namespace units +struct dim_temperature : physical::dim_temperature {}; + +template +using temperature = quantity; + +inline namespace literals { + +// K +constexpr auto operator""K(unsigned long long l) { return temperature(l); } +constexpr auto operator""_K(long double l) { return temperature(l); } // TODO: conflicts with gcc GNU extension + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/physical/si/time.h b/src/include/units/physical/si/time.h index 56534eba..9bd60783 100644 --- a/src/include/units/physical/si/time.h +++ b/src/include/units/physical/si/time.h @@ -24,6 +24,7 @@ #include #include +#include namespace units::si { diff --git a/src/include/units/physical/si/velocity.h b/src/include/units/physical/si/velocity.h index bb551919..bcd168bd 100644 --- a/src/include/units/physical/si/velocity.h +++ b/src/include/units/physical/si/velocity.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace units::si { diff --git a/src/include/units/dimensions/temperature.h b/src/include/units/physical/si/voltage.h similarity index 64% rename from src/include/units/dimensions/temperature.h rename to src/include/units/physical/si/voltage.h index 5a26a56a..5e729032 100644 --- a/src/include/units/dimensions/temperature.h +++ b/src/include/units/physical/si/voltage.h @@ -22,24 +22,27 @@ #pragma once -#include +#include +#include +#include +#include #include -namespace units { +namespace units::si { - struct temperature : derived_dimension> {}; +struct volt : named_unit {}; - template - concept ThermodynamicTemperature = QuantityOf; +struct dim_voltage : physical::dim_voltage {}; - struct kelvin : named_coherent_derived_unit {}; +template +using voltage = quantity; - inline namespace literals { +inline namespace literals { - // K - constexpr auto operator""K(unsigned long long l) { return quantity(l); } - constexpr auto operator""_K(long double l) { return quantity(l); } // TODO: conflicts with gcc GNU extension +// V +constexpr auto operator""V(unsigned long long l) { return voltage(l); } +constexpr auto operator""V(long double l) { return voltage(l); } - } // namespace literals +} // namespace literals -} // namespace units +} // namespace units::si diff --git a/src/include/units/physical/si/volume.h b/src/include/units/physical/si/volume.h new file mode 100644 index 00000000..be80331a --- /dev/null +++ b/src/include/units/physical/si/volume.h @@ -0,0 +1,66 @@ +// 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 + +namespace units::si { + +struct cubic_metre : unit {}; +struct dim_volume : physical::dim_volume {}; + +struct cubic_millimetre : deduced_unit {}; +struct cubic_centimetre : deduced_unit {}; +struct cubic_kilometre : deduced_unit {}; +struct cubic_foot : deduced_unit {}; + +template +using volume = quantity; + +inline namespace literals { + +// cub_mm +constexpr auto operator""cub_mm(unsigned long long l) { return volume(l); } +constexpr auto operator""cub_mm(long double l) { return volume(l); } + +// cub_cm +constexpr auto operator""cub_cm(unsigned long long l) { return volume(l); } +constexpr auto operator""cub_cm(long double l) { return volume(l); } + +// cub_m +constexpr auto operator""cub_m(unsigned long long l) { return volume(l); } +constexpr auto operator""cub_m(long double l) { return volume(l); } + +// cub_km +constexpr auto operator""cub_km(unsigned long long l) { return volume(l); } +constexpr auto operator""cub_km(long double l) { return volume(l); } + +// cub_ft +constexpr auto operator""cub_ft(unsigned long long l) { return volume(l); } +constexpr auto operator""cub_ft(long double l) { return volume(l); } + +} // namespace literals + +} // namespace units::si diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index b9d64bf5..340e6801 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -23,7 +23,7 @@ #pragma once #include -#include +#include #include #include @@ -73,9 +73,7 @@ struct common_quantity_impl, quantity, Rep> { template struct common_quantity_impl, quantity, Rep> { - using type = quantity< - D, downcast>>, - Rep>; + using type = quantity>, Rep>; }; } // namespace detail @@ -154,7 +152,7 @@ template { using c_ratio = ratio_divide; using c_rep = std::common_type_t; - using ret_unit = downcast>; + using ret_unit = downcast_unit; using ret = quantity; using cast = detail::quantity_cast_impl; return cast::cast(q); @@ -465,8 +463,10 @@ template { using dim = dimension_multiply; - using ratio = ratio_multiply; - using unit = detail::unit_for_dimension; + using ratio1 = ratio_divide; + using ratio2 = ratio_divide; + using ratio = ratio_multiply; + using unit = downcast_unit; using common_rep = decltype(lhs.count() * rhs.count()); using ret = quantity; return ret(lhs.count() * rhs.count()); @@ -480,7 +480,7 @@ template using dim = dim_invert; using ratio = ratio; - using unit = detail::unit_for_dimension; + using unit = downcast_unit; using common_rep = decltype(v / q.count()); using ret = quantity; return ret(v / q.count()); @@ -518,8 +518,10 @@ template; - using ratio = ratio_divide; - using unit = detail::unit_for_dimension; + using ratio1 = ratio_divide; + using ratio2 = ratio_divide; + using ratio = ratio_divide; + using unit = downcast_unit; using ret = quantity; return ret(lhs.count() / rhs.count()); } diff --git a/src/include/units/unit.h b/src/include/units/unit.h index 6f7342ca..20618c74 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -32,86 +32,35 @@ namespace units { -// scaled_unit +/** + * @brief A common point for a 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. + * + * All units of the same dimension can be convereted between each other. To allow this all of + * them are expressed as different ratios of the same one proprietary chosen reference unit + * (i.e. all length units are expressed in terms of meter, all mass units are expressed in + * terms of gram, ...) + * + * @tparam U a unit to use as a reference for this dimension + * @tparam R a ratio of a reference unit + */ template struct scaled_unit : downcast_base> { using reference = U; using ratio = R; }; -// UnitOf -namespace detail { - -template -concept SameReference = std::same_as; - -} - -template -concept UnitOf = - Unit && - Dimension && - (std::same_as || detail::SameReference); - -namespace detail { - -// same_scaled_units -template -inline constexpr bool same_scaled_units = false; - -template -inline constexpr bool same_scaled_units, Us...> = (UnitOf && ...); - -// deduced_unit -template -struct ratio_op; - -template -struct ratio_op { - using ratio = Result; -}; - -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 derived_ratio; - -template -struct derived_ratio, Us...> { - using ratio = ::units::ratio<1>; -}; - -template -struct derived_ratio, U, URest...> { - using rest_ratio = derived_ratio, URest...>::ratio; - using ratio = ratio_op::ratio; -}; - -template -using deduced_unit = - scaled_unit::ratio>; - -} // namespace detail +template +using downcast_unit = downcast>; /** * @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. + * Defines 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) */ @@ -121,6 +70,13 @@ struct unit : downcast_child>> { using prefix_type = no_prefix; }; +/** + * @brief Unknown unit + * + * Used as a reference unit of an unknown dimension. + */ +struct unknown_unit : unit {}; + /** * @brief A named unit * @@ -185,6 +141,59 @@ struct prefixed_unit : using prefix_type = P::prefix_type; }; +// UnitOf +template +concept UnitOf = + Unit && + Dimension && + std::same_as; + +namespace detail { + +// same_scaled_units +template +inline constexpr bool same_scaled_units = false; + +template +inline constexpr bool same_scaled_units, Us...> = (UnitOf && ...); + +// deduced_unit +template +struct ratio_op; + +template +struct ratio_op { + using ratio = Result; +}; + +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 derived_ratio; + +template +struct derived_ratio, Us...> { + using ratio = ::units::ratio<1>; +}; + +template +struct derived_ratio, U, URest...> { + using rest_ratio = derived_ratio, URest...>::ratio; + using ratio = ratio_op::ratio; +}; + +template +using deduced_unit = + scaled_unit::ratio>; + +} // namespace detail + /** * @brief A unit with a deduced ratio and symbol * @@ -202,7 +211,7 @@ template (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(); + static constexpr auto symbol = basic_fixed_string{""}; // detail::deduced_symbol_text(); // TODO implement this using prefix_type = no_prefix; }; diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index 0babe95d..b448503c 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -23,13 +23,12 @@ add_library(unit_tests_static # cgs_test.cpp # custom_unit_test.cpp - dimension_test.cpp + dimension_op_test.cpp # fixed_string_test.cpp math_test.cpp -# new_design.cpp quantity_test.cpp ratio_test.cpp -# si_test.cpp + si_test.cpp type_list_test.cpp unit_test.cpp ) diff --git a/test/unit_test/static/dimension_test.cpp b/test/unit_test/static/dimension_op_test.cpp similarity index 87% rename from test/unit_test/static/dimension_test.cpp rename to test/unit_test/static/dimension_op_test.cpp index 94cf5ea5..b9d199c3 100644 --- a/test/unit_test/static/dimension_test.cpp +++ b/test/unit_test/static/dimension_op_test.cpp @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/derived_dimension.h" +#include "units/dimension_op.h" #include "units/unit.h" #include @@ -63,9 +63,9 @@ static_assert(std::is_same_v, exp, exp>, // dim_invert static_assert(std::is_same_v>>, d0>); -static_assert(std::is_same_v>>, derived_dimension>>); +static_assert(std::is_same_v>>, unknown_dimension>>); static_assert( - std::is_same_v, exp>>, derived_dimension, exp>>); + std::is_same_v, exp>>, unknown_dimension, exp>>); // make_dimension @@ -94,33 +94,33 @@ static_assert(std::is_same_v, exp, exp, // dimension_multiply static_assert(std::is_same_v>, derived_dimension>>, - derived_dimension, exp>>); + unknown_dimension, exp>>); static_assert( - std::is_same_v>, d1>, derived_dimension, exp>>); + std::is_same_v>, d1>, unknown_dimension, exp>>); static_assert( - std::is_same_v>>, derived_dimension, exp>>); -static_assert(std::is_same_v, derived_dimension, exp>>); + std::is_same_v>>, unknown_dimension, exp>>); +static_assert(std::is_same_v, unknown_dimension, exp>>); static_assert(std::is_same_v< dimension_multiply, exp, exp>, derived_dimension>>, - derived_dimension, exp, exp, exp>>); + unknown_dimension, exp, exp, exp>>); static_assert(std::is_same_v< dimension_multiply, exp, exp>, derived_dimension>>, - derived_dimension, exp, exp>>); + unknown_dimension, exp, exp>>); static_assert(std::is_same_v< dimension_multiply, exp, exp>, derived_dimension>>, - derived_dimension, exp>>); + unknown_dimension, exp>>); static_assert(std::is_same_v>, derived_dimension>>, - derived_dimension<>>); -static_assert(std::is_same_v>, derived_dimension>>, d0>); + unknown_dimension<>>); +static_assert(std::is_same_v>, unknown_dimension>>, d0>); // dimension_divide static_assert(std::is_same_v>, derived_dimension>>, - derived_dimension, exp>>); + unknown_dimension, exp>>); static_assert(std::is_same_v>, derived_dimension>>, - derived_dimension<>>); + unknown_dimension<>>); static_assert(std::is_same_v>, derived_dimension>>, - derived_dimension<>>); -static_assert(std::is_same_v>, derived_dimension>>, d0>); + unknown_dimension<>>); +static_assert(std::is_same_v>, unknown_dimension>>, d0>); } // namespace diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp new file mode 100644 index 00000000..c31d5886 --- /dev/null +++ b/test/unit_test/static/si_test.cpp @@ -0,0 +1,193 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + + using namespace units; + using namespace units::si; + + /* ************** BASE DIMENSIONS **************** */ + + // length + + 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); + + static_assert(1yd == 0.9144m); + static_assert(1yd == 3ft); + static_assert(1ft == 12in); + static_assert(1mi == 1760yd); + + 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(2 / 1Hz == 2s); + + 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); + +} // namespace diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index 58eb8e9c..a23bee55 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -38,6 +38,9 @@ struct second : named_unit {}; struct hour : named_scaled_unit, second> {}; struct dim_time : base_dimension<"time", second> {}; +struct kelvin : named_unit {}; +// struct kilokelvin : prefixed_unit {}; // should not compile (prefix not allowed for this reference unit) + struct metre_per_second : unit {}; struct dim_velocity : derived_dimension, exp> {}; struct kilometre_per_hour : deduced_unit {}; From 2c29af9670fb6a28cc96b5b1fbddbb35f64a4bf1 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 7 Dec 2019 18:33:55 +0100 Subject: [PATCH 09/44] Custom unit and data tests enabled --- src/include/units/data/bitrate.h | 59 +++++++++ src/include/units/data/information.h | 74 +++++++++++ src/include/units/data/prefixes.h | 38 ++++++ src/include/units/dimension_op.h | 2 +- src/include/units/physical/dimensions.h | 142 +++++++++------------ test/unit_test/static/CMakeLists.txt | 3 +- test/unit_test/static/custom_unit_test.cpp | 69 +++------- test/unit_test/static/data_test.cpp | 48 +++++++ 8 files changed, 301 insertions(+), 134 deletions(-) create mode 100644 src/include/units/data/bitrate.h create mode 100644 src/include/units/data/information.h create mode 100644 src/include/units/data/prefixes.h create mode 100644 test/unit_test/static/data_test.cpp diff --git a/src/include/units/data/bitrate.h b/src/include/units/data/bitrate.h new file mode 100644 index 00000000..711e0fff --- /dev/null +++ b/src/include/units/data/bitrate.h @@ -0,0 +1,59 @@ +// 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::data { + +struct bit_per_second : unit {}; +struct dim_bitrate : derived_dimension, exp> {}; + +struct kibibit_per_second : deduced_unit {}; +struct mebibit_per_second : deduced_unit {}; +struct gibibit_per_second : deduced_unit {}; +struct tebibit_per_second : deduced_unit {}; +struct pebibit_per_second : deduced_unit {}; + +template +concept Bitrate = QuantityOf; + +template +using bitrate = quantity; + +inline namespace literals { + +// bits +constexpr auto operator""_bps(unsigned long long l) { return bitrate(l); } +constexpr auto operator""_Kibps(unsigned long long l) { return bitrate(l); } +constexpr auto operator""_Mibps(unsigned long long l) { return bitrate(l); } +constexpr auto operator""_Gibps(unsigned long long l) { return bitrate(l); } +constexpr auto operator""_Tibps(unsigned long long l) { return bitrate(l); } +constexpr auto operator""_Pibps(unsigned long long l) { return bitrate(l); } + +} // namespace literals + +} // namespace units::data diff --git a/src/include/units/data/information.h b/src/include/units/data/information.h new file mode 100644 index 00000000..4d28f47b --- /dev/null +++ b/src/include/units/data/information.h @@ -0,0 +1,74 @@ +// 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::data { + +struct bit : named_unit {}; +struct kibibit : prefixed_unit {}; +struct mebibit : prefixed_unit {}; +struct gibibit : prefixed_unit {}; +struct tebibit : prefixed_unit {}; +struct pebibit : prefixed_unit {}; + +struct byte : named_scaled_unit, bit> {}; +struct kibibyte : prefixed_unit {}; +struct mebibyte : prefixed_unit {}; +struct gibibyte : prefixed_unit {}; +struct tebibyte : prefixed_unit {}; +struct pebibyte : prefixed_unit {}; + +struct dim_information : base_dimension<"information", bit> {}; + +template +concept Information = QuantityOf; + +template +using information = quantity; + +inline namespace literals { + +// bits +constexpr auto operator""b(unsigned long long l) { return information(l); } +constexpr auto operator""Kib(unsigned long long l) { return information(l); } +constexpr auto operator""Mib(unsigned long long l) { return information(l); } +constexpr auto operator""Gib(unsigned long long l) { return information(l); } +constexpr auto operator""Tib(unsigned long long l) { return information(l); } +constexpr auto operator""Pib(unsigned long long l) { return information(l); } + +// bytes +constexpr auto operator""B(unsigned long long l) { return information(l); } +constexpr auto operator""KiB(unsigned long long l) { return information(l); } +constexpr auto operator""MiB(unsigned long long l) { return information(l); } +constexpr auto operator""GiB(unsigned long long l) { return information(l); } +constexpr auto operator""TiB(unsigned long long l) { return information(l); } +constexpr auto operator""PiB(unsigned long long l) { return information(l); } + +} // namespace literals + +} // namespace units::data diff --git a/src/include/units/data/prefixes.h b/src/include/units/data/prefixes.h new file mode 100644 index 00000000..827b39fa --- /dev/null +++ b/src/include/units/data/prefixes.h @@ -0,0 +1,38 @@ +// 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::data { + +struct prefix : prefix_type {}; + +struct kibi : units::prefix> {}; +struct mebi : units::prefix> {}; +struct gibi : units::prefix> {}; +struct tebi : units::prefix> {}; +struct pebi : units::prefix> {}; + +} // namespace units::si diff --git a/src/include/units/dimension_op.h b/src/include/units/dimension_op.h index 294de4b4..5a58c02f 100644 --- a/src/include/units/dimension_op.h +++ b/src/include/units/dimension_op.h @@ -199,7 +199,7 @@ struct dimension_pow_impl>, N> { template struct dimension_pow_impl { - using type = dimension_pow_impl, N>; + using type = dimension_pow_impl, N>::type; }; template diff --git a/src/include/units/physical/dimensions.h b/src/include/units/physical/dimensions.h index 89a18f53..86004125 100644 --- a/src/include/units/physical/dimensions.h +++ b/src/include/units/physical/dimensions.h @@ -29,7 +29,7 @@ namespace units { -inline namespace physical { +namespace physical { template typename DimTemplate> concept DimensionOf = (Dimension || BaseDimension) && is_derived_from_instantiation; @@ -39,148 +39,128 @@ concept QuantityOf = Quantity && is_derived_from_instantiation struct dim_length : base_dimension<"length", U> {}; -template -concept Length = QuantityOf; - -// mass template struct dim_mass : base_dimension<"mass", U> {}; -template -concept Mass = QuantityOf; - -// time template struct dim_time : base_dimension<"time", U> {}; -template -concept Time = QuantityOf; - -// current template struct dim_current : base_dimension<"current", U> {}; -template -concept Current = QuantityOf; - -// temperature template struct dim_temperature : base_dimension<"temperature", U> {}; -template -concept Temperature = QuantityOf; - -// substance template struct dim_substance : base_dimension<"substance", U> {}; -template -concept Substance = QuantityOf; - -// luminous intensity template struct dim_luminous_intensity : base_dimension<"luminous intensity", U> {}; -template -concept LuminousIntensity = QuantityOf; - // ------------------------ derived dimensions ----------------------------- -// frequency template T> struct dim_frequency : derived_dimension> {}; -template -concept Frequency = QuantityOf; - -// area template L> struct dim_area : derived_dimension> {}; -template -concept Area = QuantityOf; - -// volume template L> struct dim_volume : derived_dimension> {}; -template -concept Volume = QuantityOf; - -// velocity template L, DimensionOf T> struct dim_velocity : derived_dimension, exp> {}; -template -concept Velocity = QuantityOf; - -// acceleration template L, DimensionOf T> struct dim_acceleration : derived_dimension, exp> {}; -template -concept Acceleration = QuantityOf; - -// force template M, DimensionOf A> struct dim_force : derived_dimension, exp> {}; -template -concept Force = QuantityOf; - -// energy template F, DimensionOf L> struct dim_energy : derived_dimension, exp> {}; -template -concept Energy = QuantityOf; - -// power template E, DimensionOf T> struct dim_power : derived_dimension, exp> {}; -template -concept Power = QuantityOf; - -// voltage template P, DimensionOf C> struct dim_voltage : derived_dimension, exp> {}; -template -concept Voltage = QuantityOf; - -// electric charge template T, DimensionOf C> struct dim_electric_charge : derived_dimension, exp> {}; -template -concept ElectricCharge = QuantityOf; - -// capacitance template C, DimensionOf V> struct dim_capacitance : derived_dimension, exp> {}; -template -concept Capacitance = QuantityOf; - -// surface tension template F, DimensionOf L> struct dim_surface_tension : derived_dimension, exp> {}; -template -concept SurfaceTension = QuantityOf; - -// pressure template F, DimensionOf A> struct dim_pressure : derived_dimension, exp> {}; -template -concept Pressure = QuantityOf; - } // namespace physical +template +concept Length = physical::QuantityOf; + +template +concept Mass = physical::QuantityOf; + +template +concept Time = physical::QuantityOf; + +template +concept Current = physical::QuantityOf; + +template +concept Temperature = physical::QuantityOf; + +template +concept Substance = physical::QuantityOf; + +template +concept LuminousIntensity = physical::QuantityOf; + +template +concept Frequency = physical::QuantityOf; + +template +concept Area = physical::QuantityOf; + +template +concept Volume = physical::QuantityOf; + +template +concept Velocity = physical::QuantityOf; + +template +concept Acceleration = physical::QuantityOf; + +template +concept Force = physical::QuantityOf; + +template +concept Energy = physical::QuantityOf; + +template +concept Power = physical::QuantityOf; + +template +concept Voltage = physical::QuantityOf; + +template +concept ElectricCharge = physical::QuantityOf; + +template +concept Capacitance = physical::QuantityOf; + +template +concept SurfaceTension = physical::QuantityOf; + +template +concept Pressure = physical::QuantityOf; + } // namespace units diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index b448503c..2447b832 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -22,7 +22,8 @@ add_library(unit_tests_static # cgs_test.cpp -# custom_unit_test.cpp + custom_unit_test.cpp + data_test.cpp dimension_op_test.cpp # fixed_string_test.cpp math_test.cpp diff --git a/test/unit_test/static/custom_unit_test.cpp b/test/unit_test/static/custom_unit_test.cpp index 2df947e8..54c0c056 100644 --- a/test/unit_test/static/custom_unit_test.cpp +++ b/test/unit_test/static/custom_unit_test.cpp @@ -20,70 +20,37 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include -#include +#include +#include #include -/* ************** DERIVED DIMENSIONS THAT INCLUDE UNITS WITH SPECIAL NAMES **************** */ - namespace { - struct base_dim_digital_information : units::base_dimension<"digital information", "b"> {}; +using namespace units; +using namespace units::si; - struct digital_information : units::derived_dimension> {}; +// power spectral density +struct sq_volt_per_hertz : unit {}; +struct dim_power_spectral_density : derived_dimension, units::exp> {}; - template - concept DigitalInformation = units::QuantityOf; +template +using power_spectral_density = quantity; - struct data_prefix : units::prefix_type {}; +// amplitude spectral density +struct volt_per_sqrt_hertz : unit {}; +struct dim_amplitude_spectral_density : derived_dimension, units::exp> {}; - struct kibi : units::prefix, "Ki"> {}; - - struct bit : units::named_coherent_derived_unit {}; - struct kilobit : units::prefixed_derived_unit {}; - - struct byte : units::named_scaled_derived_unit, data_prefix> {}; - struct kilobyte : units::prefixed_derived_unit {}; - - inline namespace literals { - - constexpr auto operator""_b(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_Kib(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_B(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_KiB(unsigned long long l) { return units::quantity(l); } - - } -} - -namespace { - - static_assert(1_B == 8_b); - static_assert(1024_b == 1_Kib); - static_assert(1024_B == 1_KiB); - static_assert(8 * 1024_b == 1_KiB); - static_assert(8 * 1_Kib == 1_KiB); +template +using amplitude_spectral_density = quantity; } namespace { - using namespace units; +static_assert(std::is_same_v, dim_amplitude_spectral_density>); +static_assert(std::is_same_v, dim_power_spectral_density>); - // power spectral density - struct power_spectral_density : derived_dimension, units::exp> {}; - struct sq_volt_per_hertz : coherent_derived_unit {}; - - // amplitude spectral density - struct amplitude_spectral_density : derived_dimension, units::exp> {}; - struct volt_per_sqrt_hertz : coherent_derived_unit {}; -} - -namespace { - - static_assert(std::is_same_v, amplitude_spectral_density>); - static_assert(std::is_same_v, power_spectral_density>); - - static_assert(std::is_same_v(quantity(4))), decltype(quantity(16))>); - static_assert(std::is_same_v(16))), decltype(quantity(4))>); +static_assert(std::is_same_v(amplitude_spectral_density(4))), decltype(power_spectral_density(16))>); +static_assert(std::is_same_v(16))), decltype(amplitude_spectral_density(4))>); } diff --git a/test/unit_test/static/data_test.cpp b/test/unit_test/static/data_test.cpp new file mode 100644 index 00000000..14b483b2 --- /dev/null +++ b/test/unit_test/static/data_test.cpp @@ -0,0 +1,48 @@ +// 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 +#include + +/* ************** DERIVED DIMENSIONS THAT INCLUDE UNITS WITH SPECIAL NAMES **************** */ + +namespace { + +using namespace units::data; + +// information + +static_assert(1B == 8b); +static_assert(1024b == 1Kib); +static_assert(1024B == 1KiB); +static_assert(8 * 1024b == 1KiB); +static_assert(8 * 1Kib == 1KiB); + +static_assert(1Kib == 1024b); +static_assert(1Mib == 1024Kib); +static_assert(1Gib == 1024Mib); +static_assert(1Tib == 1024Gib); +static_assert(1Pib == 1024Tib); + +// bitrate + +} From d465d47803c5cd4be51f62f378730c18b2f0a690 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sun, 8 Dec 2019 16:09:00 +0100 Subject: [PATCH 10/44] range-v3 dependency removed for gcc-10 --- conanfile.py | 9 +++++-- src/CMakeLists.txt | 6 ++++- src/include/units/bits/hacks.h | 26 ++++++++++++++++----- src/include/units/math.h | 4 ++-- test/unit_test/static/dimension_op_test.cpp | 2 +- 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/conanfile.py b/conanfile.py index 4463ad45..5a2467b4 100644 --- a/conanfile.py +++ b/conanfile.py @@ -47,10 +47,11 @@ class UnitsConan(ConanFile): exports_sources = ["src/*", "test/*", "cmake/*", "example/*","CMakeLists.txt"] settings = "os", "compiler", "build_type", "arch" requires = ( - "range-v3/0.9.1@ericniebler/stable", - "Catch2/2.10.0@catchorg/stable", "fmt/6.0.0" ) + build_requires = ( + "Catch2/2.10.0@catchorg/stable" + ) generators = "cmake" @property @@ -73,6 +74,10 @@ class UnitsConan(ConanFile): cmake.configure(source_folder="src", build_folder="src") return cmake + def requirements(self): + if Version(self.settings.compiler.version) < "10": + self.requires("range-v3/0.10.0@ericniebler/stable") + def build(self): cmake = self._configure_cmake() cmake.build() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 389aac19..a8d69659 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -53,7 +53,6 @@ add_library(units INTERFACE) target_compile_features(units INTERFACE cxx_std_20) target_link_libraries(units INTERFACE - CONAN_PKG::range-v3 CONAN_PKG::fmt ) target_include_directories(units @@ -71,6 +70,11 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) -Wno-pedantic ) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0) + target_link_libraries(units + INTERFACE + CONAN_PKG::range-v3 + ) + target_compile_options(units INTERFACE -fconcepts diff --git a/src/include/units/bits/hacks.h b/src/include/units/bits/hacks.h index da702863..c1bb861a 100644 --- a/src/include/units/bits/hacks.h +++ b/src/include/units/bits/hacks.h @@ -22,7 +22,6 @@ #pragma once -#include #include #ifdef NDEBUG @@ -32,16 +31,24 @@ #define Expects(cond) assert(cond); #endif -#if __GNUC__ > 9 -#define AUTO auto -#define SAME_AS(T) std::same_as -#else +#if __GNUC__ < 10 + +#include #define AUTO #define SAME_AS(T) T + +#else + +#include +#define AUTO auto +#define SAME_AS(T) std::same_as + #endif namespace std { +#if __GNUC__ < 10 + // concepts using concepts::common_reference_with; using concepts::common_with; @@ -64,4 +71,11 @@ namespace std { template concept regular_invocable = invocable; -} +#else + +template +concept default_constructible = constructible_from; + +#endif + +} // namespace std diff --git a/src/include/units/math.h b/src/include/units/math.h index 4632b693..b9782c3f 100644 --- a/src/include/units/math.h +++ b/src/include/units/math.h @@ -28,8 +28,8 @@ namespace units { template - requires N == 0 - inline Rep AUTO pow(const quantity&) noexcept + requires (N == 0) + inline Rep pow(const quantity&) noexcept { return 1; } diff --git a/test/unit_test/static/dimension_op_test.cpp b/test/unit_test/static/dimension_op_test.cpp index b9d199c3..6f94c6b5 100644 --- a/test/unit_test/static/dimension_op_test.cpp +++ b/test/unit_test/static/dimension_op_test.cpp @@ -111,7 +111,7 @@ static_assert(std::is_same_v< unknown_dimension, exp>>); static_assert(std::is_same_v>, derived_dimension>>, unknown_dimension<>>); -static_assert(std::is_same_v>, unknown_dimension>>, d0>); +static_assert(std::is_same_v>, derived_dimension>>, d0>); // dimension_divide From 6b4220100fd2a4ed7826468f3655ab317d9d0afa Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sun, 8 Dec 2019 16:24:29 +0100 Subject: [PATCH 11/44] quantity_cast refactored - new version with ToD added - version removed --- example/avg_velocity.cpp | 2 +- src/include/units/quantity.h | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/example/avg_velocity.cpp b/example/avg_velocity.cpp index 5c34003a..1b470eb3 100644 --- a/example/avg_velocity.cpp +++ b/example/avg_velocity.cpp @@ -123,7 +123,7 @@ void example() // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed // also it is not possible to make a lossless conversion of miles to meters on an integral type // (explicit cast needed) - print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast(distance), quantity_cast(duration))); + print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast>(distance), quantity_cast(duration))); print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); print_result(distance, duration, si_avg_speed(distance, duration)); diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 340e6801..673b9552 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -164,19 +164,17 @@ template * Implicit conversions between quantities of different types are allowed only for "safe" * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. * - * This cast gets both the target unit and representation to cast to. For example: + * This cast gets only the target dimension to cast to. For example: * - * auto q1 = units::quantity_cast(1ms); + * auto q1 = units::quantity_cast(200Gal); * - * @tparam ToU a unit type to use for a target quantity - * @tparam ToRep a representation type to use for a target quantity + * @tparam ToD a dimension type to use for a target quantity */ -template +template [[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires UnitOf && - detail::basic_arithmetic> + requires same_dim { - return quantity_cast>(q); + return quantity_cast>(q); } /** @@ -187,7 +185,7 @@ template * * This cast gets only the target unit to cast to. For example: * - * auto q1 = units::quantity_cast(1ms); + * auto q1 = units::quantity_cast(1ms); * * @tparam ToU a unit type to use for a target quantity */ From 7619a6f831eae9f2cc205e8c5b695ee285a9b452 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sun, 8 Dec 2019 21:57:06 +0100 Subject: [PATCH 12/44] gcc-10 crash fixed --- src/include/units/quantity.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 673b9552..19796b57 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -260,7 +260,7 @@ public: constexpr explicit quantity(const Value& v) : value_{static_cast(v)} {} template - requires same_dim && + requires same_dim && detail::safe_convertible && detail::safe_divisible constexpr quantity(const Q2& q) : value_{quantity_cast(q).count()} {} From 8ffc46ba750c5b6ba2228b650317f516825182f2 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Mon, 9 Dec 2019 17:19:58 +0100 Subject: [PATCH 13/44] CGS support added --- src/include/units/base_dimension.h | 18 ++- src/include/units/derived_dimension.h | 56 ++++++-- src/include/units/dimension_op.h | 57 +++++++- src/include/units/physical/cgs/acceleration.h | 45 +++++++ src/include/units/physical/cgs/area.h | 39 ++++++ src/include/units/physical/cgs/energy.h | 47 +++++++ src/include/units/physical/cgs/force.h | 48 +++++++ src/include/units/physical/cgs/length.h | 44 ++++++ src/include/units/physical/cgs/mass.h | 44 ++++++ src/include/units/physical/cgs/power.h | 47 +++++++ src/include/units/physical/cgs/pressure.h | 48 +++++++ src/include/units/physical/cgs/time.h | 42 ++++++ src/include/units/physical/cgs/velocity.h | 46 +++++++ src/include/units/physical/si/mass.h | 1 + src/include/units/quantity.h | 115 ++++++++++------ src/include/units/unit.h | 7 +- test/unit_test/static/CMakeLists.txt | 2 +- test/unit_test/static/cgs_test.cpp | 125 +++++++++++------- 18 files changed, 724 insertions(+), 107 deletions(-) create mode 100644 src/include/units/physical/cgs/acceleration.h create mode 100644 src/include/units/physical/cgs/area.h create mode 100644 src/include/units/physical/cgs/energy.h create mode 100644 src/include/units/physical/cgs/force.h create mode 100644 src/include/units/physical/cgs/length.h create mode 100644 src/include/units/physical/cgs/mass.h create mode 100644 src/include/units/physical/cgs/power.h create mode 100644 src/include/units/physical/cgs/pressure.h create mode 100644 src/include/units/physical/cgs/time.h create mode 100644 src/include/units/physical/cgs/velocity.h diff --git a/src/include/units/base_dimension.h b/src/include/units/base_dimension.h index 3e6b03a7..73ce92c2 100644 --- a/src/include/units/base_dimension.h +++ b/src/include/units/base_dimension.h @@ -31,14 +31,18 @@ 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. + * Base quantity is a quantity in a conventionally chosen subset of a given system of quantities, where no quantity + * in the subset can be expressed in terms of the other quantities within that subset. They are referred to as + * being mutually independent since a base quantity cannot be expressed as a product of powers of the other base + * quantities. + * + * Base unit is a measurement unit that is adopted by convention for a base quantity 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. + * be multiplied and divided which will result with an adjustment of its factor in an Exponent od a DerivedDimension + * (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 @@ -47,7 +51,7 @@ template struct base_dimension { using base_type_workaround = base_dimension; // TODO Replace with is_derived_from_instantiation when fixed static constexpr auto name = Name; - using coherent_unit = U; + using base_unit = U; }; // BaseDimension diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h index cf35b99d..42456975 100644 --- a/src/include/units/derived_dimension.h +++ b/src/include/units/derived_dimension.h @@ -77,7 +77,7 @@ struct derived_dimension<> : downcast_base> {}; * @tparam ERest zero or more following exponents of a derived dimension */ template -struct derived_dimension : downcast_base> {}; +struct derived_dimension : downcast_base> {}; // TODO rename to 'dimension'? // DerivedDimension template @@ -220,6 +220,35 @@ struct extract, Num, Den>, ERest...> { template using make_dimension = dim_consolidate::type, exp_less>>::type; +template + requires (E::den == 1 || E::den == 2) // TODO provide support for any den +struct exp_ratio { + using base_ratio = E::dimension::base_unit::ratio; + using positive_ratio = conditional, base_ratio>; + static constexpr std::int64_t N = E::num * E::den < 0 ? -E::num : E::num; + using pow = ratio_pow; + using type = conditional, pow>; +}; + +template +struct base_units_ratio_impl; + +template +struct base_units_ratio_impl> { + using type = ratio_multiply::type, typename base_units_ratio_impl>::type>; +}; + +template +struct base_units_ratio_impl> { + using type = exp_ratio::type; +}; + +/** + * @brief Calculates the common ratio of all the references of base units in the derived dimension + */ +template +using base_units_ratio = base_units_ratio_impl::type; + } // namespace detail /** @@ -247,16 +276,27 @@ template struct derived_dimension : downcast_child> { using recipe = derived_dimension; using coherent_unit = U; + using base_units_ratio = detail::base_units_ratio>::downcast_base_type>; }; -// same_dim -template -inline constexpr bool same_dim = false; +namespace detail { -template -inline constexpr bool same_dim = std::is_same_v; +template +struct dimension_unit_impl; -template -inline constexpr bool same_dim = std::is_same_v; +template +struct dimension_unit_impl { + using type = D::base_unit; +}; + +template +struct dimension_unit_impl { + using type = D::coherent_unit; +}; + +} + +template +using dimension_unit = detail::dimension_unit_impl::type; } // namespace units diff --git a/src/include/units/dimension_op.h b/src/include/units/dimension_op.h index 5a58c02f..e18649e8 100644 --- a/src/include/units/dimension_op.h +++ b/src/include/units/dimension_op.h @@ -27,6 +27,40 @@ namespace units { +// equivalent_dim +namespace detail { + +template +struct equivalent_dim_impl : std::false_type {}; + +template +struct equivalent_base_dim : std::conjunction, + same_unit_reference> {}; + +template +struct equivalent_dim_impl : std::disjunction, equivalent_base_dim> {}; + +template +struct equivalent_exp : std::false_type {}; + +template +struct equivalent_exp, exp> : equivalent_dim_impl {}; + +template +struct equivalent_derived_dim : std::false_type {}; + +template + requires (sizeof...(Es1) == sizeof...(Es2)) +struct equivalent_derived_dim, derived_dimension> : std::conjunction...> {}; + +template +struct equivalent_dim_impl : std::disjunction, equivalent_derived_dim, downcast_base_t>> {}; + +} // namespace detail + +template +inline constexpr bool equivalent_dim = detail::equivalent_dim_impl::value; + /** * @brief Unknown dimension * @@ -43,21 +77,34 @@ struct unknown_dimension : derived_dimension, scaled_un namespace detail { -template -struct downcast_dimension_impl { +template +struct check_unknown { using type = D; }; -// downcast did not find user predefined type +// downcast did not find a user predefined type template -struct downcast_dimension_impl> { +struct check_unknown> { using type = unknown_dimension; }; +template +struct downcast_dimension_impl; + +template +struct downcast_dimension_impl { + using type = D; +}; + +template +struct downcast_dimension_impl { + using type = check_unknown>::type; +}; + } // namespace detail template -using downcast_dimension = detail::downcast_dimension_impl>::type; +using downcast_dimension = detail::downcast_dimension_impl::type; // dim_invert namespace detail { diff --git a/src/include/units/physical/cgs/acceleration.h b/src/include/units/physical/cgs/acceleration.h new file mode 100644 index 00000000..0c2b488a --- /dev/null +++ b/src/include/units/physical/cgs/acceleration.h @@ -0,0 +1,45 @@ +// 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 + +namespace units::cgs { + +struct gal : named_unit {}; +struct dim_acceleration : physical::dim_acceleration {}; + +template +using acceleration = quantity; + +inline namespace literals { + +// Gal +constexpr auto operator""Gal(unsigned long long l) { return acceleration(l); } +constexpr auto operator""Gal(long double l) { return acceleration(l); } + +} // namespace literals + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/area.h b/src/include/units/physical/cgs/area.h new file mode 100644 index 00000000..4eac9f6a --- /dev/null +++ b/src/include/units/physical/cgs/area.h @@ -0,0 +1,39 @@ +// 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::cgs { + +using si::square_centimetre; + +struct dim_area : physical::dim_area {}; + +template +using area = quantity; + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/energy.h b/src/include/units/physical/cgs/energy.h new file mode 100644 index 00000000..413eb773 --- /dev/null +++ b/src/include/units/physical/cgs/energy.h @@ -0,0 +1,47 @@ +// 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::cgs { + +struct erg : named_unit {}; + +struct dim_energy : physical::dim_energy {}; + +template +using energy = quantity; + +inline namespace literals { + +// erg +constexpr auto operator""_erg(unsigned long long l) { return energy(l); } +constexpr auto operator""_erg(long double l) { return energy(l); } + +} // namespace literals + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/force.h b/src/include/units/physical/cgs/force.h new file mode 100644 index 00000000..d9f9a1a7 --- /dev/null +++ b/src/include/units/physical/cgs/force.h @@ -0,0 +1,48 @@ +// 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::cgs { + +struct dyne : named_unit {}; + +struct dim_force : physical::dim_force {}; + +template +using force = quantity; + +inline namespace literals { + +// dyn +constexpr auto operator""dyn(unsigned long long l) { return force(l); } +constexpr auto operator""dyn(long double l) { return force(l); } + +} // namespace literals + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/length.h b/src/include/units/physical/cgs/length.h new file mode 100644 index 00000000..3b672ecd --- /dev/null +++ b/src/include/units/physical/cgs/length.h @@ -0,0 +1,44 @@ +// 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 + +namespace units::cgs { + +using si::centimetre; + +struct dim_length : physical::dim_length {}; + +template +using length = quantity; + +inline namespace literals { + +using si::literals::operator"" cm; + +} + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/mass.h b/src/include/units/physical/cgs/mass.h new file mode 100644 index 00000000..c58d1ca1 --- /dev/null +++ b/src/include/units/physical/cgs/mass.h @@ -0,0 +1,44 @@ +// 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 + +namespace units::cgs { + +using si::gram; + +struct dim_mass : physical::dim_mass {}; + +template +using mass = quantity; + +inline namespace literals { + +using si::literals::operator"" g; + +} + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/power.h b/src/include/units/physical/cgs/power.h new file mode 100644 index 00000000..5a813cb2 --- /dev/null +++ b/src/include/units/physical/cgs/power.h @@ -0,0 +1,47 @@ +// 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::cgs { + +struct erg_per_second : unit {}; + +struct dim_power : physical::dim_power {}; + +template +using power = quantity; + +inline namespace literals { + +// ergps +constexpr auto operator""_ergps(unsigned long long l) { return power(l); } +constexpr auto operator""_ergps(long double l) { return power(l); } + +} // namespace literals + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/pressure.h b/src/include/units/physical/cgs/pressure.h new file mode 100644 index 00000000..0e3b98d4 --- /dev/null +++ b/src/include/units/physical/cgs/pressure.h @@ -0,0 +1,48 @@ +// 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::cgs { + +struct barye : named_unit {}; + +struct dim_pressure : physical::dim_pressure {}; + +template +using pressure = quantity; + +inline namespace literals { + +// Ba +constexpr auto operator""Ba(unsigned long long l) { return pressure(l); } +constexpr auto operator""Ba(long double l) { return pressure(l); } + +} // namespace literals + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/time.h b/src/include/units/physical/cgs/time.h new file mode 100644 index 00000000..c678bc79 --- /dev/null +++ b/src/include/units/physical/cgs/time.h @@ -0,0 +1,42 @@ +// 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 + +namespace units::cgs { + +using si::second; + +using si::dim_time; +using si::time; + +inline namespace literals { + +using si::literals::operator"" s; + +} + +} // namespace units::cgs diff --git a/src/include/units/physical/cgs/velocity.h b/src/include/units/physical/cgs/velocity.h new file mode 100644 index 00000000..8adbaa58 --- /dev/null +++ b/src/include/units/physical/cgs/velocity.h @@ -0,0 +1,46 @@ +// 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::cgs { + +struct centimetre_per_second : unit {}; +struct dim_velocity : physical::dim_velocity {}; + +template +using velocity = quantity; + +inline namespace literals { + +// cmps +constexpr auto operator"" cmps(unsigned long long l) { return velocity(l); } +constexpr auto operator"" cmps(long double l) { return velocity(l); } + +} // namespace literals + +} // namespace units::cgs diff --git a/src/include/units/physical/si/mass.h b/src/include/units/physical/si/mass.h index cd3fc4e8..c6c59d15 100644 --- a/src/include/units/physical/si/mass.h +++ b/src/include/units/physical/si/mass.h @@ -24,6 +24,7 @@ #include #include +#include namespace units::si { diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 19796b57..7bfa7375 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -44,7 +44,7 @@ concept Quantity = detail::is_quantity; // QuantityOf template -concept QuantityOf = Quantity && Dimension && same_dim; +concept QuantityOf = Quantity && Dimension && equivalent_dim; // Scalar template @@ -76,9 +76,23 @@ struct common_quantity_impl, quantity, Rep> { using type = quantity>, Rep>; }; +template + requires same_unit_reference, dimension_unit>::value +struct common_quantity_impl, quantity, Rep> { + using type = quantity>, Rep>; +}; + +template +struct common_quantity_impl, quantity, Rep> { + using ratio1 = ratio_multiply; + using ratio2 = ratio_multiply; + using type = quantity>, Rep>; +}; + } // namespace detail template> + requires equivalent_dim using common_quantity = detail::common_quantity_impl::type; // quantity_cast @@ -130,6 +144,27 @@ struct quantity_cast_impl { } }; +template +struct cast_ratio; + +template +struct cast_ratio { + using type = ratio_divide; +}; + +template + requires same_unit_reference::value +struct cast_ratio { + using type = ratio_divide; +}; + +template +struct cast_ratio { + using from_ratio = ratio_multiply; + using to_ratio = ratio_multiply; + using type = ratio_divide; +}; + } // namespace detail /** @@ -150,7 +185,7 @@ template requires QuantityOf && detail::basic_arithmetic> { - using c_ratio = ratio_divide; + using c_ratio = detail::cast_ratio::type; using c_rep = std::common_type_t; using ret_unit = downcast_unit; using ret = quantity; @@ -172,7 +207,7 @@ template */ template [[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires same_dim + requires equivalent_dim { return quantity_cast>(q); } @@ -260,7 +295,7 @@ public: constexpr explicit quantity(const Value& v) : value_{static_cast(v)} {} template - requires same_dim && + requires equivalent_dim && detail::safe_convertible && detail::safe_divisible constexpr quantity(const Q2& q) : value_{quantity_cast(q).count()} {} @@ -446,7 +481,7 @@ template template [[nodiscard]] constexpr Scalar AUTO operator*(const quantity& lhs, const quantity& rhs) - requires same_dim>&& detail::basic_arithmetic + requires equivalent_dim> && detail::basic_arithmetic { using common_rep = decltype(lhs.count() * rhs.count()); using ratio = ratio_multiply; @@ -455,14 +490,14 @@ template [[nodiscard]] constexpr Quantity AUTO operator*(const quantity& lhs, const quantity& rhs) - requires (!same_dim>) && + requires (!equivalent_dim>) && // TODO equivalent_derived_dim? (treat_as_floating_point || (std::ratio_multiply::den == 1)) && detail::basic_arithmetic { using dim = dimension_multiply; - using ratio1 = ratio_divide; - using ratio2 = ratio_divide; + using ratio1 = ratio_divide::ratio>; + using ratio2 = ratio_divide::ratio>; using ratio = ratio_multiply; using unit = downcast_unit; using common_rep = decltype(lhs.count() * rhs.count()); @@ -495,29 +530,29 @@ template return ret(q.count() / v); } -template -[[nodiscard]] constexpr Scalar AUTO operator/(const quantity& lhs, const quantity& rhs) - requires detail::basic_arithmetic +template +[[nodiscard]] constexpr Scalar AUTO operator/(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic && equivalent_dim { Expects(rhs != std::remove_cvref_t(0)); using common_rep = decltype(lhs.count() / rhs.count()); - using cq = common_quantity, quantity, common_rep>; + using cq = common_quantity, quantity, common_rep>; return cq(lhs).count() / cq(rhs).count(); } template [[nodiscard]] constexpr Quantity AUTO operator/(const quantity& lhs, const quantity& rhs) - requires (treat_as_floating_point || - (ratio_divide::den == 1)) && - detail::basic_arithmetic + requires detail::basic_arithmetic && (!equivalent_dim) && // TODO equivalent_derived_dim? + (treat_as_floating_point || + (ratio_divide::den == 1)) { Expects(rhs != std::remove_cvref_t(0)); using common_rep = decltype(lhs.count() / rhs.count()); using dim = dimension_divide; - using ratio1 = ratio_divide; - using ratio2 = ratio_divide; + using ratio1 = ratio_divide::ratio>; + using ratio2 = ratio_divide::ratio>; using ratio = ratio_divide; using unit = downcast_unit; using ret = quantity; @@ -546,51 +581,57 @@ template return ret(ret(lhs).count() % ret(rhs).count()); } -template -[[nodiscard]] constexpr bool operator==(const quantity& lhs, const quantity& rhs) - requires detail::basic_arithmetic && +template +[[nodiscard]] constexpr bool operator==(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && std::equality_comparable_with { - using cq = common_quantity, quantity>; + using cq = common_quantity, quantity>; return cq(lhs).count() == cq(rhs).count(); } -template -[[nodiscard]] constexpr bool operator!=(const quantity& lhs, const quantity& rhs) - requires detail::basic_arithmetic && +template +[[nodiscard]] constexpr bool operator!=(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && std::equality_comparable_with { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const quantity& lhs, const quantity& rhs) - requires detail::basic_arithmetic && +template +[[nodiscard]] constexpr bool operator<(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && std::totally_ordered_with { - using cq = common_quantity, quantity>; + using cq = common_quantity, quantity>; return cq(lhs).count() < cq(rhs).count(); } -template -[[nodiscard]] constexpr bool operator<=(const quantity& lhs, const quantity& rhs) - requires detail::basic_arithmetic && +template +[[nodiscard]] constexpr bool operator<=(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && std::totally_ordered_with { return !(rhs < lhs); } -template -[[nodiscard]] constexpr bool operator>(const quantity& lhs, const quantity& rhs) - requires detail::basic_arithmetic && +template +[[nodiscard]] constexpr bool operator>(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && std::totally_ordered_with { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator>=(const quantity& lhs, const quantity& rhs) - requires detail::basic_arithmetic && +template +[[nodiscard]] constexpr bool operator>=(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && std::totally_ordered_with { return !(lhs < rhs); diff --git a/src/include/units/unit.h b/src/include/units/unit.h index 20618c74..2f9a3254 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -54,7 +54,10 @@ struct scaled_unit : downcast_base> { }; template -using downcast_unit = downcast>; +using downcast_unit = downcast::reference, R>>; + +template +struct same_unit_reference : std::is_same {}; /** * @brief A starting point for a new hierarchy of units @@ -146,7 +149,7 @@ template concept UnitOf = Unit && Dimension && - std::same_as; + std::same_as::reference>; namespace detail { diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index 2447b832..4b39dc76 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -21,7 +21,7 @@ # SOFTWARE. add_library(unit_tests_static -# cgs_test.cpp + cgs_test.cpp custom_unit_test.cpp data_test.cpp dimension_op_test.cpp diff --git a/test/unit_test/static/cgs_test.cpp b/test/unit_test/static/cgs_test.cpp index 341126f2..07348f4e 100644 --- a/test/unit_test/static/cgs_test.cpp +++ b/test/unit_test/static/cgs_test.cpp @@ -20,56 +20,87 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include -#include -#include -#include -namespace cgs { - - using units::centimetre; - using units::gram; - using units::second; - struct centimetre_per_second : units::deduced_derived_unit {}; - struct gal : units::named_deduced_derived_unit {}; - struct dyne : units::named_deduced_derived_unit {}; - struct erg : units::named_deduced_derived_unit {}; - struct ergps : units::named_deduced_derived_unit {}; // TODO make it work for erg and non-named - struct barye : units::named_deduced_derived_unit {}; - - - inline namespace literals { - - using namespace units::literals; - - constexpr auto operator""cmps(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""cmps(long double l) { return units::quantity(l); } - constexpr auto operator""Gal(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""Gal(long double l) { return units::quantity(l); } - constexpr auto operator""dyn(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""dyn(long double l) { return units::quantity(l); } - constexpr auto operator""_erg(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_erg(long double l) { return units::quantity(l); } - constexpr auto operator""_ergps(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_ergps(long double l) { return units::quantity(l); } - constexpr auto operator""Ba(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""Ba(long double l) { return units::quantity(l); } - - } // namespace literals - -} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace { - using namespace cgs::literals; +using namespace units; - static_assert(100cm == 1m); - static_assert(1'000g == 1kg); - static_assert(100cmps == 1mps); - static_assert(100Gal == 1mps_sq); - static_assert(100'000dyn == 1N); - static_assert(10'000'000_erg == 1_J); - static_assert(10'000'000_ergps == 1W); - static_assert(10Ba == 1Pa); +static_assert(cgs::length(100) == si::length(1)); +static_assert(cgs::mass(1'000) == si::mass(1)); +static_assert(cgs::time(1) == si::time(1)); +static_assert(cgs::velocity(100) == si::velocity(1)); +static_assert(cgs::acceleration(100) == si::acceleration(1)); +static_assert(cgs::force(100'000) == si::force(1)); +static_assert(cgs::energy(10'000'000) == si::energy(1)); +static_assert(cgs::power(10'000'000) == si::power(1)); +static_assert(cgs::pressure(10) == si::pressure(1)); -} \ No newline at end of file +namespace si_test { + +using namespace units::si::literals; + +static_assert(cgs::length(100) == 1m); +static_assert(cgs::mass(1'000) == 1kg); +static_assert(cgs::time(1) == 1s); +static_assert(cgs::velocity(100) == 1mps); +static_assert(cgs::acceleration(100) == 1mps_sq); +static_assert(cgs::force(100'000) == 1N); +static_assert(cgs::energy(10'000'000) == 1_J); +static_assert(cgs::power(10'000'000) == 1W); +static_assert(cgs::pressure(10) == 1Pa); + +} + +namespace cgs_test { + +using namespace units::cgs::literals; + +static_assert(100cm == si::length(1)); +static_assert(1'000g == si::mass(1)); +static_assert(1s == si::time(1)); +static_assert(100cmps == si::velocity(1)); +static_assert(100Gal == si::acceleration(1)); +static_assert(100'000dyn == si::force(1)); +static_assert(10'000'000_erg == si::energy(1)); +static_assert(10'000'000_ergps == si::power(1)); +static_assert(10Ba == si::pressure(1)); + +} + +namespace both_test { + +using namespace units::si::literals; +using namespace units::cgs::literals; + +static_assert(100cm == 1m); +static_assert(1'000g == 1kg); +static_assert(1s == 1s); +static_assert(100cmps == 1mps); +static_assert(100Gal == 1mps_sq); +static_assert(100'000dyn == 1N); +static_assert(10'000'000_erg == 1_J); +static_assert(10'000'000_ergps == 1W); +static_assert(10Ba == quantity_cast(1Pa)); + +} + +} From bc1901f4f001cde3efb238f147815467b75a0a46 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 08:07:13 +0100 Subject: [PATCH 14/44] Text formatting enabled + directory tree refactoring --- src/include/units/base_dimension.h | 4 +- src/include/units/{ => bits}/concepts.h | 4 +- src/include/units/bits/deduced_symbol_text.h | 82 +++++++++++ src/include/units/bits/deduced_unit.h | 72 ++++++++++ src/include/units/{ => bits}/dimension_op.h | 6 +- .../units/bits/{ => external}/downcasting.h | 2 +- .../units/bits/{ => external}/fixed_string.h | 0 src/include/units/bits/{ => external}/hacks.h | 0 .../bits/{ => external}/numeric_concepts.h | 2 +- src/include/units/bits/external/text_tools.h | 64 +++++++++ .../units/bits/{ => external}/type_list.h | 2 +- .../units/bits/{ => external}/type_traits.h | 0 src/include/units/bits/unit_text.h | 129 ++++++++++++++++++ src/include/units/derived_dimension.h | 28 +--- src/include/units/format.h | 22 +-- src/include/units/physical/dimensions.h | 2 +- src/include/units/prefix.h | 4 +- src/include/units/quantity.h | 7 +- src/include/units/ratio.h | 2 +- src/include/units/unit.h | 75 ++-------- src/include/units/{bits => }/unit_concept.h | 3 +- src/include/units/unit_of_concept.h | 57 ++++++++ test/CMakeLists.txt | 2 +- test/unit_test/runtime/digital_info_test.cpp | 44 +----- test/unit_test/runtime/fmt_test.cpp | 32 +++-- test/unit_test/runtime/fmt_units_test.cpp | 14 +- test/unit_test/runtime/math_test.cpp | 78 +++-------- test/unit_test/static/dimension_op_test.cpp | 2 +- test/unit_test/static/type_list_test.cpp | 2 +- test/unit_test/static/unit_test.cpp | 2 +- 30 files changed, 507 insertions(+), 236 deletions(-) rename src/include/units/{ => bits}/concepts.h (95%) create mode 100644 src/include/units/bits/deduced_symbol_text.h create mode 100644 src/include/units/bits/deduced_unit.h rename src/include/units/{ => bits}/dimension_op.h (98%) rename src/include/units/bits/{ => external}/downcasting.h (98%) rename src/include/units/bits/{ => external}/fixed_string.h (100%) rename src/include/units/bits/{ => external}/hacks.h (100%) rename src/include/units/bits/{ => external}/numeric_concepts.h (99%) create mode 100644 src/include/units/bits/external/text_tools.h rename src/include/units/bits/{ => external}/type_list.h (99%) rename src/include/units/bits/{ => external}/type_traits.h (100%) create mode 100644 src/include/units/bits/unit_text.h rename src/include/units/{bits => }/unit_concept.h (96%) create mode 100644 src/include/units/unit_of_concept.h diff --git a/src/include/units/base_dimension.h b/src/include/units/base_dimension.h index 73ce92c2..2df37dc4 100644 --- a/src/include/units/base_dimension.h +++ b/src/include/units/base_dimension.h @@ -22,8 +22,8 @@ #pragma once -#include -#include +#include +#include #include namespace units { diff --git a/src/include/units/concepts.h b/src/include/units/bits/concepts.h similarity index 95% rename from src/include/units/concepts.h rename to src/include/units/bits/concepts.h index fcc16f35..b43d4524 100644 --- a/src/include/units/concepts.h +++ b/src/include/units/bits/concepts.h @@ -22,8 +22,8 @@ #pragma once -#include -#include +#include +#include #include #include diff --git a/src/include/units/bits/deduced_symbol_text.h b/src/include/units/bits/deduced_symbol_text.h new file mode 100644 index 00000000..04476b27 --- /dev/null +++ b/src/include/units/bits/deduced_symbol_text.h @@ -0,0 +1,82 @@ +// 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 + +namespace units::detail { + +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 deduced_symbol_text(derived_dimension, std::index_sequence) +{ + return (exp_text() + ...); +} + +template +constexpr auto deduced_symbol_text() +{ + return deduced_symbol_text(Dim(), std::index_sequence_for()); +} + +} // namespace units::detail diff --git a/src/include/units/bits/deduced_unit.h b/src/include/units/bits/deduced_unit.h new file mode 100644 index 00000000..6f170aa5 --- /dev/null +++ b/src/include/units/bits/deduced_unit.h @@ -0,0 +1,72 @@ +// 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::detail { + +// same_scaled_units +template +inline constexpr bool same_scaled_units = false; + +template +inline constexpr bool same_scaled_units, Us...> = (UnitOf && ...); + +// deduced_unit +template +struct ratio_op; + +template +struct ratio_op { + using ratio = Result; +}; + +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 derived_ratio; + +template +struct derived_ratio, Us...> { + using ratio = ::units::ratio<1>; +}; + +template +struct derived_ratio, U, URest...> { + using rest_ratio = derived_ratio, URest...>::ratio; + using ratio = ratio_op::ratio; +}; + +template +using deduced_unit = + scaled_unit::ratio>; + +} // namespace units::detail diff --git a/src/include/units/dimension_op.h b/src/include/units/bits/dimension_op.h similarity index 98% rename from src/include/units/dimension_op.h rename to src/include/units/bits/dimension_op.h index e18649e8..520086c8 100644 --- a/src/include/units/dimension_op.h +++ b/src/include/units/bits/dimension_op.h @@ -200,7 +200,7 @@ struct dimension_sqrt_impl; template struct dimension_sqrt_impl { - using type = derived_dimension>; + using type = downcast_dimension>>; }; template @@ -210,7 +210,7 @@ struct dimension_sqrt_impl>> { template struct dimension_sqrt_impl { - using type = dimension_sqrt_impl; + using type = dimension_sqrt_impl::type; }; template @@ -221,7 +221,7 @@ struct dimension_sqrt_impl> { } // namespace detail template -using dimension_sqrt = detail::dimension_sqrt_impl::type; +using dimension_sqrt = detail::dimension_sqrt_impl::type; // dimension_pow namespace detail { diff --git a/src/include/units/bits/downcasting.h b/src/include/units/bits/external/downcasting.h similarity index 98% rename from src/include/units/bits/downcasting.h rename to src/include/units/bits/external/downcasting.h index 822bf909..535147e9 100644 --- a/src/include/units/bits/downcasting.h +++ b/src/include/units/bits/external/downcasting.h @@ -22,7 +22,7 @@ #pragma once -#include +#include #include namespace units { diff --git a/src/include/units/bits/fixed_string.h b/src/include/units/bits/external/fixed_string.h similarity index 100% rename from src/include/units/bits/fixed_string.h rename to src/include/units/bits/external/fixed_string.h diff --git a/src/include/units/bits/hacks.h b/src/include/units/bits/external/hacks.h similarity index 100% rename from src/include/units/bits/hacks.h rename to src/include/units/bits/external/hacks.h diff --git a/src/include/units/bits/numeric_concepts.h b/src/include/units/bits/external/numeric_concepts.h similarity index 99% rename from src/include/units/bits/numeric_concepts.h rename to src/include/units/bits/external/numeric_concepts.h index 5ccbe997..abbef656 100644 --- a/src/include/units/bits/numeric_concepts.h +++ b/src/include/units/bits/external/numeric_concepts.h @@ -22,7 +22,7 @@ #pragma once -#include +#include // P1813 - A Concept Design for the Numeric Algorithms diff --git a/src/include/units/bits/external/text_tools.h b/src/include/units/bits/external/text_tools.h new file mode 100644 index 00000000..7e102fe7 --- /dev/null +++ b/src/include/units/bits/external/text_tools.h @@ -0,0 +1,64 @@ +// 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::detail { + +template + requires (0 <= Value) && (Value < 10) +inline constexpr basic_fixed_string superscript_number = ""; + +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(); +} + +} // namespace units::detail diff --git a/src/include/units/bits/type_list.h b/src/include/units/bits/external/type_list.h similarity index 99% rename from src/include/units/bits/type_list.h rename to src/include/units/bits/external/type_list.h index a4b51d6e..422bb00f 100644 --- a/src/include/units/bits/type_list.h +++ b/src/include/units/bits/external/type_list.h @@ -22,7 +22,7 @@ #pragma once -#include +#include namespace units { diff --git a/src/include/units/bits/type_traits.h b/src/include/units/bits/external/type_traits.h similarity index 100% rename from src/include/units/bits/type_traits.h rename to src/include/units/bits/external/type_traits.h diff --git a/src/include/units/bits/unit_text.h b/src/include/units/bits/unit_text.h new file mode 100644 index 00000000..af95c30e --- /dev/null +++ b/src/include/units/bits/unit_text.h @@ -0,0 +1,129 @@ +// 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::detail { + +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) { + // no ratio/prefix + return basic_fixed_string(""); + } + else { + if constexpr (!std::same_as) { + // try to form a prefix + 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 derived_dimension_unit_text(derived_dimension, std::index_sequence) +{ + return (exp_text::symbol, Idxs>() + ...); +} + +template +constexpr auto derived_dimension_unit_text(derived_dimension d) +{ + return derived_dimension_unit_text(d, std::index_sequence_for()); +} + +template +constexpr bool all_named(derived_dimension) +{ + return (dimension_unit::is_named && ...); +} + +template +constexpr auto derived_dimension_unit_text() +{ + using recipe = typename Dim::recipe; + if constexpr(all_named(recipe())) + return derived_dimension_unit_text(recipe()); + else + return derived_dimension_unit_text(Dim()); +} + +// TODO Inline below concept when switched to gcc-10 +template +concept has_symbol = requires{ T::symbol; }; + +template +constexpr auto unit_text() +{ + if constexpr(has_symbol) { + // already has a symbol so print it + return U::symbol; + } + else { + // print as a prefix or ratio of a reference unit + auto prefix_txt = prefix_or_ratio_text(); + + if constexpr(has_symbol) { + // use predefined reference unit symbol + return prefix_txt + U::reference::symbol; + } + else { + // use derived dimension ingredients to create a unit symbol + return prefix_txt + derived_dimension_unit_text(); + } + } +} + +} // namespace units::detail diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h index 42456975..a0846a81 100644 --- a/src/include/units/derived_dimension.h +++ b/src/include/units/derived_dimension.h @@ -23,9 +23,9 @@ #pragma once #include -#include -#include -#include +#include +#include +#include #include #include @@ -264,7 +264,7 @@ using base_units_ratio = base_units_ratio_impl::type; * 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) @@ -279,24 +279,4 @@ struct derived_dimension : downcast_child>::downcast_base_type>; }; -namespace detail { - -template -struct dimension_unit_impl; - -template -struct dimension_unit_impl { - using type = D::base_unit; -}; - -template -struct dimension_unit_impl { - using type = D::coherent_unit; -}; - -} - -template -using dimension_unit = detail::dimension_unit_impl::type; - } // namespace units diff --git a/src/include/units/format.h b/src/include/units/format.h index 1bbea90d..68c688cc 100644 --- a/src/include/units/format.h +++ b/src/include/units/format.h @@ -22,7 +22,7 @@ #pragma once -#include +#include #include #include #include @@ -116,20 +116,20 @@ namespace units { return format_to(out, treat_as_floating_point ? "{:" + sign_text + "g}" : "{:" + sign_text + "}", val); } - template + template inline static OutputIt format_units_quantity_unit(OutputIt out) { - return format_to(out, "{}", unit_text().c_str()); + return format_to(out, "{}", unit_text().c_str()); } - template + template struct units_formatter { OutputIt out; Rep val; fmt::sign_t sign; int precision; - explicit units_formatter(OutputIt o, quantity q, fmt::sign_t s, int prec): + explicit units_formatter(OutputIt o, quantity q, fmt::sign_t s, int prec): out(o), val(q.count()), sign(s), precision(prec) { } @@ -147,7 +147,7 @@ namespace units { void on_quantity_unit() { - out = format_units_quantity_unit(out); + out = format_units_quantity_unit(out); } }; @@ -155,10 +155,10 @@ namespace units { } // namespace units -template -struct fmt::formatter, CharT> { +template +struct fmt::formatter, CharT> { private: - using quantity = units::quantity; + using quantity = units::quantity; using iterator = fmt::basic_parse_context::iterator; using arg_ref_type = fmt::internal::arg_ref; @@ -293,7 +293,7 @@ public: } template - auto format(const units::quantity& q, FormatContext& ctx) + auto format(const units::quantity& q, FormatContext& ctx) { auto begin = format_str.begin(), end = format_str.end(); @@ -310,7 +310,7 @@ public: // default format should print value followed by the unit separeted with 1 space out = units::detail::format_units_quantity_value(out, q.count(), specs.sign, precision); *out++ = CharT(' '); - units::detail::format_units_quantity_unit(out); + units::detail::format_units_quantity_unit(out); } else { // user provided format diff --git a/src/include/units/physical/dimensions.h b/src/include/units/physical/dimensions.h index 86004125..66460480 100644 --- a/src/include/units/physical/dimensions.h +++ b/src/include/units/physical/dimensions.h @@ -23,7 +23,7 @@ #pragma once #include -#include +#include #include #include diff --git a/src/include/units/prefix.h b/src/include/units/prefix.h index 5f95b7e7..2117178d 100644 --- a/src/include/units/prefix.h +++ b/src/include/units/prefix.h @@ -22,8 +22,8 @@ #pragma once -#include -#include +#include +#include #include namespace units { diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 7bfa7375..0cf29bbf 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -22,8 +22,9 @@ #pragma once -#include -#include +#include +#include +#include #include #include @@ -441,7 +442,7 @@ public: template friend std::basic_ostream& operator<<(std::basic_ostream& os, const quantity& q) { - return os << q.count(); // << " " << detail::unit_text(); TODO add support + return os << q.count() << " " << detail::unit_text(); } }; diff --git a/src/include/units/ratio.h b/src/include/units/ratio.h index 1092ca35..80e964f9 100644 --- a/src/include/units/ratio.h +++ b/src/include/units/ratio.h @@ -22,7 +22,7 @@ #pragma once -#include +#include #include #include #include diff --git a/src/include/units/unit.h b/src/include/units/unit.h index 2f9a3254..f0875a95 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -22,10 +22,13 @@ #pragma once -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -60,7 +63,7 @@ template struct same_unit_reference : std::is_same {}; /** - * @brief A starting point for a new hierarchy of units + * @brief An unnamed unit * * Defines a new unnamed (in most cases coherent) derived unit of a specific derived dimension * and it should be passed in this dimension's definition. @@ -76,7 +79,7 @@ struct unit : downcast_child>> { /** * @brief Unknown unit * - * Used as a reference unit of an unknown dimension. + * Used as a coherent unit of an unknown dimension. */ struct unknown_unit : unit {}; @@ -144,65 +147,13 @@ struct prefixed_unit : using prefix_type = P::prefix_type; }; -// UnitOf -template -concept UnitOf = - Unit && - Dimension && - std::same_as::reference>; - -namespace detail { - -// same_scaled_units -template -inline constexpr bool same_scaled_units = false; - -template -inline constexpr bool same_scaled_units, Us...> = (UnitOf && ...); - -// deduced_unit -template -struct ratio_op; - -template -struct ratio_op { - using ratio = Result; -}; - -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 derived_ratio; - -template -struct derived_ratio, Us...> { - using ratio = ::units::ratio<1>; -}; - -template -struct derived_ratio, U, URest...> { - using rest_ratio = derived_ratio, URest...>::ratio; - using ratio = ratio_op::ratio; -}; - -template -using deduced_unit = - scaled_unit::ratio>; - -} // namespace detail - /** * @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. + * derived dimension. The number and order of provided units should match the recipe of the + * derived dimension. All of the units provided should also be a named ones so it is possible + * to create a deduced symbol text. * * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) * @tparam Dim a derived dimension recipe to use for deduction @@ -214,7 +165,7 @@ template (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(); // TODO implement this + static constexpr auto symbol = detail::deduced_symbol_text(); using prefix_type = no_prefix; }; diff --git a/src/include/units/bits/unit_concept.h b/src/include/units/unit_concept.h similarity index 96% rename from src/include/units/bits/unit_concept.h rename to src/include/units/unit_concept.h index d8e675b3..9e250657 100644 --- a/src/include/units/bits/unit_concept.h +++ b/src/include/units/unit_concept.h @@ -22,11 +22,12 @@ #pragma once -#include +#include #include namespace units { +// UnitRatio template concept UnitRatio = Ratio && (R::num * R::den > 0); diff --git a/src/include/units/unit_of_concept.h b/src/include/units/unit_of_concept.h new file mode 100644 index 00000000..143d4678 --- /dev/null +++ b/src/include/units/unit_of_concept.h @@ -0,0 +1,57 @@ +// 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 { + +namespace detail { + +template +struct dimension_unit_impl; + +template +struct dimension_unit_impl { + using type = D::base_unit; +}; + +template +struct dimension_unit_impl { + using type = D::coherent_unit; +}; + +} // namespace detail + +template +using dimension_unit = detail::dimension_unit_impl::type; + +// UnitOf +template +concept UnitOf = + Unit && + Dimension && + std::same_as::reference>; + // same_unit_reference> // TODO check if this works + +} // namespace units diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1a5b1883..67a1d088 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) diff --git a/test/unit_test/runtime/digital_info_test.cpp b/test/unit_test/runtime/digital_info_test.cpp index 600c17e6..0b032a09 100644 --- a/test/unit_test/runtime/digital_info_test.cpp +++ b/test/unit_test/runtime/digital_info_test.cpp @@ -20,43 +20,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include +#include #include #include -namespace data { +using namespace units::data; - struct base_dim_digital_information : units::base_dimension<"digital information", "b"> {}; - - struct digital_information : units::derived_dimension> {}; - - template - concept DigitalInformation = units::QuantityOf; - - struct data_prefix : units::prefix_type {}; - - struct kibi : units::prefix, "Ki"> {}; - struct mebi : units::prefix, "Mi"> {}; - - struct bit : units::named_coherent_derived_unit {}; - struct kilobit : units::prefixed_derived_unit {}; - struct byte : units::named_scaled_derived_unit, data_prefix> {}; - struct kilobyte : units::prefixed_derived_unit {}; - - inline namespace literals { - - constexpr auto operator""_b(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_Kib(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_B(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_KiB(unsigned long long l) { return units::quantity(l); } - - } - -} - -using namespace data; - -TEST_CASE("operator<< on a custom quantity", "[text][ostream]") +TEST_CASE("operator<< on a data quantity", "[text][ostream]") { std::stringstream stream; @@ -64,25 +34,25 @@ TEST_CASE("operator<< on a custom quantity", "[text][ostream]") { SECTION("named unit") { - stream << 64_B; + stream << 64B; REQUIRE(stream.str() == "64 B"); } SECTION("prefixed coherent unit") { - stream << 256_Kib; + stream << 256Kib; REQUIRE(stream.str() == "256 Kib"); } SECTION("prefixed non-coherent unit") { - stream << 1024_KiB; + stream << 1024KiB; REQUIRE(stream.str() == "1024 KiB"); } SECTION("other unit matching prefix") { - stream << 8_Kib * 8_Kib / 2_b; + stream << 8Kib * 8Kib / 2b; REQUIRE(stream.str() == "32 Mib"); } } diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index a214bb6f..b3123215 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -20,18 +20,20 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/dimensions/area.h" -#include "units/dimensions/frequency.h" -#include "units/dimensions/power.h" -#include "units/dimensions/velocity.h" -#include "units/dimensions/volume.h" -#include "units/dimensions/surface_tension.h" +#include "units/physical/si/area.h" +#include "units/physical/si/frequency.h" +#include "units/physical/si/power.h" +#include "units/physical/si/pressure.h" +#include "units/physical/si/velocity.h" +#include "units/physical/si/volume.h" +#include "units/physical/si/surface_tension.h" #include "units/format.h" #include "units/math.h" #include #include using namespace units; +using namespace units::si; using namespace Catch::Matchers; TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") @@ -108,7 +110,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") { SECTION("in terms of base units") { - const quantity>> q(123); + const length>> q(123); stream << q; SECTION("iostream") @@ -129,7 +131,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("in terms of derived units") { - const quantity>> q(60); + const energy>> q(60); stream << q; SECTION("iostream") @@ -242,8 +244,8 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("surface tension") { - struct newton_per_centimetre : deduced_derived_unit {}; - const quantity q(123); + struct newton_per_centimetre : deduced_unit {}; + const surface_tension q(123); stream << q; SECTION("iostream") @@ -636,17 +638,17 @@ TEST_CASE("format string with only %Q should print quantity value only", "[text] SECTION("nan") { - CHECK(fmt::format("{:%Q}", quantity(std::numeric_limits::quiet_NaN())) == "nan"); + CHECK(fmt::format("{:%Q}", length(std::numeric_limits::quiet_NaN())) == "nan"); } SECTION("inf") { - CHECK(fmt::format("{:%Q}", quantity(std::numeric_limits::infinity())) == "inf"); + CHECK(fmt::format("{:%Q}", length(std::numeric_limits::infinity())) == "inf"); } SECTION("-inf") { - CHECK(fmt::format("{:%Q}", quantity(-std::numeric_limits::infinity())) == "-inf"); + CHECK(fmt::format("{:%Q}", length(-std::numeric_limits::infinity())) == "-inf"); } } } @@ -742,8 +744,8 @@ TEST_CASE("fill and align specification", "[text][fmt]") TEST_CASE("sign specification", "[text][fmt]") { - quantity inf(std::numeric_limits::infinity()); - quantity nan(std::numeric_limits::quiet_NaN()); + length inf(std::numeric_limits::infinity()); + length nan(std::numeric_limits::quiet_NaN()); SECTION("default format {} on a quantity") { diff --git a/test/unit_test/runtime/fmt_units_test.cpp b/test/unit_test/runtime/fmt_units_test.cpp index 390f8cb7..1e721528 100644 --- a/test/unit_test/runtime/fmt_units_test.cpp +++ b/test/unit_test/runtime/fmt_units_test.cpp @@ -20,16 +20,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/dimensions/area.h" -#include "units/dimensions/frequency.h" -#include "units/dimensions/power.h" -#include "units/dimensions/velocity.h" -#include "units/dimensions/volume.h" -#include "units/dimensions/surface_tension.h" +#include "units/physical/si/area.h" +#include "units/physical/si/frequency.h" +#include "units/physical/si/power.h" +#include "units/physical/si/velocity.h" +#include "units/physical/si/volume.h" +#include "units/physical/si/surface_tension.h" #include "units/format.h" #include -using namespace units; +using namespace units::si; TEST_CASE("fmt::format on synthesized unit symbols", "[text][fmt]") { diff --git a/test/unit_test/runtime/math_test.cpp b/test/unit_test/runtime/math_test.cpp index 4e72e26b..b147e839 100644 --- a/test/unit_test/runtime/math_test.cpp +++ b/test/unit_test/runtime/math_test.cpp @@ -21,73 +21,35 @@ // SOFTWARE. #include "units/math.h" -#include "units/dimensions/area.h" -#include "units/dimensions/volume.h" +#include "units/physical/si/area.h" +#include "units/physical/si/volume.h" #include using namespace units; +using namespace units::si; // classical -TEST_CASE("pow() on quantity changes the value and the dimension accordingly", "[math][pow]") +TEST_CASE("'pow()' on quantity changes the value and the dimension accordingly", "[math][pow]") { - CHECK(pow<0>(2m) == 1); - CHECK(pow<1>(2m) == 2m); - CHECK(pow<2>(2m) == 4sq_m); - CHECK(pow<3>(2m) == 8cub_m); + SECTION ("'pow<0>(q)' returns '1'") { + CHECK(pow<0>(2m) == 1); + } + + SECTION ("'pow<1>(q)' returns 'q'") { + CHECK(pow<1>(2m) == 2m); + } + + SECTION ("'pow<2>(q)' squares both the value and a dimension") { + CHECK(pow<2>(2m) == 4sq_m); + } + + SECTION ("'pow<3>(q)' cubes both the value and a dimension") { + CHECK(pow<3>(2m) == 8cub_m); + } } -TEST_CASE("sqrt() on quantity changes the value and the dimension accordingly", "[math][sqrt]") +TEST_CASE("'sqrt()' on quantity changes the value and the dimension accordingly", "[math][sqrt]") { REQUIRE(sqrt(4sq_m) == 2m); } - -// BDD style - -SCENARIO("quantities should work with pow()", "[math][pow]") -{ - GIVEN("A quantity q") { - auto q = 2m; - - REQUIRE(q.count() == 2); - - WHEN("pow<1>(q) is called") { - auto res = pow<1>(q); - - THEN("the same quantity is received") { - REQUIRE(res == q); - } - } - WHEN("pow<2>(q) is called") { - auto res = pow<2>(q); - - THEN("both the value and dimension is raised to the exponent 2") { - REQUIRE(res == 4sq_m); - } - } - WHEN("pow<3>(q) is called") { - auto res = pow<3>(q); - - THEN("both the value and dimension is raised to the exponent 3") { - REQUIRE(res == 8cub_m); - } - } - } -} - -SCENARIO("quantities should work with sqrt()", "[math][sqrt]") -{ - GIVEN("A quantity q") { - auto q = 4sq_m; - - REQUIRE(q.count() == 4); - - WHEN("sqrt(q) is called") { - auto res = sqrt(q); - - THEN("both the value and dimension are square rooted") { - REQUIRE(res == 2m); - } - } - } -} diff --git a/test/unit_test/static/dimension_op_test.cpp b/test/unit_test/static/dimension_op_test.cpp index 6f94c6b5..900a8b02 100644 --- a/test/unit_test/static/dimension_op_test.cpp +++ b/test/unit_test/static/dimension_op_test.cpp @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/dimension_op.h" +#include "units/bits/dimension_op.h" #include "units/unit.h" #include diff --git a/test/unit_test/static/type_list_test.cpp b/test/unit_test/static/type_list_test.cpp index 6ee9688f..9c5bec90 100644 --- a/test/unit_test/static/type_list_test.cpp +++ b/test/unit_test/static/type_list_test.cpp @@ -20,7 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "units/bits/type_list.h" +#include "units/bits/external/type_list.h" #include "units/derived_dimension.h" #include "units/unit.h" #include diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index a23bee55..84dc5e10 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -53,6 +53,6 @@ static_assert(std::is_same_v Date: Wed, 11 Dec 2019 08:22:12 +0100 Subject: [PATCH 15/44] fmt and catch2 updated to the latest versions --- conanfile.py | 4 ++-- src/include/units/format.h | 15 +++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/conanfile.py b/conanfile.py index 5a2467b4..c79e860a 100644 --- a/conanfile.py +++ b/conanfile.py @@ -47,10 +47,10 @@ class UnitsConan(ConanFile): exports_sources = ["src/*", "test/*", "cmake/*", "example/*","CMakeLists.txt"] settings = "os", "compiler", "build_type", "arch" requires = ( - "fmt/6.0.0" + "fmt/6.1.0" ) build_requires = ( - "Catch2/2.10.0@catchorg/stable" + "Catch2/2.11.0@catchorg/stable" ) generators = "cmake" diff --git a/src/include/units/format.h b/src/include/units/format.h index 68c688cc..4b361a02 100644 --- a/src/include/units/format.h +++ b/src/include/units/format.h @@ -159,7 +159,7 @@ template struct fmt::formatter, CharT> { private: using quantity = units::quantity; - using iterator = fmt::basic_parse_context::iterator; + using iterator = fmt::basic_format_parse_context::iterator; using arg_ref_type = fmt::internal::arg_ref; fmt::basic_format_specs specs; @@ -172,7 +172,7 @@ private: struct spec_handler { formatter& f; - fmt::basic_parse_context& context; + fmt::basic_format_parse_context& context; fmt::basic_string_view format_str; template @@ -185,8 +185,7 @@ private: constexpr arg_ref_type make_arg_ref(fmt::basic_string_view arg_id) { context.check_arg_id(arg_id); - const auto str_val = fmt::internal::string_view_metadata(format_str, arg_id); - return arg_ref_type(str_val); + return arg_ref_type(arg_id); } constexpr arg_ref_type make_arg_ref(fmt::internal::auto_id) @@ -226,7 +225,7 @@ private: iterator end; }; - constexpr parse_range do_parse(fmt::basic_parse_context& ctx) + constexpr parse_range do_parse(fmt::basic_format_parse_context& ctx) { auto begin = ctx.begin(), end = ctx.end(); if(begin == end || *begin == '}') @@ -285,7 +284,7 @@ private: } public: - constexpr auto parse(fmt::basic_parse_context& ctx) + constexpr auto parse(fmt::basic_format_parse_context& ctx) { auto range = do_parse(ctx); format_str = fmt::basic_string_view(&*range.begin, fmt::internal::to_unsigned(range.end - range.begin)); @@ -302,8 +301,8 @@ public: auto out = std::back_inserter(buf); // process dynamic width and precision - fmt::internal::handle_dynamic_spec(specs.width, width_ref, ctx, format_str.begin()); - fmt::internal::handle_dynamic_spec(precision, precision_ref, ctx, format_str.begin()); + fmt::internal::handle_dynamic_spec(specs.width, width_ref, ctx); + fmt::internal::handle_dynamic_spec(precision, precision_ref, ctx); // deal with quantity content if(begin == end || *begin == '}') { From 9bebf316a77f2e85d7607f0ee61eace259a32803 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 08:27:12 +0100 Subject: [PATCH 16/44] gcc-9.1 build fixed --- src/include/units/base_dimension.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/include/units/base_dimension.h b/src/include/units/base_dimension.h index 2df37dc4..bdf19e77 100644 --- a/src/include/units/base_dimension.h +++ b/src/include/units/base_dimension.h @@ -57,12 +57,21 @@ struct base_dimension { // BaseDimension namespace detail { +#if __GNUC__ == 9 && __GNUC_MINOR__ < 2 + +template +inline constexpr bool is_base_dimension = true; + +#else + template inline constexpr bool is_base_dimension = false; template inline constexpr bool is_base_dimension> = true; +#endif + } // namespace detail template From 62164e426aea33fa04832e5e8328fbd93e1e76db Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 08:34:16 +0100 Subject: [PATCH 17/44] conan test_package fixed --- test_package/test_package.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test_package/test_package.cpp b/test_package/test_package.cpp index 16d5c493..a8a30cd9 100644 --- a/test_package/test_package.cpp +++ b/test_package/test_package.cpp @@ -20,18 +20,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include +#include #include -using namespace units::literals; - -template -constexpr units::Velocity avg_speed(D d, T t) +constexpr units::Velocity AUTO avg_speed(units::Length AUTO d, units::Time AUTO t) { return d / t; } int main() { + using namespace units::si::literals; std::cout << "Average speed = " << avg_speed(240.km, 2h) << '\n'; } From 43047474065c647b21d4a14edc2f193b495611f3 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 09:27:17 +0100 Subject: [PATCH 18/44] -pedantic warning flag reenabled --- conanfile.py | 3 +-- src/CMakeLists.txt | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/conanfile.py b/conanfile.py index c79e860a..56035345 100644 --- a/conanfile.py +++ b/conanfile.py @@ -96,8 +96,7 @@ class UnitsConan(ConanFile): "-fconcepts", "-Wno-literal-suffix", "-Wno-non-template-friend", - "-Wno-stringop-overflow", - "-Wno-pedantic" + "-Wno-stringop-overflow" ] def package_id(self): diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a8d69659..2352b3c3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -66,8 +66,6 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) -Wno-literal-suffix -Wno-non-template-friend -Wno-stringop-overflow -# TODO gcc:92101 - -Wno-pedantic ) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0) target_link_libraries(units From c1e4e57d84a5cd4786764bfdd9b7bf66d2ab6902 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 09:28:40 +0100 Subject: [PATCH 19/44] clang-format + TODOs added to ratio --- src/include/units/ratio.h | 311 ++++++++++++++++++++------------------ 1 file changed, 160 insertions(+), 151 deletions(-) diff --git a/src/include/units/ratio.h b/src/include/units/ratio.h index 80e964f9..6e0700a0 100644 --- a/src/include/units/ratio.h +++ b/src/include/units/ratio.h @@ -23,177 +23,186 @@ #pragma once #include -#include -#include #include +#include +#include namespace units { - namespace detail { +namespace detail { - template - [[nodiscard]] constexpr T abs(T v) noexcept { return v < 0 ? -v : v; } +template +[[nodiscard]] constexpr T abs(T v) noexcept +{ + return v < 0 ? -v : v; +} - } +} // namespace detail - template - requires (Den != 0) - struct ratio { - static_assert(-INTMAX_MAX <= Num, "numerator too negative"); - static_assert(-INTMAX_MAX <= Den, "denominator too negative"); +template + requires(Den != 0) +struct ratio { + static_assert(-INTMAX_MAX <= Num, "numerator too negative"); + static_assert(-INTMAX_MAX <= Den, "denominator too negative"); - static constexpr std::intmax_t num = Num * (Den < 0 ? -1 : 1) / std::gcd(Num, Den); - static constexpr std::intmax_t den = detail::abs(Den) / std::gcd(Num, Den); + static constexpr std::intmax_t num = Num * (Den < 0 ? -1 : 1) / std::gcd(Num, Den); + static constexpr std::intmax_t den = detail::abs(Den) / std::gcd(Num, Den); - using type = ratio; - }; + using type = ratio; +}; - // is_ratio +// is_ratio - namespace detail { +namespace detail { - template - inline constexpr bool is_ratio = false; +template +inline constexpr bool is_ratio = false; - template - inline constexpr bool is_ratio> = true; +template +inline constexpr bool is_ratio> = true; - } // namespace detail +} // namespace detail - template - concept Ratio = detail::is_ratio; - - // ratio_multiply - - namespace detail { - - static constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs) - { - constexpr std::uintmax_t c = std::uintmax_t(1) << (sizeof(std::intmax_t) * 4); - - const std::uintmax_t a0 = detail::abs(lhs) % c; - const std::uintmax_t a1 = detail::abs(lhs) / c; - const std::uintmax_t b0 = detail::abs(rhs) % c; - const std::uintmax_t b1 = detail::abs(rhs) / c; - - Expects(a1 == 0 || b1 == 0); // overflow in multiplication - Expects(a0 * b1 + b0 * a1 < (c >> 1)); // overflow in multiplication - Expects(b0 * a0 <= INTMAX_MAX); // overflow in multiplication - Expects((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); // overflow in multiplication - - return lhs * rhs; - } - - template - struct ratio_multiply_impl { - private: - static constexpr std::intmax_t gcd1 = std::gcd(R1::num, R2::den); - static constexpr std::intmax_t gcd2 = std::gcd(R2::num, R1::den); - - public: - using type = ratio; - static constexpr std::intmax_t num = type::num; - static constexpr std::intmax_t den = type::den; - }; - - } - - template - using ratio_multiply = detail::ratio_multiply_impl::type; - - // ratio_divide - - namespace detail { - - template - struct ratio_divide_impl { - static_assert(R2::num != 0, "division by 0"); - using type = ratio_multiply>; - static constexpr std::intmax_t num = type::num; - static constexpr std::intmax_t den = type::den; - }; - - } - - template - using ratio_divide = detail::ratio_divide_impl::type; - - // ratio_pow - - namespace detail { - - template - struct ratio_pow_impl { - using type = ratio_multiply::type, R>; - }; - - template - struct ratio_pow_impl { - using type = R; - }; - - template - struct ratio_pow_impl { - using type = ratio<1>; - }; - - } - - template - using ratio_pow = detail::ratio_pow_impl::type; - - // ratio_sqrt - - namespace detail { - - constexpr std::intmax_t sqrt_impl(std::intmax_t v, std::intmax_t l, std::intmax_t r) - { - if(l == r) - return r; - - const auto mid = (r + l) / 2; - if(mid * mid >= v) - return sqrt_impl(v, l, mid); - else - return sqrt_impl(v, mid + 1, r); - } - - static constexpr std::intmax_t sqrt_impl(std::intmax_t v) - { - return sqrt_impl(v, 1, v); - } - - template - struct ratio_sqrt_impl { - using type = ratio; - }; - - template - struct ratio_sqrt_impl> { - using type = ratio<0>; - }; - - } - - template - using ratio_sqrt = detail::ratio_sqrt_impl::type; +template +concept Ratio = detail::is_ratio; - // common_ratio +// ratio_add +// TODO implement ratio_add +// template +// using ratio_add = detail::ratio_add_impl::type; - namespace detail { +// ratio_subtract +// TODO implement ratio_subtract +// template +// using ratio_subtract = detail::ratio_subtract_impl::type; - // TODO: simplified - template - struct common_ratio_impl { - static constexpr std::intmax_t gcd_num = std::gcd(R1::num, R2::num); - static constexpr std::intmax_t gcd_den = std::gcd(R1::den, R2::den); - using type = ratio; - }; +// ratio_multiply - } +namespace detail { - template - using common_ratio = detail::common_ratio_impl::type; +static constexpr std::intmax_t safe_multiply(std::intmax_t lhs, std::intmax_t rhs) +{ + constexpr std::uintmax_t c = std::uintmax_t(1) << (sizeof(std::intmax_t) * 4); + + const std::uintmax_t a0 = detail::abs(lhs) % c; + const std::uintmax_t a1 = detail::abs(lhs) / c; + const std::uintmax_t b0 = detail::abs(rhs) % c; + const std::uintmax_t b1 = detail::abs(rhs) / c; + + Expects(a1 == 0 || b1 == 0); // overflow in multiplication + Expects(a0 * b1 + b0 * a1 < (c >> 1)); // overflow in multiplication + Expects(b0 * a0 <= INTMAX_MAX); // overflow in multiplication + Expects((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); // overflow in multiplication + + return lhs * rhs; +} + +template +struct ratio_multiply_impl { +private: + static constexpr std::intmax_t gcd1 = std::gcd(R1::num, R2::den); + static constexpr std::intmax_t gcd2 = std::gcd(R2::num, R1::den); + +public: + using type = ratio; + static constexpr std::intmax_t num = type::num; + static constexpr std::intmax_t den = type::den; +}; + +} // namespace detail + +template +using ratio_multiply = detail::ratio_multiply_impl::type; + +// ratio_divide + +namespace detail { + +template +struct ratio_divide_impl { + static_assert(R2::num != 0, "division by 0"); + using type = ratio_multiply>; + static constexpr std::intmax_t num = type::num; + static constexpr std::intmax_t den = type::den; +}; + +} // namespace detail + +template +using ratio_divide = detail::ratio_divide_impl::type; + +// ratio_pow + +namespace detail { + +template +struct ratio_pow_impl { + using type = ratio_multiply::type, R>; +}; + +template +struct ratio_pow_impl { + using type = R; +}; + +template +struct ratio_pow_impl { + using type = ratio<1>; +}; + +} // namespace detail + +template +using ratio_pow = detail::ratio_pow_impl::type; + +// ratio_sqrt + +namespace detail { + +constexpr std::intmax_t sqrt_impl(std::intmax_t v, std::intmax_t l, std::intmax_t r) +{ + if (l == r) return r; + + const auto mid = (r + l) / 2; + if (mid * mid >= v) + return sqrt_impl(v, l, mid); + else + return sqrt_impl(v, mid + 1, r); +} + +static constexpr std::intmax_t sqrt_impl(std::intmax_t v) { return sqrt_impl(v, 1, v); } + +template +struct ratio_sqrt_impl { + using type = ratio; +}; + +template +struct ratio_sqrt_impl> { + using type = ratio<0>; +}; + +} // namespace detail + +template +using ratio_sqrt = detail::ratio_sqrt_impl::type; + +// common_ratio + +namespace detail { + +// TODO: simplified +template +struct common_ratio_impl { + static constexpr std::intmax_t gcd_num = std::gcd(R1::num, R2::num); + static constexpr std::intmax_t gcd_den = std::gcd(R1::den, R2::den); + using type = ratio; +}; + +} // namespace detail + +template +using common_ratio = detail::common_ratio_impl::type; } // namespace units From 4f769982a5bf600f1e804b388276dc21befefe20 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 09:32:29 +0100 Subject: [PATCH 20/44] TODO removed --- src/include/units/unit_of_concept.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/include/units/unit_of_concept.h b/src/include/units/unit_of_concept.h index 143d4678..c39a4016 100644 --- a/src/include/units/unit_of_concept.h +++ b/src/include/units/unit_of_concept.h @@ -52,6 +52,5 @@ concept UnitOf = Unit && Dimension && std::same_as::reference>; - // same_unit_reference> // TODO check if this works } // namespace units From e472fdc348075e4127cd63bdd2f7702928590248 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 10:44:55 +0100 Subject: [PATCH 21/44] Most of the operators converted to hidden friends --- src/include/units/quantity.h | 198 ++++++++++++++++++----------------- 1 file changed, 100 insertions(+), 98 deletions(-) diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 0cf29bbf..5381907f 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -439,6 +439,102 @@ public: return *this; } + template + [[nodiscard]] friend constexpr Quantity AUTO operator+(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic + { + using common_rep = decltype(lhs.count() + rhs.count()); + using ret = common_quantity, common_rep>; + return ret(ret(lhs).count() + ret(rhs).count()); + } + + template + [[nodiscard]] friend constexpr Quantity AUTO operator-(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic + { + using common_rep = decltype(lhs.count() - rhs.count()); + using ret = common_quantity, common_rep>; + return ret(ret(lhs).count() - ret(rhs).count()); + } + + template + [[nodiscard]] friend constexpr Quantity AUTO operator%(const quantity& q, const Value& v) + requires (!treat_as_floating_point) && + (!treat_as_floating_point) && + std::magma + { + using common_rep = decltype(q.count() % v); + using ret = quantity; + return ret(q.count() % v); + } + + template + [[nodiscard]] friend constexpr Quantity AUTO operator%(const quantity& lhs, const quantity& rhs) + requires (!treat_as_floating_point) && + (!treat_as_floating_point) && + std::magma + { + using common_rep = decltype(lhs.count() % rhs.count()); + using ret = common_quantity, common_rep>; + return ret(ret(lhs).count() % ret(rhs).count()); + } + + template + [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::equality_comparable_with + { + using cq = common_quantity>; + return cq(lhs).count() == cq(rhs).count(); + } + + template + [[nodiscard]] friend constexpr bool operator!=(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::equality_comparable_with + { + return !(lhs == rhs); + } + + template + [[nodiscard]] friend constexpr bool operator<(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::totally_ordered_with + { + using cq = common_quantity>; + return cq(lhs).count() < cq(rhs).count(); + } + + template + [[nodiscard]] friend constexpr bool operator<=(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::totally_ordered_with + { + return !(rhs < lhs); + } + + template + [[nodiscard]] friend constexpr bool operator>(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::totally_ordered_with + { + return rhs < lhs; + } + + template + [[nodiscard]] friend constexpr bool operator>=(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::totally_ordered_with + { + return !(lhs < rhs); + } + template friend std::basic_ostream& operator<<(std::basic_ostream& os, const quantity& q) { @@ -446,23 +542,7 @@ public: } }; -template -[[nodiscard]] constexpr Quantity AUTO operator+(const quantity& lhs, const quantity& rhs) - requires detail::basic_arithmetic -{ - using common_rep = decltype(lhs.count() + rhs.count()); - using ret = common_quantity, quantity, common_rep>; - return ret(ret(lhs).count() + ret(rhs).count()); -} - -template -[[nodiscard]] constexpr Quantity AUTO operator-(const quantity& lhs, const quantity& rhs) - requires detail::basic_arithmetic -{ - using common_rep = decltype(lhs.count() - rhs.count()); - using ret = common_quantity, quantity, common_rep>; - return ret(ret(lhs).count() - ret(rhs).count()); -} +// TODO make hidden friends when moved to gcc-10 template [[nodiscard]] constexpr Quantity AUTO operator*(const quantity& q, const Value& v) @@ -510,7 +590,7 @@ template [[nodiscard]] constexpr Quantity AUTO operator/(const Value& v, const quantity& q) requires std::magma { - Expects(q != std::remove_cvref_t(0)); + Expects(q.count() != 0); using dim = dim_invert; using ratio = ratio; @@ -535,7 +615,7 @@ template& lhs, const quantity& rhs) requires detail::basic_arithmetic && equivalent_dim { - Expects(rhs != std::remove_cvref_t(0)); + Expects(rhs.count() != 0); using common_rep = decltype(lhs.count() / rhs.count()); using cq = common_quantity, quantity, common_rep>; @@ -548,7 +628,7 @@ template || (ratio_divide::den == 1)) { - Expects(rhs != std::remove_cvref_t(0)); + Expects(rhs.count() != 0); using common_rep = decltype(lhs.count() / rhs.count()); using dim = dimension_divide; @@ -560,82 +640,4 @@ template -[[nodiscard]] constexpr Quantity AUTO operator%(const quantity& q, const Value& v) - requires (!treat_as_floating_point) && - (!treat_as_floating_point) && - std::magma -{ - using common_rep = decltype(q.count() % v); - using ret = quantity; - return ret(q.count() % v); -} - -template -[[nodiscard]] constexpr Quantity AUTO operator%(const quantity& lhs, const quantity& rhs) - requires (!treat_as_floating_point) && - (!treat_as_floating_point) && - std::magma -{ - using common_rep = decltype(lhs.count() % rhs.count()); - using ret = common_quantity, quantity, common_rep>; - return ret(ret(lhs).count() % ret(rhs).count()); -} - -template -[[nodiscard]] constexpr bool operator==(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && - detail::basic_arithmetic && - std::equality_comparable_with -{ - using cq = common_quantity, quantity>; - return cq(lhs).count() == cq(rhs).count(); -} - -template -[[nodiscard]] constexpr bool operator!=(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && - detail::basic_arithmetic && - std::equality_comparable_with -{ - return !(lhs == rhs); -} - -template -[[nodiscard]] constexpr bool operator<(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && - detail::basic_arithmetic && - std::totally_ordered_with -{ - using cq = common_quantity, quantity>; - return cq(lhs).count() < cq(rhs).count(); -} - -template -[[nodiscard]] constexpr bool operator<=(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && - detail::basic_arithmetic && - std::totally_ordered_with -{ - return !(rhs < lhs); -} - -template -[[nodiscard]] constexpr bool operator>(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && - detail::basic_arithmetic && - std::totally_ordered_with -{ - return rhs < lhs; -} - -template -[[nodiscard]] constexpr bool operator>=(const quantity& lhs, const quantity& rhs) - requires equivalent_dim && - detail::basic_arithmetic && - std::totally_ordered_with -{ - return !(lhs < rhs); -} - } // namespace units From c3c80fde11305b0da919018fb78bcd5893cbd272 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 11:27:51 +0100 Subject: [PATCH 22/44] Spaceship support added (resolves #19) --- src/include/units/quantity.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 5381907f..90b57881 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -25,6 +25,9 @@ #include #include #include +#if __GNUC__ >= 10 +#include +#endif #include #include @@ -479,6 +482,31 @@ public: return ret(ret(lhs).count() % ret(rhs).count()); } +#if __GNUC__ >= 10 + + template + [[nodiscard]] friend constexpr auto operator<=>(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::totally_ordered_with + { + using cq = common_quantity>; + return cq(lhs).count() <=> cq(rhs).count(); + } + + // TODO op== not needed (gcc bug) + template + [[nodiscard]] friend constexpr auto operator==(const quantity& lhs, const quantity& rhs) + requires equivalent_dim && + detail::basic_arithmetic && + std::equality_comparable_with + { + using cq = common_quantity>; + return cq(lhs).count() == cq(rhs).count(); + } + +#else + template [[nodiscard]] friend constexpr bool operator==(const quantity& lhs, const quantity& rhs) requires equivalent_dim && @@ -535,6 +563,8 @@ public: return !(lhs < rhs); } +#endif + template friend std::basic_ostream& operator<<(std::basic_ostream& os, const quantity& q) { From 5a7324596d54f2dea83cfb4437dc9c78c478d91b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 13:31:40 +0100 Subject: [PATCH 23/44] Resulting unit ratio fixed in arithmetic operations --- src/include/units/quantity.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 90b57881..7241cc78 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -609,7 +609,7 @@ template; using ratio1 = ratio_divide::ratio>; using ratio2 = ratio_divide::ratio>; - using ratio = ratio_multiply; + using ratio = ratio_multiply, typename dimension_unit::ratio>; using unit = downcast_unit; using common_rep = decltype(lhs.count() * rhs.count()); using ret = quantity; @@ -664,7 +664,7 @@ template; using ratio1 = ratio_divide::ratio>; using ratio2 = ratio_divide::ratio>; - using ratio = ratio_divide; + using ratio = ratio_multiply, typename dimension_unit::ratio>; using unit = downcast_unit; using ret = quantity; return ret(lhs.count() / rhs.count()); From a10660d06568ab2c6e944cee9c85f7b53cbaf8db Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 13:55:58 +0100 Subject: [PATCH 24/44] More SI tests added --- test/unit_test/static/si_test.cpp | 242 ++++++++++++++++++------------ 1 file changed, 149 insertions(+), 93 deletions(-) diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp index c31d5886..6179f669 100644 --- a/test/unit_test/static/si_test.cpp +++ b/test/unit_test/static/si_test.cpp @@ -44,150 +44,206 @@ namespace { - using namespace units; - using namespace units::si; +using namespace units; +using namespace units::si; - /* ************** BASE DIMENSIONS **************** */ +/* ************** BASE DIMENSIONS **************** */ - // length +// length - 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); +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); - static_assert(1yd == 0.9144m); - static_assert(1yd == 3ft); - static_assert(1ft == 12in); - static_assert(1mi == 1760yd); +static_assert(1yd == 0.9144m); +static_assert(1yd == 3ft); +static_assert(1ft == 12in); +static_assert(1mi == 1760yd); - static_assert(5in + 8cm == 207mm); +static_assert(5in + 8cm == 207mm); - static_assert(millimetre::symbol == "mm"); - static_assert(centimetre::symbol == "cm"); - static_assert(kilometre::symbol == "km"); +static_assert(millimetre::symbol == "mm"); +static_assert(centimetre::symbol == "cm"); +static_assert(kilometre::symbol == "km"); - // mass +// mass - static_assert(1kg == 1000g); +static_assert(1kg == 1000g); - static_assert(kilogram::symbol == "kg"); +static_assert(kilogram::symbol == "kg"); - // time +// time - static_assert(1h == 3600s); +static_assert(1us == 1000ns); +static_assert(1ms == 1000us); +static_assert(1s == 1000ms); +static_assert(1min == 60s); +static_assert(1h == 3600s); - static_assert(nanosecond::symbol == "ns"); - static_assert(microsecond::symbol == "µs"); - static_assert(millisecond::symbol == "ms"); +static_assert(nanosecond::symbol == "ns"); +static_assert(microsecond::symbol == "µs"); +static_assert(millisecond::symbol == "ms"); - // current +// current - // temperature +// temperature - // substance +// substance - // luminous intensity +// luminous intensity +/* ************** DERIVED DIMENSIONS WITH NAMED UNITS **************** */ - /* ************** DERIVED DIMENSIONS WITH NAMED UNITS **************** */ +// frequency - // frequency +static_assert(1000mHz == 1Hz); +static_assert(1000Hz == 1kHz); +static_assert(1000kHz == 1MHz); +static_assert(1000MHz == 1GHz); +static_assert(1000GHz == 1THz); - 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(2 / 1Hz == 2s); +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"); - 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"); +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(2 / 1Hz == 2s); - // force - static_assert(10kg * 10mps_sq == 100N); +// force +static_assert(10kg * 10mps_sq == 100N); +static_assert(100N / 1mps_sq == 100kg); +static_assert(100.N / 1kg == 100mps_sq); - // pressure +// pressure - static_assert(10N / 10sq_m == 1Pa); +static_assert(10N / 10sq_m == 1Pa); - // energy +// energy - static_assert(10N * 10m == 100_J); - static_assert(10Pa * 10cub_m == 100_J); +static_assert(1000mJ == 1_J); +static_assert(1000_J == 1kJ); +static_assert(1000kJ == 1MJ); +static_assert(1000MJ == 1GJ); - // power +static_assert(millijoule::symbol == "mJ"); +static_assert(kilojoule::symbol == "kJ"); +static_assert(megajoule::symbol == "MJ"); +static_assert(gigajoule::symbol == "GJ"); - static_assert(10_J / 10s == 1W); +static_assert(10N * 10m == 100_J); +static_assert(100_J / 10m == 10N); +static_assert(100_J / 10N == 10m); +static_assert(10Pa * 10cub_m == 100_J); +static_assert(100_J / 10Pa == 10cub_m); +static_assert(100_J / 10cub_m == 10Pa); - // electric charge +// power - static_assert(10A * 10s == 100C); +static_assert(1000mW == 1W); +static_assert(1000W == 1kW); +static_assert(1000kW == 1MW); +static_assert(1000MW == 1GW); - // voltage +static_assert(milliwatt::symbol == "mW"); +static_assert(kilowatt::symbol == "kW"); +static_assert(megawatt::symbol == "MW"); +static_assert(gigawatt::symbol == "GW"); - static_assert(10W / 10A == 1V); - static_assert(10_J / 10C == 1V); +static_assert(10_J / 10s == 1W); +static_assert(1W * 10s == 10_J); +static_assert(10_J / 1W == 10s); - // capacitance +// electric charge - static_assert(10C / 10V == 1F); +static_assert(10A * 10s == 100C); +static_assert(100C / 10A == 10s); +static_assert(100C / 10s == 10A); - /* ************** DERIVED DIMENSIONS IN TERMS OF BASE UNITS **************** */ +// voltage - // velocity +static_assert(10W / 10A == 1V); +static_assert(10W / 1V == 10A); +static_assert(1V * 10A == 10W); +static_assert(10_J / 10C == 1V); +static_assert(10_J / 1V == 10C); +static_assert(10C * 1V == 10_J); - static_assert(std::is_same_v>, std::int64_t>>); +// capacitance - 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(10C / 10V == 1F); +static_assert(10C / 1F == 10V); +static_assert(10V * 1F == 10C); - static_assert(10.0mi / 2h == 5mph); +/* ************** DERIVED DIMENSIONS IN TERMS OF BASE UNITS **************** */ - 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); +// velocity - static_assert(2km / 2kmph == 1h); - // static_assert(2000m / 2kmph == 1h); // should not compile - static_assert(quantity_cast>(2000m) / 2kmph == 1h); +static_assert(std::is_same_v>, std::int64_t>>); -// static_assert(metre_per_second::symbol == basic_fixed_string("m/s")); - // static_assert(kilometre_per_hour::symbol == basic_fixed_string("km/h")); +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); - // acceleration +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(10mps / 10s == 1mps_sq); +static_assert(2km / 2kmph == 1h); +// static_assert(2000m / 2kmph == 1h); // should not compile +static_assert(quantity_cast(2000m) / 2kmph == 1h); - // area +static_assert(detail::unit_text() == "m/s"); +static_assert(kilometre_per_hour::symbol == "km/h"); +static_assert(mile_per_hour::symbol == "mi/h"); - static_assert(1m * 1m == 1sq_m); - static_assert(10km * 10km == 100sq_km); - static_assert(1sq_m == 10'000sq_cm); +// acceleration - // volume +static_assert(10mps / 10s == 1mps_sq); +static_assert(10mps / 1mps_sq == 10s); +static_assert(1mps_sq * 10s == 10mps); - 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); +static_assert(detail::unit_text() == "m/s²"); +// area - /* ************** DERIVED DIMENSIONS IN TERMS OF OTHER UNITS **************** */ +static_assert(1m * 1m == 1sq_m); +static_assert(10km * 10km == 100sq_km); +static_assert(1sq_m == 10'000sq_cm); - static_assert(10N / 2m == 5Npm); +static_assert(detail::unit_text() == "m²"); + +// 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); + +static_assert(detail::unit_text() == "m³"); + +/* ************** DERIVED DIMENSIONS IN TERMS OF OTHER UNITS **************** */ + +static_assert(10N / 2m == 5Npm); +static_assert(10N / 5Npm == 2m); +static_assert(2m * 5Npm == 10N); + +static_assert(detail::unit_text() == "N/m"); } // namespace From 71f38222ea190005e32c4f750cf31610b8444ed2 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 16:16:05 +0100 Subject: [PATCH 25/44] Implicit conversions fixed --- src/include/units/bits/unit_op.h | 39 ++++++++++++++++++++++++++++++++ src/include/units/quantity.h | 15 ++++++------ 2 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 src/include/units/bits/unit_op.h diff --git a/src/include/units/bits/unit_op.h b/src/include/units/bits/unit_op.h new file mode 100644 index 00000000..a4a1527a --- /dev/null +++ b/src/include/units/bits/unit_op.h @@ -0,0 +1,39 @@ +// 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::detail { + +template::ratio>, + typename Ratio2 = ratio_divide::ratio>> +using unit_ratio_multiply = ratio_multiply; + +template::ratio>, + typename Ratio2 = ratio_divide::ratio>> +using unit_ratio_divide = ratio_divide; + +} // namespace units::detail diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 7241cc78..5f665e7f 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -24,10 +24,13 @@ #include #include +#include #include + #if __GNUC__ >= 10 #include #endif + #include #include @@ -603,13 +606,11 @@ template& lhs, const quantity& rhs) requires (!equivalent_dim>) && // TODO equivalent_derived_dim? (treat_as_floating_point || - (std::ratio_multiply::den == 1)) && + detail::unit_ratio_multiply::den == 1) && detail::basic_arithmetic { using dim = dimension_multiply; - using ratio1 = ratio_divide::ratio>; - using ratio2 = ratio_divide::ratio>; - using ratio = ratio_multiply, typename dimension_unit::ratio>; + using ratio = ratio_multiply, typename dimension_unit::ratio>; ; using unit = downcast_unit; using common_rep = decltype(lhs.count() * rhs.count()); using ret = quantity; @@ -656,15 +657,13 @@ template& lhs, const quantity& rhs) requires detail::basic_arithmetic && (!equivalent_dim) && // TODO equivalent_derived_dim? (treat_as_floating_point || - (ratio_divide::den == 1)) + detail::unit_ratio_divide::den == 1) { Expects(rhs.count() != 0); using common_rep = decltype(lhs.count() / rhs.count()); using dim = dimension_divide; - using ratio1 = ratio_divide::ratio>; - using ratio2 = ratio_divide::ratio>; - using ratio = ratio_multiply, typename dimension_unit::ratio>; + using ratio = ratio_multiply, typename dimension_unit::ratio>; using unit = downcast_unit; using ret = quantity; return ret(lhs.count() / rhs.count()); From 12b67923bc45fe89ff84a43aabf2dc287412e88a Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 16:20:08 +0100 Subject: [PATCH 26/44] CGS tests added --- src/include/units/physical/cgs/area.h | 8 ++ src/include/units/physical/cgs/length.h | 4 +- src/include/units/physical/cgs/mass.h | 4 +- src/include/units/physical/si/mass.h | 12 +-- test/unit_test/static/CMakeLists.txt | 1 + test/unit_test/static/cgs_test.cpp | 125 ++++++++++++------------ test/unit_test/static/si_cgs_test.cpp | 111 +++++++++++++++++++++ test/unit_test/static/si_test.cpp | 7 +- 8 files changed, 198 insertions(+), 74 deletions(-) create mode 100644 test/unit_test/static/si_cgs_test.cpp diff --git a/src/include/units/physical/cgs/area.h b/src/include/units/physical/cgs/area.h index 4eac9f6a..c9618a53 100644 --- a/src/include/units/physical/cgs/area.h +++ b/src/include/units/physical/cgs/area.h @@ -36,4 +36,12 @@ struct dim_area : physical::dim_area {} template using area = quantity; +inline namespace literals { + +// sq_cm +constexpr auto operator"" sq_cm(unsigned long long l) { return area(l); } +constexpr auto operator"" sq_cm(long double l) { return area(l); } + +} + } // namespace units::cgs diff --git a/src/include/units/physical/cgs/length.h b/src/include/units/physical/cgs/length.h index 3b672ecd..a49a4cb8 100644 --- a/src/include/units/physical/cgs/length.h +++ b/src/include/units/physical/cgs/length.h @@ -37,7 +37,9 @@ using length = quantity; inline namespace literals { -using si::literals::operator"" cm; +// cm +constexpr auto operator"" cm(unsigned long long l) { return length(l); } +constexpr auto operator"" cm(long double l) { return length(l); } } diff --git a/src/include/units/physical/cgs/mass.h b/src/include/units/physical/cgs/mass.h index c58d1ca1..d809eb32 100644 --- a/src/include/units/physical/cgs/mass.h +++ b/src/include/units/physical/cgs/mass.h @@ -37,7 +37,9 @@ using mass = quantity; inline namespace literals { -using si::literals::operator"" g; +// g +constexpr auto operator""g(unsigned long long l) { return mass(l); } +constexpr auto operator""g(long double l) { return mass(l); } } diff --git a/src/include/units/physical/si/mass.h b/src/include/units/physical/si/mass.h index c6c59d15..7af3df31 100644 --- a/src/include/units/physical/si/mass.h +++ b/src/include/units/physical/si/mass.h @@ -38,13 +38,13 @@ using mass = quantity; inline namespace literals { - // g - constexpr auto operator""g(unsigned long long l) { return mass(l); } - constexpr auto operator""g(long double l) { return mass(l); } +// g +constexpr auto operator""g(unsigned long long l) { return mass(l); } +constexpr auto operator""g(long double l) { return mass(l); } - // kg - constexpr auto operator""kg(unsigned long long l) { return mass(l); } - constexpr auto operator""kg(long double l) { return mass(l); } +// kg +constexpr auto operator""kg(unsigned long long l) { return mass(l); } +constexpr auto operator""kg(long double l) { return mass(l); } } // namespace literals diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index 4b39dc76..790ee380 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -30,6 +30,7 @@ add_library(unit_tests_static quantity_test.cpp ratio_test.cpp si_test.cpp + si_cgs_test.cpp type_list_test.cpp unit_test.cpp ) diff --git a/test/unit_test/static/cgs_test.cpp b/test/unit_test/static/cgs_test.cpp index 07348f4e..e58bb4d4 100644 --- a/test/unit_test/static/cgs_test.cpp +++ b/test/unit_test/static/cgs_test.cpp @@ -30,77 +30,74 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include namespace { using namespace units; +using namespace units::cgs; -static_assert(cgs::length(100) == si::length(1)); -static_assert(cgs::mass(1'000) == si::mass(1)); -static_assert(cgs::time(1) == si::time(1)); -static_assert(cgs::velocity(100) == si::velocity(1)); -static_assert(cgs::acceleration(100) == si::acceleration(1)); -static_assert(cgs::force(100'000) == si::force(1)); -static_assert(cgs::energy(10'000'000) == si::energy(1)); -static_assert(cgs::power(10'000'000) == si::power(1)); -static_assert(cgs::pressure(10) == si::pressure(1)); +/* ************** BASE DIMENSIONS **************** */ -namespace si_test { +// length -using namespace units::si::literals; +static_assert(centimetre::symbol == "cm"); -static_assert(cgs::length(100) == 1m); -static_assert(cgs::mass(1'000) == 1kg); -static_assert(cgs::time(1) == 1s); -static_assert(cgs::velocity(100) == 1mps); -static_assert(cgs::acceleration(100) == 1mps_sq); -static_assert(cgs::force(100'000) == 1N); -static_assert(cgs::energy(10'000'000) == 1_J); -static_assert(cgs::power(10'000'000) == 1W); -static_assert(cgs::pressure(10) == 1Pa); - -} - -namespace cgs_test { - -using namespace units::cgs::literals; - -static_assert(100cm == si::length(1)); -static_assert(1'000g == si::mass(1)); -static_assert(1s == si::time(1)); -static_assert(100cmps == si::velocity(1)); -static_assert(100Gal == si::acceleration(1)); -static_assert(100'000dyn == si::force(1)); -static_assert(10'000'000_erg == si::energy(1)); -static_assert(10'000'000_ergps == si::power(1)); -static_assert(10Ba == si::pressure(1)); - -} - -namespace both_test { - -using namespace units::si::literals; -using namespace units::cgs::literals; - -static_assert(100cm == 1m); -static_assert(1'000g == 1kg); -static_assert(1s == 1s); -static_assert(100cmps == 1mps); -static_assert(100Gal == 1mps_sq); -static_assert(100'000dyn == 1N); -static_assert(10'000'000_erg == 1_J); -static_assert(10'000'000_ergps == 1W); -static_assert(10Ba == quantity_cast(1Pa)); - -} +// mass + +// time + +/* ************** DERIVED DIMENSIONS IN TERMS OF BASE UNITS **************** */ + +// velocity + +static_assert(10cm / 5s == 2cmps); +static_assert(10cm / 2cmps == 5s); +static_assert(10cm == 2cmps * 5s); + +static_assert(detail::unit_text() == "cm/s"); + +// area +static_assert(std::is_same_v::ratio>, ratio<1>>); + +static_assert(1cm * 1cm == 1sq_cm); +static_assert(100sq_cm / 10cm == 10cm); + +static_assert(detail::unit_text() == "cm²"); + +/* ************** DERIVED DIMENSIONS WITH NAMED UNITS **************** */ + +// acceleration + +static_assert(10cmps / 10s == 1Gal); +static_assert(10cmps / 1Gal == 10s); +static_assert(1Gal * 10s == 10cmps); + +// force + +static_assert(10g * 10Gal == 100dyn); +static_assert(100dyn / 10g == 10Gal); +static_assert(100dyn / 10Gal == 10g); + +// pressure + +static_assert(10dyn / 10sq_cm == 1Ba); +static_assert(10dyn / 1Ba == 10sq_cm); +static_assert(1Ba * 10sq_cm == 10dyn); + +// energy + +static_assert(10dyn * 10cm == 100_erg); +static_assert(100_erg / 10cm == 10dyn); +static_assert(100_erg / 10dyn == 10cm); + +/* ************** DERIVED DIMENSIONS IN TERMS OF OTHER UNITS **************** */ + +// power + +static_assert(10_erg / 10s == 1_ergps); +static_assert(1_ergps * 10s == 10_erg); +static_assert(10_erg / 1_ergps == 10s); + +static_assert(detail::unit_text() == "erg/s"); } diff --git a/test/unit_test/static/si_cgs_test.cpp b/test/unit_test/static/si_cgs_test.cpp new file mode 100644 index 00000000..151e920f --- /dev/null +++ b/test/unit_test/static/si_cgs_test.cpp @@ -0,0 +1,111 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using namespace units; + +static_assert(cgs::length(100) == si::length(1)); +static_assert(cgs::mass(1'000) == si::mass(1)); +static_assert(cgs::time(1) == si::time(1)); +static_assert(cgs::velocity(100) == si::velocity(1)); +static_assert(cgs::area(10000) == si::area(1)); +static_assert(cgs::acceleration(100) == si::acceleration(1)); +static_assert(cgs::force(100'000) == si::force(1)); +static_assert(cgs::energy(10'000'000) == si::energy(1)); +static_assert(cgs::power(10'000'000) == si::power(1)); +static_assert(cgs::pressure(10) == si::pressure(1)); + +namespace si_test { + +using namespace units::si::literals; + +static_assert(cgs::length(100) == 1m); +static_assert(cgs::mass(1'000) == 1kg); +static_assert(cgs::time(1) == 1s); +static_assert(cgs::velocity(100) == 1mps); +static_assert(cgs::acceleration(100) == 1mps_sq); +static_assert(cgs::force(100'000) == 1N); +static_assert(cgs::energy(10'000'000) == 1_J); +static_assert(cgs::power(10'000'000) == 1W); +static_assert(cgs::pressure(10) == 1Pa); + +} + +namespace cgs_test { + +using namespace units::cgs::literals; + +static_assert(100cm == si::length(1)); +static_assert(1'000g == si::mass(1)); +static_assert(1s == si::time(1)); +static_assert(100cmps == si::velocity(1)); +static_assert(100Gal == si::acceleration(1)); +static_assert(100'000dyn == si::force(1)); +static_assert(10'000'000_erg == si::energy(1)); +static_assert(10'000'000_ergps == si::power(1)); +static_assert(10Ba == si::pressure(1)); + +} + +namespace both_test { + +using namespace units::si::literals; +using namespace units::cgs::literals; + +// static_assert(100cm == 1m); // ambiguous +// static_assert(1'000g == 1kg); // ambiguous +static_assert(1s == 1s); +static_assert(100cmps == 1mps); +static_assert(100Gal == 1mps_sq); +static_assert(100'000dyn == 1N); +static_assert(10'000'000_erg == 1_J); +static_assert(10'000'000_ergps == 1W); +static_assert(10Ba == quantity_cast(1Pa)); + +} + +} + +// TODO add tests for arithmetic operations and ensure that they require quantity_cast diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp index 6179f669..1fec088e 100644 --- a/test/unit_test/static/si_test.cpp +++ b/test/unit_test/static/si_test.cpp @@ -123,11 +123,13 @@ static_assert(2 / 1Hz == 2s); // force static_assert(10kg * 10mps_sq == 100N); static_assert(100N / 1mps_sq == 100kg); -static_assert(100.N / 1kg == 100mps_sq); +static_assert(100N / 1kg == 100mps_sq); // pressure static_assert(10N / 10sq_m == 1Pa); +static_assert(10N / 1Pa == 10sq_m); +static_assert(1Pa * 10sq_m == 10N); // energy @@ -223,7 +225,8 @@ static_assert(detail::unit_text() == "m/s // area -static_assert(1m * 1m == 1sq_m); +static_assert(10m * 10m == 100sq_m); +static_assert(100sq_m / 10m == 10m); static_assert(10km * 10km == 100sq_km); static_assert(1sq_m == 10'000sq_cm); From 283b725eb7db15cecd596e47949ab4e50d99d6bc Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 17:30:47 +0100 Subject: [PATCH 27/44] PNG files explicitly marked as binary (resolves #39) --- .gitattributes | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.gitattributes b/.gitattributes index 314766e9..a802e460 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,16 @@ +# Set the default behavior * text=auto eol=lf *.{cmd,[cC][mM][dD]} text eol=crlf *.{bat,[bB][aA][tT]} text eol=crlf + +# Explicitly declare text files to always be normalized and converted to native line endings on checkout +*.cpp text +*.h text +*.py text +*.txt text +*.md text +*.yml text + +# Denote all files that are truly binary and should not be modified +*.png binary +*.jpg binary From 73033e347ae1c563aaff9f68b6d51f2e3d7c2675 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 17:41:18 +0100 Subject: [PATCH 28/44] exbi data prefix added (resolves #37) --- src/include/units/data/prefixes.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/include/units/data/prefixes.h b/src/include/units/data/prefixes.h index 827b39fa..c03ecf83 100644 --- a/src/include/units/data/prefixes.h +++ b/src/include/units/data/prefixes.h @@ -29,10 +29,11 @@ namespace units::data { struct prefix : prefix_type {}; -struct kibi : units::prefix> {}; -struct mebi : units::prefix> {}; -struct gibi : units::prefix> {}; -struct tebi : units::prefix> {}; -struct pebi : units::prefix> {}; +struct kibi : units::prefix> {}; +struct mebi : units::prefix> {}; +struct gibi : units::prefix> {}; +struct tebi : units::prefix> {}; +struct pebi : units::prefix> {}; +struct exbi : units::prefix> {}; } // namespace units::si From 9d50ea6ab556692205e677cba9075079f707c1bd Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Wed, 11 Dec 2019 21:38:44 +0100 Subject: [PATCH 29/44] CGS case added to avg_speed example --- example/avg_velocity.cpp | 63 ++++++++++++++++++++++++++------ src/include/units/bits/unit_op.h | 39 -------------------- src/include/units/quantity.h | 27 ++++++-------- 3 files changed, 63 insertions(+), 66 deletions(-) delete mode 100644 src/include/units/bits/unit_op.h diff --git a/example/avg_velocity.cpp b/example/avg_velocity.cpp index 1b470eb3..49d65993 100644 --- a/example/avg_velocity.cpp +++ b/example/avg_velocity.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include +#include #include namespace { @@ -54,7 +55,7 @@ constexpr units::Velocity AUTO avg_speed(units::Length AUTO d, units::Time AUTO template void print_result(D distance, T duration, V velocity) { - const auto result_in_kmph = units::quantity_cast(velocity); + const auto result_in_kmph = units::quantity_cast>(velocity); std::cout << "Average speed of a car that makes " << distance << " in " << duration << " is " << result_in_kmph << ".\n"; } @@ -62,10 +63,10 @@ void print_result(D distance, T duration, V velocity) void example() { using namespace units; - using namespace units::si::literals; // SI (int) { + using namespace units::si::literals; constexpr Length AUTO distance = 220km; // constructed from a UDL constexpr si::time duration(2); // constructed from a value @@ -73,15 +74,13 @@ void example() print_result(distance, duration, fixed_int_si_avg_speed(distance, duration)); print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); - - // the framework will not allow a division (and multiplication) of different dimensions - // with two integral representation (at least one of them have to ba floating-point one) - print_result(distance, duration, si_avg_speed(quantity_cast(distance), duration)); - print_result(distance, duration, avg_speed(quantity_cast(distance), duration)); + print_result(distance, duration, si_avg_speed(distance, duration)); + print_result(distance, duration, avg_speed(distance, duration)); } // SI (double) { + using namespace units::si::literals; constexpr Length AUTO distance = 220.km; // constructed from a UDL constexpr si::time duration(2); // constructed from a value @@ -97,6 +96,7 @@ void example() // Customary Units (int) { + using namespace units::si::literals; constexpr Length AUTO distance = 140mi; // constructed from a UDL constexpr si::time duration(2); // constructed from a value @@ -106,15 +106,13 @@ void example() // (explicit cast needed) print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast(distance), duration)); print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); - - // the framework will not allow a division (and multiplication) of different dimensions - // with two integral representation (at least one of them have to ba floating-point one) - print_result(distance, duration, si_avg_speed(quantity_cast(distance), duration)); - print_result(distance, duration, avg_speed(quantity_cast(distance), duration)); + print_result(distance, duration, si_avg_speed(distance, duration)); + print_result(distance, duration, avg_speed(distance, duration)); } // Customary Units (double) { + using namespace units::si::literals; constexpr Length AUTO distance = 140.mi; // constructed from a UDL constexpr si::time duration(2); // constructed from a value @@ -129,6 +127,47 @@ void example() print_result(distance, duration, si_avg_speed(distance, duration)); print_result(distance, duration, avg_speed(distance, duration)); } + + // CGS (int) + { + using namespace units::cgs::literals; + constexpr Length AUTO distance = 22'000'000cm; // constructed from a UDL + constexpr cgs::time duration(2); // constructed from a value + + std::cout << "\nCGS units with 'int' as representation\n"; + + // it is not possible to make a lossless conversion of centimeters to meters on an integral type + // (explicit cast needed) + print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast(distance), duration)); + print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); + + // not possible to convert both a dimension and a unit with implicit cast + print_result(distance, duration, si_avg_speed(quantity_cast(distance), duration)); + + print_result(distance, duration, avg_speed(distance, duration)); + } + + // CGS (double) + { + using namespace units::cgs::literals; + constexpr Length AUTO distance = 22'000'000.cm; // constructed from a UDL + constexpr cgs::time duration(2); // constructed from a value + + std::cout << "\nCGS units with 'double' as representation\n"; + + // conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed + // it is not possible to make a lossless conversion of centimeters to meters on an integral type + // (explicit cast needed) + print_result(distance, duration, fixed_int_si_avg_speed(quantity_cast>(distance), quantity_cast(duration))); + + print_result(distance, duration, fixed_double_si_avg_speed(distance, duration)); + + // not possible to convert both a dimension and a unit with implicit cast + print_result(distance, duration, si_avg_speed(quantity_cast(distance), duration)); + + print_result(distance, duration, avg_speed(distance, duration)); + } + } } // namespace diff --git a/src/include/units/bits/unit_op.h b/src/include/units/bits/unit_op.h deleted file mode 100644 index a4a1527a..00000000 --- a/src/include/units/bits/unit_op.h +++ /dev/null @@ -1,39 +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 - -namespace units::detail { - -template::ratio>, - typename Ratio2 = ratio_divide::ratio>> -using unit_ratio_multiply = ratio_multiply; - -template::ratio>, - typename Ratio2 = ratio_divide::ratio>> -using unit_ratio_divide = ratio_divide; - -} // namespace units::detail diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 5f665e7f..c87e9175 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -24,7 +24,6 @@ #include #include -#include #include #if __GNUC__ >= 10 @@ -182,8 +181,7 @@ struct cast_ratio { * * This cast gets the target quantity type to cast to. For example: * - * using seconds = units::time; - * auto q1 = units::quantity_cast(1ms); + * auto q1 = units::quantity_cast>(1ms); * * @tparam To a target quantity type to cast to */ @@ -194,8 +192,8 @@ template { using c_ratio = detail::cast_ratio::type; using c_rep = std::common_type_t; - using ret_unit = downcast_unit; - using ret = quantity; + using ret_unit = downcast_unit; + using ret = quantity; using cast = detail::quantity_cast_impl; return cast::cast(q); } @@ -595,7 +593,7 @@ template template [[nodiscard]] constexpr Scalar AUTO operator*(const quantity& lhs, const quantity& rhs) - requires equivalent_dim> && detail::basic_arithmetic + requires detail::basic_arithmetic && equivalent_dim> { using common_rep = decltype(lhs.count() * rhs.count()); using ratio = ratio_multiply; @@ -604,13 +602,12 @@ template [[nodiscard]] constexpr Quantity AUTO operator*(const quantity& lhs, const quantity& rhs) - requires (!equivalent_dim>) && // TODO equivalent_derived_dim? - (treat_as_floating_point || - detail::unit_ratio_multiply::den == 1) && - detail::basic_arithmetic + requires detail::basic_arithmetic && (!equivalent_dim>) // TODO equivalent_derived_dim? { using dim = dimension_multiply; - using ratio = ratio_multiply, typename dimension_unit::ratio>; ; + using ratio1 = ratio_divide::ratio>; + using ratio2 = ratio_divide::ratio>; + using ratio = ratio_multiply, typename dimension_unit::ratio>; using unit = downcast_unit; using common_rep = decltype(lhs.count() * rhs.count()); using ret = quantity; @@ -655,15 +652,15 @@ template [[nodiscard]] constexpr Quantity AUTO operator/(const quantity& lhs, const quantity& rhs) - requires detail::basic_arithmetic && (!equivalent_dim) && // TODO equivalent_derived_dim? - (treat_as_floating_point || - detail::unit_ratio_divide::den == 1) + requires detail::basic_arithmetic && (!equivalent_dim) // TODO equivalent_derived_dim? { Expects(rhs.count() != 0); using common_rep = decltype(lhs.count() / rhs.count()); using dim = dimension_divide; - using ratio = ratio_multiply, typename dimension_unit::ratio>; + using ratio1 = ratio_divide::ratio>; + using ratio2 = ratio_divide::ratio>; + using ratio = ratio_multiply, typename dimension_unit::ratio>; using unit = downcast_unit; using ret = quantity; return ret(lhs.count() / rhs.count()); From fbf3ef8c4fe7aace836ec50ce25361183b620e13 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 12 Dec 2019 13:17:31 +0100 Subject: [PATCH 30/44] Most of concepts moved to a new file + quantity.h split to smaller pieces --- example/measurement.cpp | 1 + src/include/units/base_dimension.h | 25 +- src/include/units/bits/common_quantity.h | 66 +++++ src/include/units/bits/concepts.h | 53 ---- src/include/units/bits/deduced_unit.h | 1 - src/include/units/concepts.h | 181 ++++++++++++ src/include/units/customization_points.h | 164 ++++++----- src/include/units/derived_dimension.h | 21 -- src/include/units/prefix.h | 21 +- src/include/units/quantity.h | 334 ++++------------------- src/include/units/quantity_cast.h | 188 +++++++++++++ src/include/units/ratio.h | 9 +- src/include/units/unit.h | 1 - src/include/units/unit_concept.h | 41 --- src/include/units/unit_of_concept.h | 56 ---- 15 files changed, 592 insertions(+), 570 deletions(-) create mode 100644 src/include/units/bits/common_quantity.h delete mode 100644 src/include/units/bits/concepts.h create mode 100644 src/include/units/concepts.h create mode 100644 src/include/units/quantity_cast.h delete mode 100644 src/include/units/unit_concept.h delete mode 100644 src/include/units/unit_of_concept.h diff --git a/example/measurement.cpp b/example/measurement.cpp index db7163a5..3ef98288 100644 --- a/example/measurement.cpp +++ b/example/measurement.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include +#include #include namespace { diff --git a/src/include/units/base_dimension.h b/src/include/units/base_dimension.h index bdf19e77..0f1e5309 100644 --- a/src/include/units/base_dimension.h +++ b/src/include/units/base_dimension.h @@ -23,7 +23,7 @@ #pragma once #include -#include +#include #include namespace units { @@ -54,29 +54,6 @@ struct base_dimension { using base_unit = U; }; -// BaseDimension -namespace detail { - -#if __GNUC__ == 9 && __GNUC_MINOR__ < 2 - -template -inline constexpr bool is_base_dimension = true; - -#else - -template -inline constexpr bool is_base_dimension = false; - -template -inline constexpr bool is_base_dimension> = true; - -#endif - -} // 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 diff --git a/src/include/units/bits/common_quantity.h b/src/include/units/bits/common_quantity.h new file mode 100644 index 00000000..0a1b4da0 --- /dev/null +++ b/src/include/units/bits/common_quantity.h @@ -0,0 +1,66 @@ +// 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 { + +template U, Scalar Rep> +class quantity; + +namespace detail { + +template +struct common_quantity_impl; + +template +struct common_quantity_impl, quantity, Rep> { + using type = quantity; +}; + +template +struct common_quantity_impl, quantity, Rep> { + using type = quantity>, Rep>; +}; + +template + requires same_unit_reference, dimension_unit>::value +struct common_quantity_impl, quantity, Rep> { + using type = quantity>, Rep>; +}; + +template +struct common_quantity_impl, quantity, Rep> { + using ratio1 = ratio_multiply; + using ratio2 = ratio_multiply; + using type = quantity>, Rep>; +}; + +} // namespace detail + +template> + requires equivalent_dim +using common_quantity = detail::common_quantity_impl::type; + +} // namespace units diff --git a/src/include/units/bits/concepts.h b/src/include/units/bits/concepts.h deleted file mode 100644 index b43d4524..00000000 --- a/src/include/units/bits/concepts.h +++ /dev/null @@ -1,53 +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 - -namespace units { - - namespace detail { - - template - concept basic_arithmetic = // exposition only - std::magma && - std::magma && - std::magma && - std::magma; - - template - concept safe_convertible = // exposition only - std::convertible_to && - (treat_as_floating_point || (!treat_as_floating_point)); - - template - concept safe_divisible = // exposition only - treat_as_floating_point || - ratio_divide::den == 1; - - } - -} // namespace units diff --git a/src/include/units/bits/deduced_unit.h b/src/include/units/bits/deduced_unit.h index 6f170aa5..7d846b45 100644 --- a/src/include/units/bits/deduced_unit.h +++ b/src/include/units/bits/deduced_unit.h @@ -22,7 +22,6 @@ #pragma once -#include #include namespace units::detail { diff --git a/src/include/units/concepts.h b/src/include/units/concepts.h new file mode 100644 index 00000000..9f6184f6 --- /dev/null +++ b/src/include/units/concepts.h @@ -0,0 +1,181 @@ +// 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 { + +namespace detail { + +template +concept basic_arithmetic = // exposition only + std::magma && + std::magma && + std::magma && + std::magma; + +} // namespace detail + +// PrefixType +struct prefix_type; + +template +concept PrefixType = std::derived_from; + +// Prefix +// 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> = true; + +// } // namespace detail + +template +// concept Prefix = detail::is_prefix; +concept Prefix = true; + + +namespace detail { + +template +inline constexpr bool is_ratio = false; + +} // namespace detail + +template +concept Ratio = detail::is_ratio; + + +// UnitRatio +template +concept UnitRatio = Ratio && (R::num * R::den > 0); + +// Unit +template +struct scaled_unit; + +template +concept Unit = is_derived_from_instantiation; + +// BaseDimension +template +struct base_dimension; + +namespace detail { + +#if __GNUC__ == 9 && __GNUC_MINOR__ < 2 + +template +inline constexpr bool is_base_dimension = true; + +#else + +template +inline constexpr bool is_base_dimension = false; + +template +inline constexpr bool is_base_dimension> = true; + +#endif + +} // namespace detail + +template +concept BaseDimension = detail::is_base_dimension; // TODO Replace with is_derived_from_instantiation when fixed + +// Exponent +namespace detail { + +template +inline constexpr bool is_exp = false; + +} // namespace detail + +template +concept Exponent = detail::is_exp; + +// DerivedDimension +template +struct derived_dimension; + +template +concept DerivedDimension = std::is_empty_v && is_instantiation, derived_dimension>; + +// Dimension +template +concept Dimension = BaseDimension || DerivedDimension; + +// UnitOf +namespace detail { + +template +struct dimension_unit_impl; + +template +struct dimension_unit_impl { + using type = D::base_unit; +}; + +template +struct dimension_unit_impl { + using type = D::coherent_unit; +}; + +} // namespace detail + +template +using dimension_unit = detail::dimension_unit_impl::type; + +template +concept UnitOf = + Unit && + Dimension && + std::same_as::reference>; + +// Quantity +namespace detail { + +template +inline constexpr bool is_quantity = false; + +} // namespace detail + +template +concept Quantity = detail::is_quantity; + +// Scalar +template +concept Scalar = (!Quantity) && std::regular && std::totally_ordered && detail::basic_arithmetic; + +} // namespace units diff --git a/src/include/units/customization_points.h b/src/include/units/customization_points.h index 9613d554..0e8b70bd 100644 --- a/src/include/units/customization_points.h +++ b/src/include/units/customization_points.h @@ -22,94 +22,120 @@ #pragma once +#include #include -#include namespace units { - // treat_as_floating_point +/** + * @brief Specifies if a value of a type should be treated as a floating-point value + * + * This type trait should be specialized for a custom representation type to specify + * that values fo this type should be treated by the library as a floating-point ones + * which will enable implicit conversions between quantities. + * + * @tparam Rep a representation type for which a type trait is defined + */ +template +inline constexpr bool treat_as_floating_point = std::is_floating_point_v; - template // TODO Conceptify that - inline constexpr bool treat_as_floating_point = std::is_floating_point_v; +/** + * @brief A type trait that defines zero, one, min, and max for a representation type + * + * The zero, one, min, and max member functions in units::quantity forward their work to + * these methods. This type can be specialized if the representation Rep requires a specific + * implementation to return these quantity objects. + * + * @tparam Rep a representation type for which a type trait is defined + */ +template +struct quantity_values { + static constexpr Rep zero() noexcept { return Rep(0); } + static constexpr Rep one() noexcept { return Rep(1); } + static constexpr Rep min() noexcept { return std::numeric_limits::lowest(); } + static constexpr Rep max() noexcept { return std::numeric_limits::max(); } +}; - // // isnan - // namespace isnan_impl { - // // non-ADL lookup block - // void isnan(); // undefined - // template - // inline constexpr bool has_customization = false; +// // isnan +// namespace isnan_impl { - // template - // requires requires(const T& t) { - // { isnan(t) } -> bool; - // } - // inline constexpr bool has_customization = true; +// // non-ADL lookup block +// void isnan(); // undefined - // struct fn { - // template - // constexpr bool operator()(const T&) const - // { - // return false; - // } +// template +// inline constexpr bool has_customization = false; - // template - // requires treat_as_floating_point - // constexpr bool operator()(const T& value) const - // { - // return std::isnan(value); - // } +// template +// requires requires(const T& t) { +// { isnan(t) } -> bool; +// } +// inline constexpr bool has_customization = true; - // template - // requires treat_as_floating_point && has_customization - // constexpr bool operator()(const T& value) const - // { - // return isnan(value); // uses ADL - // } - // }; - // } +// struct fn { +// template +// constexpr bool operator()(const T&) const +// { +// return false; +// } - // inline constexpr isnan_impl::fn isnan{}; +// template +// requires treat_as_floating_point +// constexpr bool operator()(const T& value) const +// { +// return std::isnan(value); +// } - // // isfinite - // namespace isfinite_impl { +// template +// requires treat_as_floating_point && has_customization +// constexpr bool operator()(const T& value) const +// { +// return isnan(value); // uses ADL +// } +// }; +// } - // // non-ADL lookup block - // void isfinite(); // undefined +// inline constexpr isnan_impl::fn isnan{}; - // template - // inline constexpr bool has_customization = false; +// // isfinite +// namespace isfinite_impl { - // template - // requires requires(const T& t) { - // { isfinite(t) } -> bool; - // } - // inline constexpr bool has_customization = true; +// // non-ADL lookup block +// void isfinite(); // undefined - // struct fn { - // template - // constexpr bool operator()(const T&) const - // { - // return true; - // } +// template +// inline constexpr bool has_customization = false; - // template - // requires treat_as_floating_point - // constexpr bool operator()(const T& value) const - // { - // return std::isfinite(value); - // } +// template +// requires requires(const T& t) { +// { isfinite(t) } -> bool; +// } +// inline constexpr bool has_customization = true; - // template - // requires treat_as_floating_point && has_customization - // constexpr bool operator()(const T& value) const - // { - // return isfinite(value); // uses ADL - // } - // }; - // } +// struct fn { +// template +// constexpr bool operator()(const T&) const +// { +// return true; +// } - // inline constexpr isfinite_impl::fn isfinite{}; +// template +// requires treat_as_floating_point +// constexpr bool operator()(const T& value) const +// { +// return std::isfinite(value); +// } -} +// template +// requires treat_as_floating_point && has_customization +// constexpr bool operator()(const T& value) const +// { +// return isfinite(value); // uses ADL +// } +// }; +// } + +// inline constexpr isfinite_impl::fn isfinite{}; + +} // namespace units diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h index a0846a81..73870a26 100644 --- a/src/include/units/derived_dimension.h +++ b/src/include/units/derived_dimension.h @@ -31,19 +31,6 @@ 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 * @@ -79,14 +66,6 @@ struct derived_dimension<> : downcast_base> {}; template struct derived_dimension : downcast_base> {}; // TODO rename to 'dimension'? -// 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 * diff --git a/src/include/units/prefix.h b/src/include/units/prefix.h index 2117178d..eaded432 100644 --- a/src/include/units/prefix.h +++ b/src/include/units/prefix.h @@ -35,9 +35,6 @@ namespace units { */ struct prefix_type {}; -template -concept PrefixType = std::derived_from; - /** * @brief No prefix possible for the unit * @@ -72,25 +69,9 @@ struct prefix_base : downcast_base> { * @tparam R factor to be used to scale a unit */ template - requires(!std::same_as) + 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 { - -// template -// inline constexpr bool is_prefix = false; - -// template -// inline constexpr bool is_prefix> = true; - -// } // namespace detail - -template -// concept Prefix = detail::is_prefix; -concept Prefix = true; - } // namespace units diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index c87e9175..acbc276b 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -22,255 +22,32 @@ #pragma once -#include +#include #include #include +#include #if __GNUC__ >= 10 #include #endif -#include #include namespace units { -// Quantity namespace detail { -template -inline constexpr bool is_quantity = false; +template +concept safe_convertible = // exposition only + std::convertible_to && + (treat_as_floating_point || (!treat_as_floating_point)); -// partial specialization below after the first quantity forward declaration +template +concept safe_divisible = // exposition only + treat_as_floating_point || + ratio_divide::den == 1; -} // namespace detail - -template -concept Quantity = detail::is_quantity; - -// QuantityOf -template -concept QuantityOf = Quantity && Dimension && equivalent_dim; - -// Scalar -template -concept Scalar = (!Quantity) && std::regular && std::totally_ordered && detail::basic_arithmetic; - -template U, Scalar Rep> -class quantity; - -namespace detail { - -template -inline constexpr bool is_quantity> = true; - -} // namespace detail - -// common_quantity -namespace detail { - -template -struct common_quantity_impl; - -template -struct common_quantity_impl, quantity, Rep> { - using type = quantity; -}; - -template -struct common_quantity_impl, quantity, Rep> { - using type = quantity>, Rep>; -}; - -template - requires same_unit_reference, dimension_unit>::value -struct common_quantity_impl, quantity, Rep> { - using type = quantity>, Rep>; -}; - -template -struct common_quantity_impl, quantity, Rep> { - using ratio1 = ratio_multiply; - using ratio2 = ratio_multiply; - using type = quantity>, Rep>; -}; - -} // namespace detail - -template> - requires equivalent_dim -using common_quantity = detail::common_quantity_impl::type; - -// quantity_cast -namespace detail { - -template -struct quantity_cast_impl { - template - static constexpr To cast(const Q& q) - { - if constexpr (treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * - (static_cast(CRatio::num) / static_cast(CRatio::den)))); - } else { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num) / - static_cast(CRatio::den))); - } - } -}; - -template -struct quantity_cast_impl { - template - static constexpr To cast(const Q& q) - { - return To(static_cast(q.count())); - } -}; - -template -struct quantity_cast_impl { - template - static constexpr To cast(const Q& q) - { - if constexpr (treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * (CRep{1} / static_cast(CRatio::den)))); - } else { - return To(static_cast(static_cast(q.count()) / static_cast(CRatio::den))); - } - } -}; - -template -struct quantity_cast_impl { - template - static constexpr To cast(const Q& q) - { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num))); - } -}; - -template -struct cast_ratio; - -template -struct cast_ratio { - using type = ratio_divide; -}; - -template - requires same_unit_reference::value -struct cast_ratio { - using type = ratio_divide; -}; - -template -struct cast_ratio { - using from_ratio = ratio_multiply; - using to_ratio = ratio_multiply; - using type = ratio_divide; -}; - -} // namespace detail - -/** - * @brief Explcit cast of a quantity - * - * Implicit conversions between quantities of different types are allowed only for "safe" - * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. - * - * This cast gets the target quantity type to cast to. For example: - * - * auto q1 = units::quantity_cast>(1ms); - * - * @tparam To a target quantity type to cast to - */ -template -[[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires QuantityOf && - detail::basic_arithmetic> -{ - using c_ratio = detail::cast_ratio::type; - using c_rep = std::common_type_t; - using ret_unit = downcast_unit; - using ret = quantity; - using cast = detail::quantity_cast_impl; - return cast::cast(q); -} - -/** - * @brief Explcit cast of a quantity - * - * Implicit conversions between quantities of different types are allowed only for "safe" - * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. - * - * This cast gets only the target dimension to cast to. For example: - * - * auto q1 = units::quantity_cast(200Gal); - * - * @tparam ToD a dimension type to use for a target quantity - */ -template -[[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires equivalent_dim -{ - return quantity_cast>(q); -} - -/** - * @brief Explcit cast of a quantity - * - * Implicit conversions between quantities of different types are allowed only for "safe" - * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. - * - * This cast gets only the target unit to cast to. For example: - * - * auto q1 = units::quantity_cast(1ms); - * - * @tparam ToU a unit type to use for a target quantity - */ -template -[[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires UnitOf -{ - return quantity_cast>(q); -} - -/** - * @brief Explcit cast of a quantity - * - * Implicit conversions between quantities of different types are allowed only for "safe" - * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. - * - * This cast gets only representation to cast to. For example: - * - * auto q1 = units::quantity_cast(1ms); - * - * @tparam ToRep a representation type to use for a target quantity - */ -template -[[nodiscard]] constexpr auto quantity_cast(const quantity& q) - requires detail::basic_arithmetic> -{ - return quantity_cast>(q); -} - -/** - * @brief A type trait that defines zero, one, min, and max for a representation type - * - * The zero, one, min, and max member functions in units::quantity forward their work to - * these methods. This type can be specialized if the representation Rep requires a specific - * implementation to return these quantity objects. - * - * @tparam Rep a representation type for which a type trait is defined - */ -template -struct quantity_values { - static constexpr Rep zero() noexcept { return Rep(0); } - static constexpr Rep one() noexcept { return Rep(1); } - static constexpr Rep min() noexcept { return std::numeric_limits::lowest(); } - static constexpr Rep max() noexcept { return std::numeric_limits::max(); } -}; +} // namespace detail /** * @brief A quantity @@ -443,46 +220,6 @@ public: return *this; } - template - [[nodiscard]] friend constexpr Quantity AUTO operator+(const quantity& lhs, const quantity& rhs) - requires detail::basic_arithmetic - { - using common_rep = decltype(lhs.count() + rhs.count()); - using ret = common_quantity, common_rep>; - return ret(ret(lhs).count() + ret(rhs).count()); - } - - template - [[nodiscard]] friend constexpr Quantity AUTO operator-(const quantity& lhs, const quantity& rhs) - requires detail::basic_arithmetic - { - using common_rep = decltype(lhs.count() - rhs.count()); - using ret = common_quantity, common_rep>; - return ret(ret(lhs).count() - ret(rhs).count()); - } - - template - [[nodiscard]] friend constexpr Quantity AUTO operator%(const quantity& q, const Value& v) - requires (!treat_as_floating_point) && - (!treat_as_floating_point) && - std::magma - { - using common_rep = decltype(q.count() % v); - using ret = quantity; - return ret(q.count() % v); - } - - template - [[nodiscard]] friend constexpr Quantity AUTO operator%(const quantity& lhs, const quantity& rhs) - requires (!treat_as_floating_point) && - (!treat_as_floating_point) && - std::magma - { - using common_rep = decltype(lhs.count() % rhs.count()); - using ret = common_quantity, common_rep>; - return ret(ret(lhs).count() % ret(rhs).count()); - } - #if __GNUC__ >= 10 template @@ -573,7 +310,23 @@ public: } }; -// TODO make hidden friends when moved to gcc-10 +template +[[nodiscard]] constexpr Quantity AUTO operator+(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic +{ + using common_rep = decltype(lhs.count() + rhs.count()); + using ret = common_quantity, quantity, common_rep>; + return ret(ret(lhs).count() + ret(rhs).count()); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator-(const quantity& lhs, const quantity& rhs) + requires detail::basic_arithmetic +{ + using common_rep = decltype(lhs.count() - rhs.count()); + using ret = common_quantity, quantity, common_rep>; + return ret(ret(lhs).count() - ret(rhs).count()); +} template [[nodiscard]] constexpr Quantity AUTO operator*(const quantity& q, const Value& v) @@ -614,7 +367,7 @@ template +template [[nodiscard]] constexpr Quantity AUTO operator/(const Value& v, const quantity& q) requires std::magma { @@ -666,4 +419,33 @@ template +[[nodiscard]] constexpr Quantity AUTO operator%(const quantity& q, const Value& v) + requires (!treat_as_floating_point) && + (!treat_as_floating_point) && + std::magma +{ + using common_rep = decltype(q.count() % v); + using ret = quantity; + return ret(q.count() % v); +} + +template +[[nodiscard]] constexpr Quantity AUTO operator%(const quantity& lhs, const quantity& rhs) + requires (!treat_as_floating_point) && + (!treat_as_floating_point) && + std::magma +{ + using common_rep = decltype(lhs.count() % rhs.count()); + using ret = common_quantity, quantity, common_rep>; + return ret(ret(lhs).count() % ret(rhs).count()); +} + +namespace detail { + +template +inline constexpr bool is_quantity> = true; + +} // namespace detail + } // namespace units diff --git a/src/include/units/quantity_cast.h b/src/include/units/quantity_cast.h new file mode 100644 index 00000000..c84548ac --- /dev/null +++ b/src/include/units/quantity_cast.h @@ -0,0 +1,188 @@ +// 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 { + +// QuantityOf +template +concept QuantityOf = Quantity && Dimension && equivalent_dim; + +// quantity_cast +namespace detail { + +template +struct quantity_cast_impl { + template + static constexpr To cast(const Q& q) + { + if constexpr (treat_as_floating_point) { + return To(static_cast(static_cast(q.count()) * + (static_cast(CRatio::num) / static_cast(CRatio::den)))); + } else { + return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num) / + static_cast(CRatio::den))); + } + } +}; + +template +struct quantity_cast_impl { + template + static constexpr To cast(const Q& q) + { + return To(static_cast(q.count())); + } +}; + +template +struct quantity_cast_impl { + template + static constexpr To cast(const Q& q) + { + if constexpr (treat_as_floating_point) { + return To(static_cast(static_cast(q.count()) * (CRep{1} / static_cast(CRatio::den)))); + } else { + return To(static_cast(static_cast(q.count()) / static_cast(CRatio::den))); + } + } +}; + +template +struct quantity_cast_impl { + template + static constexpr To cast(const Q& q) + { + return To(static_cast(static_cast(q.count()) * static_cast(CRatio::num))); + } +}; + +template +struct cast_ratio; + +template +struct cast_ratio { + using type = ratio_divide; +}; + +template + requires same_unit_reference::value +struct cast_ratio { + using type = ratio_divide; +}; + +template +struct cast_ratio { + using from_ratio = ratio_multiply; + using to_ratio = ratio_multiply; + using type = ratio_divide; +}; + +} // namespace detail + +/** + * @brief Explcit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets the target quantity type to cast to. For example: + * + * auto q1 = units::quantity_cast>(1ms); + * + * @tparam To a target quantity type to cast to + */ +template +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires QuantityOf && + detail::basic_arithmetic> +{ + using c_ratio = detail::cast_ratio::type; + using c_rep = std::common_type_t; + using ret_unit = downcast_unit; + using ret = quantity; + using cast = detail::quantity_cast_impl; + return cast::cast(q); +} + +/** + * @brief Explcit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets only the target dimension to cast to. For example: + * + * auto q1 = units::quantity_cast(200Gal); + * + * @tparam ToD a dimension type to use for a target quantity + */ +template +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires equivalent_dim +{ + return quantity_cast>(q); +} + +/** + * @brief Explcit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets only the target unit to cast to. For example: + * + * auto q1 = units::quantity_cast(1ms); + * + * @tparam ToU a unit type to use for a target quantity + */ +template +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires UnitOf +{ + return quantity_cast>(q); +} + +/** + * @brief Explcit cast of a quantity + * + * Implicit conversions between quantities of different types are allowed only for "safe" + * (i.e. non-truncating) conversion. In such cases an explicit cast have to be used. + * + * This cast gets only representation to cast to. For example: + * + * auto q1 = units::quantity_cast(1ms); + * + * @tparam ToRep a representation type to use for a target quantity + */ +template +[[nodiscard]] constexpr auto quantity_cast(const quantity& q) + requires detail::basic_arithmetic> +{ + return quantity_cast>(q); +} + +} // namespace units diff --git a/src/include/units/ratio.h b/src/include/units/ratio.h index 6e0700a0..6ad15cef 100644 --- a/src/include/units/ratio.h +++ b/src/include/units/ratio.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include #include #include @@ -51,21 +52,13 @@ struct ratio { using type = ratio; }; -// is_ratio - namespace detail { -template -inline constexpr bool is_ratio = false; - template inline constexpr bool is_ratio> = true; } // namespace detail -template -concept Ratio = detail::is_ratio; - // ratio_add // TODO implement ratio_add diff --git a/src/include/units/unit.h b/src/include/units/unit.h index f0875a95..cc7973d8 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/src/include/units/unit_concept.h b/src/include/units/unit_concept.h deleted file mode 100644 index 9e250657..00000000 --- a/src/include/units/unit_concept.h +++ /dev/null @@ -1,41 +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 { - -// UnitRatio -template -concept UnitRatio = Ratio && (R::num * R::den > 0); - -template -struct scaled_unit; - -// Unit -template -concept Unit = is_derived_from_instantiation; - -} // namespace units diff --git a/src/include/units/unit_of_concept.h b/src/include/units/unit_of_concept.h deleted file mode 100644 index c39a4016..00000000 --- a/src/include/units/unit_of_concept.h +++ /dev/null @@ -1,56 +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 - -namespace units { - -namespace detail { - -template -struct dimension_unit_impl; - -template -struct dimension_unit_impl { - using type = D::base_unit; -}; - -template -struct dimension_unit_impl { - using type = D::coherent_unit; -}; - -} // namespace detail - -template -using dimension_unit = detail::dimension_unit_impl::type; - -// UnitOf -template -concept UnitOf = - Unit && - Dimension && - std::same_as::reference>; - -} // namespace units From 6765054e5d4e045906c13fe9020103c79e343638 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 12 Dec 2019 15:37:52 +0100 Subject: [PATCH 31/44] More CGS tests added --- test/unit_test/static/si_cgs_test.cpp | 42 ++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/test/unit_test/static/si_cgs_test.cpp b/test/unit_test/static/si_cgs_test.cpp index 151e920f..78605221 100644 --- a/test/unit_test/static/si_cgs_test.cpp +++ b/test/unit_test/static/si_cgs_test.cpp @@ -106,6 +106,46 @@ static_assert(10Ba == quantity_cast(1Pa)); } +namespace cgs_test { + +// addition + +// static_assert(100cm + si::length(1) == si::length(2)); // should not compile (different dimensions) +// static_assert(si::length(1) + 100cm == si::length(2)); // should not compile (different dimensions) +static_assert(quantity_cast>(100cm) + si::length(1) == si::length(2)); +static_assert(si::length(1) + quantity_cast>(100cm) == si::length(2)); +static_assert(100cm + quantity_cast>(si::length(1)) == 200cm); +static_assert(quantity_cast>(si::length(1)) + 100cm == 200cm); + +// substraction + +// static_assert(500cm - si::length(1) == si::length(4)); // should not compile (different dimensions) +// static_assert(si::length(5) - 100cm == si::length(4)); // should not compile (different dimensions) +static_assert(quantity_cast>(500cm) - si::length(1) == si::length(4)); +static_assert(si::length(5) - quantity_cast>(100cm) == si::length(4)); +static_assert(500cm - quantity_cast>(si::length(1)) == 400cm); +static_assert(quantity_cast>(si::length(5)) - 100cm == 400cm); + +// multiplication + +// static_assert(200cm * si::length(2) == si::area(4)); // should not compile (unknown dimension) + +static_assert(quantity_cast(200cm) * si::length(2) == si::area(4)); +static_assert(200cm * quantity_cast(si::length(2)) == 40'000sq_cm); + +// TODO Add support for quantity_cast on an unknown_dimension? +// static_assert(quantity_cast>(200cm * si::length(2)) == si::area(4)); +// static_assert(quantity_cast(200cm * si::length(2)) == si::area(4)); +// static_assert(quantity_cast>(200cm * si::length(2)) == 40'000sq_cm); +// static_assert(quantity_cast(200cm * si::length(2)) == 40'000sq_cm); + +// division + +// static_assert(si::area(4) / 200cm == si::length(2)); // should not compile (unknown dimension) + +static_assert(si::area(4) / quantity_cast>(200cm) == si::length(2)); +static_assert(quantity_cast>(si::area(4)) / 200cm == 200cm); + } -// TODO add tests for arithmetic operations and ensure that they require quantity_cast +} From e10f5baab63d9fa031ddcb5734e38f9bb5e38752 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 12 Dec 2019 15:38:21 +0100 Subject: [PATCH 32/44] Quantity cast for dimension now uses dimension's coherent unit --- src/include/units/quantity_cast.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/units/quantity_cast.h b/src/include/units/quantity_cast.h index c84548ac..4906995b 100644 --- a/src/include/units/quantity_cast.h +++ b/src/include/units/quantity_cast.h @@ -144,7 +144,7 @@ template [[nodiscard]] constexpr auto quantity_cast(const quantity& q) requires equivalent_dim { - return quantity_cast>(q); + return quantity_cast, Rep>>(q); } /** From 156ab7ae7faa0b773f31c73e92c48cc6310a48eb Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Thu, 12 Dec 2019 15:39:04 +0100 Subject: [PATCH 33/44] Unknown dimension assert added to quantity_test --- test/unit_test/static/quantity_test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index 3636cf32..7de30728 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -272,6 +272,8 @@ static_assert((7m % 2).count() == 1); static_assert((7m % 2m).count() == 1); static_assert((7km % 2000m).count() == 1000); +static_assert((10sq_km * 10sq_km) / 50sq_km == 2sq_km); + // comparators static_assert(2m + 1m == 3m); From b1b63e1b3af5e5508b28bb58309e0c2d44e6bcc6 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 14 Dec 2019 11:44:56 +0100 Subject: [PATCH 34/44] 2 parital specializations of a derived_dimension split to different types --- src/include/units/bits/deduced_symbol_text.h | 4 +- src/include/units/bits/deduced_unit.h | 12 +- src/include/units/bits/dimension_op.h | 47 +++---- src/include/units/bits/unit_text.h | 11 +- src/include/units/concepts.h | 9 +- src/include/units/derived_dimension.h | 128 +++++++++---------- src/include/units/unit.h | 2 +- test/unit_test/static/dimension_op_test.cpp | 80 +++++------- test/unit_test/static/quantity_test.cpp | 15 +-- test/unit_test/static/type_list_test.cpp | 10 +- 10 files changed, 152 insertions(+), 166 deletions(-) diff --git a/src/include/units/bits/deduced_symbol_text.h b/src/include/units/bits/deduced_symbol_text.h index 04476b27..05015919 100644 --- a/src/include/units/bits/deduced_symbol_text.h +++ b/src/include/units/bits/deduced_symbol_text.h @@ -68,7 +68,7 @@ constexpr auto exp_text() } template -constexpr auto deduced_symbol_text(derived_dimension, std::index_sequence) +constexpr auto deduced_symbol_text(exp_list, std::index_sequence) { return (exp_text() + ...); } @@ -76,7 +76,7 @@ constexpr auto deduced_symbol_text(derived_dimension, std::index_sequence template constexpr auto deduced_symbol_text() { - return deduced_symbol_text(Dim(), std::index_sequence_for()); + return deduced_symbol_text(typename Dim::recipe(), std::index_sequence_for()); } } // namespace units::detail diff --git a/src/include/units/bits/deduced_unit.h b/src/include/units/bits/deduced_unit.h index 7d846b45..9f36466b 100644 --- a/src/include/units/bits/deduced_unit.h +++ b/src/include/units/bits/deduced_unit.h @@ -27,11 +27,11 @@ namespace units::detail { // same_scaled_units -template +template inline constexpr bool same_scaled_units = false; template -inline constexpr bool same_scaled_units, Us...> = (UnitOf && ...); +inline constexpr bool same_scaled_units, Us...> = (UnitOf && ...); // deduced_unit template @@ -50,17 +50,17 @@ struct ratio_op { using ratio = ratio_op::ratio; }; -template +template struct derived_ratio; template -struct derived_ratio, Us...> { +struct derived_ratio, Us...> { using ratio = ::units::ratio<1>; }; template -struct derived_ratio, U, URest...> { - using rest_ratio = derived_ratio, URest...>::ratio; +struct derived_ratio, U, URest...> { + using rest_ratio = derived_ratio, URest...>::ratio; using ratio = ratio_op::ratio; }; diff --git a/src/include/units/bits/dimension_op.h b/src/include/units/bits/dimension_op.h index 520086c8..6ca7122e 100644 --- a/src/include/units/bits/dimension_op.h +++ b/src/include/units/bits/dimension_op.h @@ -51,7 +51,7 @@ struct equivalent_derived_dim : std::false_type {}; template requires (sizeof...(Es1) == sizeof...(Es2)) -struct equivalent_derived_dim, derived_dimension> : std::conjunction...> {}; +struct equivalent_derived_dim, derived_dimension_base> : std::conjunction...> {}; template struct equivalent_dim_impl : std::disjunction, equivalent_derived_dim, downcast_base_t>> {}; @@ -84,7 +84,7 @@ struct check_unknown { // downcast did not find a user predefined type template -struct check_unknown> { +struct check_unknown> { using type = unknown_dimension; }; @@ -114,17 +114,17 @@ struct dim_invert_impl; template struct dim_invert_impl { - using type = downcast_dimension>>; + using type = downcast_dimension>>; }; template -struct dim_invert_impl>> { +struct dim_invert_impl>> { using type = D; }; template -struct dim_invert_impl> { - using type = downcast_dimension...>>; +struct dim_invert_impl> { + using type = downcast_dimension...>>; }; template @@ -139,18 +139,21 @@ using dim_invert = detail::dim_invert_impl::type; // dimension_multiply namespace detail { -template -struct dim_unpack { - using type = D; +template +struct to_dimension; + +template +struct to_dimension> { + using type = derived_dimension_base; }; template -struct dim_unpack>> { +struct to_dimension>> { using type = D; }; /** - * @brief Merges 2 sorted derived dimensions into one units::derived_dimension + * @brief Merges 2 sorted derived dimensions into one units::derived_dimension_base * * 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 @@ -159,19 +162,19 @@ struct dim_unpack>> { * dimension itself. */ template -using merge_dimension = dim_unpack>::type>::type; +using merge_dimension = to_dimension>::type>::type; template struct dimension_multiply_impl; template struct dimension_multiply_impl { - using type = downcast_dimension>, derived_dimension>>>; + using type = downcast_dimension>, derived_dimension_base>>>; }; template struct dimension_multiply_impl { - using type = downcast_dimension>, typename D2::downcast_base_type>>; + using type = downcast_dimension>, typename D2::downcast_base_type>>; }; template @@ -200,11 +203,11 @@ struct dimension_sqrt_impl; template struct dimension_sqrt_impl { - using type = downcast_dimension>>; + using type = downcast_dimension>>; }; template -struct dimension_sqrt_impl>> { +struct dimension_sqrt_impl>> { using type = D; }; @@ -214,8 +217,8 @@ struct dimension_sqrt_impl { }; template -struct dimension_sqrt_impl> { - using type = downcast_dimension...>>; +struct dimension_sqrt_impl> { + using type = downcast_dimension...>>; }; } // namespace detail @@ -231,7 +234,7 @@ struct dimension_pow_impl; template struct dimension_pow_impl { - using type = downcast_dimension>>; + using type = downcast_dimension>>; }; template @@ -240,7 +243,7 @@ struct dimension_pow_impl { }; template -struct dimension_pow_impl>, N> { +struct dimension_pow_impl>, N> { using type = D; }; @@ -250,8 +253,8 @@ struct dimension_pow_impl { }; template -struct dimension_pow_impl, N> { - using type = downcast_dimension...>>; +struct dimension_pow_impl, N> { + using type = downcast_dimension...>>; }; } // namespace detail diff --git a/src/include/units/bits/unit_text.h b/src/include/units/bits/unit_text.h index af95c30e..f1fc8152 100644 --- a/src/include/units/bits/unit_text.h +++ b/src/include/units/bits/unit_text.h @@ -22,7 +22,6 @@ #pragma once -#include #include namespace units::detail { @@ -73,19 +72,19 @@ constexpr auto prefix_or_ratio_text() } template -constexpr auto derived_dimension_unit_text(derived_dimension, std::index_sequence) +constexpr auto derived_dimension_unit_text(exp_list, std::index_sequence) { return (exp_text::symbol, Idxs>() + ...); } template -constexpr auto derived_dimension_unit_text(derived_dimension d) +constexpr auto derived_dimension_unit_text(exp_list list) { - return derived_dimension_unit_text(d, std::index_sequence_for()); + return derived_dimension_unit_text(list, std::index_sequence_for()); } template -constexpr bool all_named(derived_dimension) +constexpr bool all_named(exp_list) { return (dimension_unit::is_named && ...); } @@ -97,7 +96,7 @@ constexpr auto derived_dimension_unit_text() if constexpr(all_named(recipe())) return derived_dimension_unit_text(recipe()); else - return derived_dimension_unit_text(Dim()); + return derived_dimension_unit_text(typename Dim::exponents()); } // TODO Inline below concept when switched to gcc-10 diff --git a/src/include/units/concepts.h b/src/include/units/concepts.h index 9f6184f6..46fe4668 100644 --- a/src/include/units/concepts.h +++ b/src/include/units/concepts.h @@ -112,7 +112,7 @@ inline constexpr bool is_base_dimension> = true; } // namespace detail template -concept BaseDimension = detail::is_base_dimension; // TODO Replace with is_derived_from_instantiation when fixed +concept BaseDimension = detail::is_base_dimension; // Exponent namespace detail { @@ -126,11 +126,12 @@ template concept Exponent = detail::is_exp; // DerivedDimension -template -struct derived_dimension; +template + requires (BaseDimension && ... && BaseDimension) +struct derived_dimension_base; template -concept DerivedDimension = std::is_empty_v && is_instantiation, derived_dimension>; +concept DerivedDimension = is_instantiation, derived_dimension_base>; // Dimension template diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h index 73870a26..b50c4dde 100644 --- a/src/include/units/derived_dimension.h +++ b/src/include/units/derived_dimension.h @@ -31,41 +31,6 @@ namespace units { -/** - * @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> {}; // TODO rename to 'dimension'? - /** * @brief A power of factor corresponding to the dimension of a quantity * @@ -119,6 +84,31 @@ struct exp_multiply_impl { template using exp_multiply = detail::exp_multiply_impl::type; +template +struct exp_list {}; + +/** + * @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 class template is used by the library engine and should not be directly instantiated by the user. + * + * @tparam E a first exponent of a derived dimension + * @tparam ERest zero or more following exponents of a derived dimension + */ +template + requires (BaseDimension && ... && BaseDimension) +struct derived_dimension_base : downcast_base> { + using exponents = exp_list; +}; + // make_dimension namespace detail { @@ -130,32 +120,32 @@ namespace detail { * * @tparam D derived dimension to consolidate */ -template +template struct dim_consolidate; template<> -struct dim_consolidate> { - using type = derived_dimension<>; +struct dim_consolidate> { + using type = exp_list<>; }; template -struct dim_consolidate> { - using type = derived_dimension; +struct dim_consolidate> { + using type = exp_list; }; template -struct dim_consolidate> { - using type = type_list_push_front>::type, E1>; +struct dim_consolidate> { + using type = type_list_push_front>::type, E1>; }; template -struct dim_consolidate, exp, ERest...>> { +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>; + using type = conditional>::type, + typename dim_consolidate, ERest...>>::type>; }; /** @@ -168,7 +158,7 @@ struct extract; template<> struct extract<> { - using type = derived_dimension<>; + using type = exp_list<>; }; template @@ -182,12 +172,21 @@ struct extract, ERest...> { }; template -struct extract, Num, Den>, ERest...> { +struct extract, Num, Den>, ERest...> { using type = type_list_push_front::type, exp_multiply...>; }; +template +struct to_derived_dimension_base; + +template +struct to_derived_dimension_base> { + using type = derived_dimension_base; +}; + + /** - * @brief Converts user provided derived dimension specification into a valid units::derived_dimension definition + * @brief Converts user provided derived dimension specification into a valid units::derived_dimension_base 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: @@ -197,7 +196,7 @@ struct extract, Num, Den>, ERest...> { * this base dimension. */ template -using make_dimension = dim_consolidate::type, exp_less>>::type; +using make_dimension = to_derived_dimension_base::type, exp_less>>::type>::type; template requires (E::den == 1 || E::den == 2) // TODO provide support for any den @@ -209,31 +208,31 @@ struct exp_ratio { using type = conditional, pow>; }; -template +template struct base_units_ratio_impl; template -struct base_units_ratio_impl> { - using type = ratio_multiply::type, typename base_units_ratio_impl>::type>; +struct base_units_ratio_impl> { + using type = ratio_multiply::type, typename base_units_ratio_impl>::type>; }; template -struct base_units_ratio_impl> { +struct base_units_ratio_impl> { using type = exp_ratio::type; }; /** * @brief Calculates the common ratio of all the references of base units in the derived dimension */ -template -using base_units_ratio = base_units_ratio_impl::type; +template +using base_units_ratio = base_units_ratio_impl::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 + * This is the user's 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. * @@ -241,21 +240,18 @@ using base_units_ratio = base_units_ratio_impl::type; * of powers of base units with no other proportionality factor than one. * * 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. + * and their factors and putting them to derived_dimension_base class template. * * @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 + * @tparam U a coherent unit of a derived dimension + * @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; +struct derived_dimension : downcast_child> { + using recipe = exp_list; using coherent_unit = U; - using base_units_ratio = detail::base_units_ratio>::downcast_base_type>; + using base_units_ratio = detail::base_units_ratio; }; } // namespace units diff --git a/src/include/units/unit.h b/src/include/units/unit.h index cc7973d8..c465c4c8 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -164,7 +164,7 @@ template (U::is_named && (URest::is_named && ... && true)) struct deduced_unit : downcast_child> { static constexpr bool is_named = false; - static constexpr auto symbol = detail::deduced_symbol_text(); + static constexpr auto symbol = detail::deduced_symbol_text(); using prefix_type = no_prefix; }; diff --git a/test/unit_test/static/dimension_op_test.cpp b/test/unit_test/static/dimension_op_test.cpp index 900a8b02..0da9dd31 100644 --- a/test/unit_test/static/dimension_op_test.cpp +++ b/test/unit_test/static/dimension_op_test.cpp @@ -28,13 +28,13 @@ using namespace units; namespace { -struct u0 : unit {}; +struct u0 : named_unit {}; struct d0 : base_dimension<"d0", u0> {}; -struct u1 : unit {}; +struct u1 : named_unit {}; struct d1 : base_dimension<"d1", u1> {}; -struct u2 : unit {}; +struct u2 : named_unit {}; struct d2 : base_dimension<"d2", u2> {}; -struct u3 : unit {}; +struct u3 : named_unit {}; struct d3 : base_dimension<"d3", u3> {}; // exp_invert @@ -50,77 +50,67 @@ struct typeinfo; template using extract = detail::extract::type; -static_assert(std::is_same_v, derived_dimension<>>); -static_assert(std::is_same_v>, derived_dimension>>); -static_assert(std::is_same_v, exp>, derived_dimension, exp>>); -using dim0 = derived_dimension<>; -using dim1 = derived_dimension>; -using dim2 = derived_dimension, exp>; -static_assert(std::is_same_v, exp>, derived_dimension>>); -static_assert(std::is_same_v, exp>, derived_dimension, exp>>); +static_assert(std::is_same_v, exp_list<>>); +static_assert(std::is_same_v>, exp_list>>); +static_assert(std::is_same_v, exp>, exp_list, exp>>); +using dim1 = derived_dimension_base>; +using dim2 = derived_dimension_base, exp>; +static_assert(std::is_same_v, exp>, exp_list, exp>>); static_assert(std::is_same_v, exp, exp>, - derived_dimension, exp, exp, exp>>); + exp_list, exp, exp, exp>>); // dim_invert -static_assert(std::is_same_v>>, d0>); -static_assert(std::is_same_v>>, unknown_dimension>>); +static_assert(std::is_same_v>>, d0>); +static_assert(std::is_same_v>>, unknown_dimension>>); static_assert( - std::is_same_v, exp>>, unknown_dimension, exp>>); + std::is_same_v, exp>>, unknown_dimension, exp>>); // make_dimension template using make_dimension = detail::make_dimension; -static_assert(std::is_same_v>, derived_dimension>>); -static_assert(std::is_same_v, exp>, derived_dimension, exp>>); -static_assert(std::is_same_v, exp>, derived_dimension, exp>>); -static_assert(std::is_same_v, exp>, derived_dimension>>); -static_assert(std::is_same_v, exp>, derived_dimension<>>); -static_assert(std::is_same_v, exp>, derived_dimension>>); -static_assert(std::is_same_v, exp>, derived_dimension>>); -static_assert(std::is_same_v, exp>, derived_dimension>>); +static_assert(std::is_same_v>, derived_dimension_base>>); +static_assert(std::is_same_v, exp>, derived_dimension_base, exp>>); +static_assert(std::is_same_v, exp>, derived_dimension_base, exp>>); +static_assert(std::is_same_v, exp>, derived_dimension_base>>); +static_assert(std::is_same_v, exp>, derived_dimension_base>>); +static_assert(std::is_same_v, exp>, derived_dimension_base>>); +static_assert(std::is_same_v, exp>, derived_dimension_base>>); static_assert(std::is_same_v, exp, exp, exp>, - derived_dimension, exp>>); + derived_dimension_base, exp>>); static_assert(std::is_same_v, exp, exp, exp>, - derived_dimension, exp>>); + derived_dimension_base, exp>>); -static_assert(std::is_same_v, exp, exp>, derived_dimension>>); -static_assert(std::is_same_v, exp, exp>, derived_dimension>>); -static_assert(std::is_same_v, exp, exp>, derived_dimension>>); -static_assert(std::is_same_v, exp, exp, exp>, derived_dimension<>>); +static_assert(std::is_same_v, exp, exp>, derived_dimension_base>>); +static_assert(std::is_same_v, exp, exp>, derived_dimension_base>>); +static_assert(std::is_same_v, exp, exp>, derived_dimension_base>>); // dimension_multiply -static_assert(std::is_same_v>, derived_dimension>>, +static_assert(std::is_same_v>, derived_dimension_base>>, unknown_dimension, exp>>); static_assert( - std::is_same_v>, d1>, unknown_dimension, exp>>); + std::is_same_v>, d1>, unknown_dimension, exp>>); static_assert( - std::is_same_v>>, unknown_dimension, exp>>); + std::is_same_v>>, unknown_dimension, exp>>); static_assert(std::is_same_v, unknown_dimension, exp>>); static_assert(std::is_same_v< - dimension_multiply, exp, exp>, derived_dimension>>, + dimension_multiply, exp, exp>, derived_dimension_base>>, unknown_dimension, exp, exp, exp>>); static_assert(std::is_same_v< - dimension_multiply, exp, exp>, derived_dimension>>, + dimension_multiply, exp, exp>, derived_dimension_base>>, unknown_dimension, exp, exp>>); static_assert(std::is_same_v< - dimension_multiply, exp, exp>, derived_dimension>>, + dimension_multiply, exp, exp>, derived_dimension_base>>, unknown_dimension, exp>>); -static_assert(std::is_same_v>, derived_dimension>>, - unknown_dimension<>>); -static_assert(std::is_same_v>, derived_dimension>>, d0>); +static_assert(std::is_same_v>, derived_dimension_base>>, d0>); // dimension_divide -static_assert(std::is_same_v>, derived_dimension>>, +static_assert(std::is_same_v>, derived_dimension_base>>, unknown_dimension, exp>>); -static_assert(std::is_same_v>, derived_dimension>>, - unknown_dimension<>>); -static_assert(std::is_same_v>, derived_dimension>>, - unknown_dimension<>>); -static_assert(std::is_same_v>, unknown_dimension>>, d0>); +static_assert(std::is_same_v>, unknown_dimension>>, d0>); } // namespace diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index 7de30728..ff4edce3 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -237,15 +237,13 @@ static_assert( std::is_same_v() * si::time()), length>); static_assert( std::is_same_v() * si::time()), length>, int>>); -// TODO uncomment below when fixed in gcc -// static_assert(std::is_same_v() * si::time()), -// quantity, exp>, scaled_unit>>>); +static_assert(std::is_same_v() * si::time()), + quantity, units::exp>, scaled_unit>>>); static_assert(std::is_same_v()), frequency>); static_assert(std::is_same_v()), frequency>, int>>); static_assert(std::is_same_v()), si::time>); -// TODO uncomment below when fixed in gcc -// static_assert(std::is_same_v()), -// quantity>, scaled_unit>>>); +static_assert(std::is_same_v()), + quantity>, scaled_unit>>>); static_assert(std::is_same_v() / 1.0), length>); static_assert(std::is_same_v() / length()), double>); static_assert(std::is_same_v() / length()), double>); @@ -253,9 +251,8 @@ static_assert( std::is_same_v() / si::time()), velocity>); static_assert( std::is_same_v() / si::time()), velocity>>>); -// TODO uncomment below when fixed in gcc -// static_assert(std::is_same_v() / length()), -// quantity, exp>, scaled_unit>>>); +static_assert(std::is_same_v() / length()), + quantity, units::exp>, scaled_unit>>>); static_assert(std::is_same_v() % short(1)), length>); static_assert(std::is_same_v() % length(1)), length>); diff --git a/test/unit_test/static/type_list_test.cpp b/test/unit_test/static/type_list_test.cpp index 9c5bec90..f4236a6a 100644 --- a/test/unit_test/static/type_list_test.cpp +++ b/test/unit_test/static/type_list_test.cpp @@ -84,9 +84,9 @@ static_assert( std::is_same_v>::second_list, type_list>); // type_list_merge_sorted -struct u0 : unit {}; +struct u0 : named_unit {}; struct d0 : base_dimension<"d0", u0> {}; -struct u1 : unit {}; +struct u1 : named_unit {}; struct d1 : base_dimension<"d1", u1> {}; static_assert(std::is_same_v>, type_list>, exp_less>, @@ -99,10 +99,10 @@ static_assert(std::is_same_v>, type_ template using exp_sort = type_list_sort; -static_assert(std::is_same_v>>, derived_dimension>>); +static_assert(std::is_same_v>>, exp_list>>); static_assert( - std::is_same_v, exp>>, derived_dimension, exp>>); + std::is_same_v, exp>>, exp_list, exp>>); static_assert( - std::is_same_v, exp>>, derived_dimension, exp>>); + std::is_same_v, exp>>, exp_list, exp>>); } // namespace From 8d1eb4c41595cc8ef7ab6d7af327c622b6737d1f Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 14 Dec 2019 12:23:47 +0100 Subject: [PATCH 35/44] derived_dimension code split to a few smaller files --- src/include/units/bits/base_units_ratio.h | 60 ++++++ .../units/bits/derived_dimension_base.h | 61 ++++++ src/include/units/bits/dim_consolidate.h | 67 ++++++ src/include/units/bits/dim_unpack.h | 59 ++++++ src/include/units/concepts.h | 6 +- src/include/units/derived_dimension.h | 191 +----------------- src/include/units/exp.h | 87 ++++++++ test/unit_test/static/dimension_op_test.cpp | 69 ++++--- 8 files changed, 381 insertions(+), 219 deletions(-) create mode 100644 src/include/units/bits/base_units_ratio.h create mode 100644 src/include/units/bits/derived_dimension_base.h create mode 100644 src/include/units/bits/dim_consolidate.h create mode 100644 src/include/units/bits/dim_unpack.h create mode 100644 src/include/units/exp.h diff --git a/src/include/units/bits/base_units_ratio.h b/src/include/units/bits/base_units_ratio.h new file mode 100644 index 00000000..06ef2a5c --- /dev/null +++ b/src/include/units/bits/base_units_ratio.h @@ -0,0 +1,60 @@ +// 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 + +namespace units::detail { + +template + requires (E::den == 1 || E::den == 2) // TODO provide support for any den +struct exp_ratio { + using base_ratio = E::dimension::base_unit::ratio; + using positive_ratio = conditional, base_ratio>; + static constexpr std::int64_t N = E::num * E::den < 0 ? -E::num : E::num; + using pow = ratio_pow; + using type = conditional, pow>; +}; + +template +struct base_units_ratio_impl; + +template +struct base_units_ratio_impl> { + using type = ratio_multiply::type, typename base_units_ratio_impl>::type>; +}; + +template +struct base_units_ratio_impl> { + using type = exp_ratio::type; +}; + +/** + * @brief Calculates the common ratio of all the references of base units in the derived dimension + */ +template +using base_units_ratio = base_units_ratio_impl::type; + +} // namespace units::detail diff --git a/src/include/units/bits/derived_dimension_base.h b/src/include/units/bits/derived_dimension_base.h new file mode 100644 index 00000000..c58ec217 --- /dev/null +++ b/src/include/units/bits/derived_dimension_base.h @@ -0,0 +1,61 @@ +// 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 + +namespace units::detail { + +/** + * @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 class template is used by the library engine and should not be directly instantiated by the user. + * + * @tparam E a first exponent of a derived dimension + * @tparam ERest zero or more following exponents of a derived dimension + */ +template + requires (BaseDimension && ... && BaseDimension) +struct derived_dimension_base : downcast_base> { + using exponents = exp_list; +}; + +template +struct to_derived_dimension_base; + +template +struct to_derived_dimension_base> { + using type = derived_dimension_base; +}; + +} // namespace units::detail diff --git a/src/include/units/bits/dim_consolidate.h b/src/include/units/bits/dim_consolidate.h new file mode 100644 index 00000000..5a433706 --- /dev/null +++ b/src/include/units/bits/dim_consolidate.h @@ -0,0 +1,67 @@ +// 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 // TODO remove this dependency with #11 + +namespace units::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 = exp_list<>; +}; + +template +struct dim_consolidate> { + using type = exp_list; +}; + +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>; +}; + +} // namespace units::detail diff --git a/src/include/units/bits/dim_unpack.h b/src/include/units/bits/dim_unpack.h new file mode 100644 index 00000000..710ee17b --- /dev/null +++ b/src/include/units/bits/dim_unpack.h @@ -0,0 +1,59 @@ +// 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 + +namespace units::detail { + +/** + * @brief Unpacks the list of potentially derived dimensions to a list containing only base dimensions + * + * @tparam Es Exponents of potentially derived dimensions + */ +template +struct dim_unpack; + +template<> +struct dim_unpack<> { + using type = exp_list<>; +}; + +template +struct dim_unpack, ERest...> { + using type = type_list_push_front::type, exp>; +}; + +template +struct dim_unpack, ERest...> { + using type = dim_unpack, Num, Den>, ERest...>::type; +}; + +template +struct dim_unpack, Num, Den>, ERest...> { + using type = type_list_push_front::type, exp_multiply...>; +}; + +} // namespace units::detail diff --git a/src/include/units/concepts.h b/src/include/units/concepts.h index 46fe4668..3934a93c 100644 --- a/src/include/units/concepts.h +++ b/src/include/units/concepts.h @@ -126,12 +126,16 @@ template concept Exponent = detail::is_exp; // DerivedDimension +namespace detail { + template requires (BaseDimension && ... && BaseDimension) struct derived_dimension_base; +} // namespace detail + template -concept DerivedDimension = is_instantiation, derived_dimension_base>; +concept DerivedDimension = is_instantiation, detail::derived_dimension_base>; // Dimension template diff --git a/src/include/units/derived_dimension.h b/src/include/units/derived_dimension.h index b50c4dde..753c31a4 100644 --- a/src/include/units/derived_dimension.h +++ b/src/include/units/derived_dimension.h @@ -23,168 +23,18 @@ #pragma once #include +#include +#include +#include +#include #include -#include #include -#include -#include +#include namespace units { -/** - * @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; - -template -struct exp_list {}; - -/** - * @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 class template is used by the library engine and should not be directly instantiated by the user. - * - * @tparam E a first exponent of a derived dimension - * @tparam ERest zero or more following exponents of a derived dimension - */ -template - requires (BaseDimension && ... && BaseDimension) -struct derived_dimension_base : downcast_base> { - using exponents = exp_list; -}; - -// 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 = exp_list<>; -}; - -template -struct dim_consolidate> { - using type = exp_list; -}; - -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 = exp_list<>; -}; - -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...>; -}; - -template -struct to_derived_dimension_base; - -template -struct to_derived_dimension_base> { - using type = derived_dimension_base; -}; - - /** * @brief Converts user provided derived dimension specification into a valid units::derived_dimension_base definition * @@ -196,36 +46,7 @@ struct to_derived_dimension_base> { * this base dimension. */ template -using make_dimension = to_derived_dimension_base::type, exp_less>>::type>::type; - -template - requires (E::den == 1 || E::den == 2) // TODO provide support for any den -struct exp_ratio { - using base_ratio = E::dimension::base_unit::ratio; - using positive_ratio = conditional, base_ratio>; - static constexpr std::int64_t N = E::num * E::den < 0 ? -E::num : E::num; - using pow = ratio_pow; - using type = conditional, pow>; -}; - -template -struct base_units_ratio_impl; - -template -struct base_units_ratio_impl> { - using type = ratio_multiply::type, typename base_units_ratio_impl>::type>; -}; - -template -struct base_units_ratio_impl> { - using type = exp_ratio::type; -}; - -/** - * @brief Calculates the common ratio of all the references of base units in the derived dimension - */ -template -using base_units_ratio = base_units_ratio_impl::type; +using make_dimension = to_derived_dimension_base::type, exp_less>>::type>::type; } // namespace detail diff --git a/src/include/units/exp.h b/src/include/units/exp.h new file mode 100644 index 00000000..b2d71d67 --- /dev/null +++ b/src/include/units/exp.h @@ -0,0 +1,87 @@ +// 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 { + +/** + * @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 + requires BaseDimension && BaseDimension +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; + +template +struct exp_list {}; + +} // namespace units diff --git a/test/unit_test/static/dimension_op_test.cpp b/test/unit_test/static/dimension_op_test.cpp index 0da9dd31..4ac7fa61 100644 --- a/test/unit_test/static/dimension_op_test.cpp +++ b/test/unit_test/static/dimension_op_test.cpp @@ -42,75 +42,78 @@ struct d3 : base_dimension<"d3", u3> {}; static_assert(std::is_same_v>, exp>); static_assert(std::is_same_v>, exp>); -// extract +// dim_unpack template struct typeinfo; template -using extract = detail::extract::type; +using dim_unpack = detail::dim_unpack::type; -static_assert(std::is_same_v, exp_list<>>); -static_assert(std::is_same_v>, exp_list>>); -static_assert(std::is_same_v, exp>, exp_list, exp>>); -using dim1 = derived_dimension_base>; -using dim2 = derived_dimension_base, exp>; -static_assert(std::is_same_v, exp>, exp_list, exp>>); -static_assert(std::is_same_v, exp, exp>, +template +using derived_dim = detail::derived_dimension_base; + +static_assert(std::is_same_v, exp_list<>>); +static_assert(std::is_same_v>, exp_list>>); +static_assert(std::is_same_v, exp>, exp_list, exp>>); +using dim1 = derived_dim>; +using dim2 = derived_dim, exp>; +static_assert(std::is_same_v, exp>, exp_list, exp>>); +static_assert(std::is_same_v, exp, exp>, exp_list, exp, exp, exp>>); // dim_invert -static_assert(std::is_same_v>>, d0>); -static_assert(std::is_same_v>>, unknown_dimension>>); +static_assert(std::is_same_v>>, d0>); +static_assert(std::is_same_v>>, unknown_dimension>>); static_assert( - std::is_same_v, exp>>, unknown_dimension, exp>>); + std::is_same_v, exp>>, unknown_dimension, exp>>); // make_dimension template using make_dimension = detail::make_dimension; -static_assert(std::is_same_v>, derived_dimension_base>>); -static_assert(std::is_same_v, exp>, derived_dimension_base, exp>>); -static_assert(std::is_same_v, exp>, derived_dimension_base, exp>>); -static_assert(std::is_same_v, exp>, derived_dimension_base>>); -static_assert(std::is_same_v, exp>, derived_dimension_base>>); -static_assert(std::is_same_v, exp>, derived_dimension_base>>); -static_assert(std::is_same_v, exp>, derived_dimension_base>>); +static_assert(std::is_same_v>, derived_dim>>); +static_assert(std::is_same_v, exp>, derived_dim, exp>>); +static_assert(std::is_same_v, exp>, derived_dim, exp>>); +static_assert(std::is_same_v, exp>, derived_dim>>); +static_assert(std::is_same_v, exp>, derived_dim>>); +static_assert(std::is_same_v, exp>, derived_dim>>); +static_assert(std::is_same_v, exp>, derived_dim>>); static_assert(std::is_same_v, exp, exp, exp>, - derived_dimension_base, exp>>); + derived_dim, exp>>); static_assert(std::is_same_v, exp, exp, exp>, - derived_dimension_base, exp>>); + derived_dim, exp>>); -static_assert(std::is_same_v, exp, exp>, derived_dimension_base>>); -static_assert(std::is_same_v, exp, exp>, derived_dimension_base>>); -static_assert(std::is_same_v, exp, exp>, derived_dimension_base>>); +static_assert(std::is_same_v, exp, exp>, derived_dim>>); +static_assert(std::is_same_v, exp, exp>, derived_dim>>); +static_assert(std::is_same_v, exp, exp>, derived_dim>>); // dimension_multiply -static_assert(std::is_same_v>, derived_dimension_base>>, +static_assert(std::is_same_v>, derived_dim>>, unknown_dimension, exp>>); static_assert( - std::is_same_v>, d1>, unknown_dimension, exp>>); + std::is_same_v>, d1>, unknown_dimension, exp>>); static_assert( - std::is_same_v>>, unknown_dimension, exp>>); + std::is_same_v>>, unknown_dimension, exp>>); static_assert(std::is_same_v, unknown_dimension, exp>>); static_assert(std::is_same_v< - dimension_multiply, exp, exp>, derived_dimension_base>>, + dimension_multiply, exp, exp>, derived_dim>>, unknown_dimension, exp, exp, exp>>); static_assert(std::is_same_v< - dimension_multiply, exp, exp>, derived_dimension_base>>, + dimension_multiply, exp, exp>, derived_dim>>, unknown_dimension, exp, exp>>); static_assert(std::is_same_v< - dimension_multiply, exp, exp>, derived_dimension_base>>, + dimension_multiply, exp, exp>, derived_dim>>, unknown_dimension, exp>>); -static_assert(std::is_same_v>, derived_dimension_base>>, d0>); +static_assert(std::is_same_v>, derived_dim>>, d0>); // dimension_divide -static_assert(std::is_same_v>, derived_dimension_base>>, +static_assert(std::is_same_v>, derived_dim>>, unknown_dimension, exp>>); -static_assert(std::is_same_v>, unknown_dimension>>, d0>); +static_assert(std::is_same_v>, unknown_dimension>>, d0>); } // namespace From 5ef0bb9a9fa00ce3aaeb08f19aad905e3b4f64e8 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 14 Dec 2019 21:12:17 +0100 Subject: [PATCH 36/44] base_dimension more constrained and comparison fixed --- src/include/units/base_dimension.h | 4 +++- src/include/units/concepts.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/include/units/base_dimension.h b/src/include/units/base_dimension.h index 0f1e5309..34546104 100644 --- a/src/include/units/base_dimension.h +++ b/src/include/units/base_dimension.h @@ -48,6 +48,7 @@ namespace units { * @tparam U a base unit to be used for this base dimension */ template + requires U::is_named struct base_dimension { using base_type_workaround = base_dimension; // TODO Replace with is_derived_from_instantiation when fixed static constexpr auto name = Name; @@ -58,7 +59,8 @@ struct base_dimension { // 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 {}; +struct base_dimension_less : std::bool_constant< + D1::name < D2::name || (D1::name == D2::name && D1::base_unit::symbol < D1::base_unit::symbol)> {}; // clang-format on } // namespace units diff --git a/src/include/units/concepts.h b/src/include/units/concepts.h index 3934a93c..efc1fb51 100644 --- a/src/include/units/concepts.h +++ b/src/include/units/concepts.h @@ -90,6 +90,7 @@ concept Unit = is_derived_from_instantiation; // BaseDimension template + requires U::is_named struct base_dimension; namespace detail { From 6596c15238911e9156728befb2c5824d2d39d680 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Sat, 14 Dec 2019 21:16:15 +0100 Subject: [PATCH 37/44] scaled_unit template parameters order fixed (sorry Oliver) --- src/include/units/bits/deduced_unit.h | 2 +- src/include/units/bits/dimension_op.h | 4 ++-- src/include/units/concepts.h | 2 +- src/include/units/unit.h | 16 ++++++++-------- test/unit_test/runtime/fmt_test.cpp | 4 ++-- test/unit_test/static/quantity_test.cpp | 16 ++++++++-------- test/unit_test/static/si_test.cpp | 2 +- test/unit_test/static/unit_test.cpp | 10 +++++----- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/include/units/bits/deduced_unit.h b/src/include/units/bits/deduced_unit.h index 9f36466b..82a28fc6 100644 --- a/src/include/units/bits/deduced_unit.h +++ b/src/include/units/bits/deduced_unit.h @@ -66,6 +66,6 @@ struct derived_ratio, U, URest...> { template using deduced_unit = - scaled_unit::ratio>; + scaled_unit::ratio, typename D::coherent_unit::reference>; } // namespace units::detail diff --git a/src/include/units/bits/dimension_op.h b/src/include/units/bits/dimension_op.h index 6ca7122e..6089dba6 100644 --- a/src/include/units/bits/dimension_op.h +++ b/src/include/units/bits/dimension_op.h @@ -71,8 +71,8 @@ inline constexpr bool equivalent_dim = detail::equivalent_dim_impl::valu * @tparam Es zero or more exponents of a derived dimension */ template -struct unknown_dimension : derived_dimension, scaled_unit>, Es...> { - using coherent_unit = scaled_unit>; +struct unknown_dimension : derived_dimension, scaled_unit, unknown_unit>, Es...> { + using coherent_unit = scaled_unit, unknown_unit>; }; namespace detail { diff --git a/src/include/units/concepts.h b/src/include/units/concepts.h index efc1fb51..00801715 100644 --- a/src/include/units/concepts.h +++ b/src/include/units/concepts.h @@ -82,7 +82,7 @@ template concept UnitRatio = Ratio && (R::num * R::den > 0); // Unit -template +template struct scaled_unit; template diff --git a/src/include/units/unit.h b/src/include/units/unit.h index c465c4c8..7ce63081 100644 --- a/src/include/units/unit.h +++ b/src/include/units/unit.h @@ -49,14 +49,14 @@ namespace units { * @tparam U a unit to use as a reference for this dimension * @tparam R a ratio of a reference unit */ -template -struct scaled_unit : downcast_base> { - using reference = U; +template +struct scaled_unit : downcast_base> { using ratio = R; + using reference = U; }; template -using downcast_unit = downcast::reference, R>>; +using downcast_unit = downcast::reference>>; template struct same_unit_reference : std::is_same {}; @@ -70,7 +70,7 @@ struct same_unit_reference : std::is_same -struct unit : downcast_child>> { +struct unit : downcast_child, Child>> { static constexpr bool is_named = false; using prefix_type = no_prefix; }; @@ -95,7 +95,7 @@ struct unknown_unit : unit {}; * @tparam PT no_prefix or a type of prefix family */ template -struct named_unit : downcast_child>> { +struct named_unit : downcast_child, Child>> { static constexpr bool is_named = true; static constexpr auto symbol = Symbol; using prefix_type = PT; @@ -116,7 +116,7 @@ struct named_unit : downcast_child>> { * @tparam U a reference unit to scale */ template -struct named_scaled_unit : downcast_child>> { +struct named_scaled_unit : downcast_child, typename U::reference>> { static constexpr bool is_named = true; static constexpr auto symbol = Symbol; using prefix_type = PT; @@ -140,7 +140,7 @@ template // ratio_multiply, // typename U::reference> {}; struct prefixed_unit : - downcast_child>> { + downcast_child, typename U::reference>> { static constexpr bool is_named = true; static constexpr auto symbol = P::symbol + U::symbol; using prefix_type = P::prefix_type; diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index b3123215..c4c3aa03 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -110,7 +110,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") { SECTION("in terms of base units") { - const length>> q(123); + const length, metre>> q(123); stream << q; SECTION("iostream") @@ -131,7 +131,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("in terms of derived units") { - const energy>> q(60); + const energy, joule>> q(60); stream << q; SECTION("iostream") diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index ff4edce3..7dd965f3 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -112,7 +112,7 @@ using namespace units::si; // constexpr quantity error(0); // should not compile (unit of a different dimension) // constexpr quantity> error(0); // should not compile (quantity used as Rep) // constexpr quantity error(0); // should not compile (reordered arguments) -// constexpr quantity>, int> error(0); // should not compile (negative unit ratio) +// constexpr quantity, metre>, int> error(0); // should not compile (negative unit ratio) // member types @@ -236,23 +236,23 @@ static_assert(std::is_same_v()), length() * si::time()), length>); static_assert( - std::is_same_v() * si::time()), length>, int>>); + std::is_same_v() * si::time()), length, metre>, int>>); static_assert(std::is_same_v() * si::time()), - quantity, units::exp>, scaled_unit>>>); + quantity, units::exp>, scaled_unit, unknown_unit>>>); static_assert(std::is_same_v()), frequency>); -static_assert(std::is_same_v()), frequency>, int>>); +static_assert(std::is_same_v()), frequency, hertz>, int>>); static_assert(std::is_same_v()), si::time>); static_assert(std::is_same_v()), - quantity>, scaled_unit>>>); + quantity>, scaled_unit, unknown_unit>>>); static_assert(std::is_same_v() / 1.0), length>); static_assert(std::is_same_v() / length()), double>); static_assert(std::is_same_v() / length()), double>); static_assert( std::is_same_v() / si::time()), velocity>); static_assert( - std::is_same_v() / si::time()), velocity>>>); + std::is_same_v() / si::time()), velocity, metre_per_second>>>); static_assert(std::is_same_v() / length()), - quantity, units::exp>, scaled_unit>>>); + quantity, units::exp>, scaled_unit, unknown_unit>>>); static_assert(std::is_same_v() % short(1)), length>); static_assert(std::is_same_v() % length(1)), length>); @@ -316,7 +316,7 @@ static_assert(std::is_same_v, lengt // quantity_cast -static_assert(std::is_same_v>>(2km))::unit, metre>); +static_assert(std::is_same_v, metre>>(2km))::unit, metre>); static_assert(quantity_cast>(2km).count() == 2000); static_assert(quantity_cast>(2000m).count() == 2); diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp index 1fec088e..19a3cb8e 100644 --- a/test/unit_test/static/si_test.cpp +++ b/test/unit_test/static/si_test.cpp @@ -191,7 +191,7 @@ static_assert(10V * 1F == 10C); // velocity -static_assert(std::is_same_v>, std::int64_t>>); +static_assert(std::is_same_v, metre_per_second>, std::int64_t>>); static_assert(10m / 5s == 2mps); static_assert(10 / 5s * 1m == 2mps); diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index 84dc5e10..f7b7aa42 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -45,11 +45,11 @@ struct metre_per_second : unit {}; struct dim_velocity : derived_dimension, exp> {}; struct kilometre_per_hour : deduced_unit {}; -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(std::is_same_v, metre>>, metre>); +static_assert(std::is_same_v, metre>>, centimetre>); +static_assert(std::is_same_v, metre>>, yard>); +static_assert(std::is_same_v>, metre>>, foot>); +static_assert(std::is_same_v, metre_per_second>>, kilometre_per_hour>); static_assert(centimetre::symbol == "cm"); static_assert(kilometre::symbol == "km"); From 00d863508d524cc8365610c826f0e909e292ead8 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Mon, 16 Dec 2019 15:58:41 +0100 Subject: [PATCH 38/44] base_dimension refactored - now stores dimension's symbol rather than its name - temperature and current renamed to exactly match their names in SI --- src/include/units/base_dimension.h | 14 ++++++------ src/include/units/bits/dimension_op.h | 2 +- src/include/units/concepts.h | 2 +- src/include/units/physical/dimensions.h | 22 +++++++++---------- src/include/units/physical/si/current.h | 4 ++-- .../units/physical/si/electric_charge.h | 2 +- src/include/units/physical/si/temperature.h | 4 ++-- src/include/units/physical/si/voltage.h | 2 +- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/include/units/base_dimension.h b/src/include/units/base_dimension.h index 34546104..5b8d020f 100644 --- a/src/include/units/base_dimension.h +++ b/src/include/units/base_dimension.h @@ -38,20 +38,20 @@ namespace units { * * Base unit is a measurement unit that is adopted by convention for a base quantity 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 od a DerivedDimension + * Pair of Symbol and Unit template parameters form 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 of a DerivedDimension * (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 + * the Symbol 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 Symbol 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 +template requires U::is_named struct base_dimension { using base_type_workaround = base_dimension; // TODO Replace with is_derived_from_instantiation when fixed - static constexpr auto name = Name; + static constexpr auto symbol = Symbol; using base_unit = U; }; @@ -60,7 +60,7 @@ struct base_dimension { // clang-format off template struct base_dimension_less : std::bool_constant< - D1::name < D2::name || (D1::name == D2::name && D1::base_unit::symbol < D1::base_unit::symbol)> {}; + D1::symbol < D2::symbol || (D1::symbol == D2::symbol && D1::base_unit::symbol < D1::base_unit::symbol)> {}; // clang-format on } // namespace units diff --git a/src/include/units/bits/dimension_op.h b/src/include/units/bits/dimension_op.h index 6089dba6..8b01b2cd 100644 --- a/src/include/units/bits/dimension_op.h +++ b/src/include/units/bits/dimension_op.h @@ -34,7 +34,7 @@ template struct equivalent_dim_impl : std::false_type {}; template -struct equivalent_base_dim : std::conjunction, +struct equivalent_base_dim : std::conjunction, same_unit_reference> {}; template diff --git a/src/include/units/concepts.h b/src/include/units/concepts.h index 00801715..76f8c654 100644 --- a/src/include/units/concepts.h +++ b/src/include/units/concepts.h @@ -89,7 +89,7 @@ template concept Unit = is_derived_from_instantiation; // BaseDimension -template +template requires U::is_named struct base_dimension; diff --git a/src/include/units/physical/dimensions.h b/src/include/units/physical/dimensions.h index 66460480..ea8ceb51 100644 --- a/src/include/units/physical/dimensions.h +++ b/src/include/units/physical/dimensions.h @@ -40,25 +40,25 @@ concept QuantityOf = Quantity && is_derived_from_instantiation -struct dim_length : base_dimension<"length", U> {}; +struct dim_length : base_dimension<"L", U> {}; template -struct dim_mass : base_dimension<"mass", U> {}; +struct dim_mass : base_dimension<"M", U> {}; template -struct dim_time : base_dimension<"time", U> {}; +struct dim_time : base_dimension<"T", U> {}; template -struct dim_current : base_dimension<"current", U> {}; +struct dim_electric_current : base_dimension<"I", U> {}; template -struct dim_temperature : base_dimension<"temperature", U> {}; +struct dim_thermodynamic_temperature : base_dimension<"Θ", U> {}; template -struct dim_substance : base_dimension<"substance", U> {}; +struct dim_substance : base_dimension<"N", U> {}; template -struct dim_luminous_intensity : base_dimension<"luminous intensity", U> {}; +struct dim_luminous_intensity : base_dimension<"J", U> {}; // ------------------------ derived dimensions ----------------------------- @@ -86,10 +86,10 @@ struct dim_energy : derived_dimension, exp> {}; template E, DimensionOf T> struct dim_power : derived_dimension, exp> {}; -template P, DimensionOf C> +template P, DimensionOf C> struct dim_voltage : derived_dimension, exp> {}; -template T, DimensionOf C> +template T, DimensionOf C> struct dim_electric_charge : derived_dimension, exp> {}; template C, DimensionOf V> @@ -113,10 +113,10 @@ template concept Time = physical::QuantityOf; template -concept Current = physical::QuantityOf; +concept Current = physical::QuantityOf; template -concept Temperature = physical::QuantityOf; +concept Temperature = physical::QuantityOf; template concept Substance = physical::QuantityOf; diff --git a/src/include/units/physical/si/current.h b/src/include/units/physical/si/current.h index 57580451..26b5143b 100644 --- a/src/include/units/physical/si/current.h +++ b/src/include/units/physical/si/current.h @@ -30,10 +30,10 @@ namespace units::si { struct ampere : named_unit {}; -struct dim_current : physical::dim_current {}; +struct dim_electric_current : physical::dim_electric_current {}; template -using current = quantity; +using current = quantity; inline namespace literals { diff --git a/src/include/units/physical/si/electric_charge.h b/src/include/units/physical/si/electric_charge.h index 3b3c5de0..c2059c98 100644 --- a/src/include/units/physical/si/electric_charge.h +++ b/src/include/units/physical/si/electric_charge.h @@ -31,7 +31,7 @@ namespace units::si { struct coulomb : named_unit {}; -struct dim_electric_charge : physical::dim_electric_charge {}; +struct dim_electric_charge : physical::dim_electric_charge {}; template using electric_charge = quantity; diff --git a/src/include/units/physical/si/temperature.h b/src/include/units/physical/si/temperature.h index 27e6ab33..a00ef3b1 100644 --- a/src/include/units/physical/si/temperature.h +++ b/src/include/units/physical/si/temperature.h @@ -29,10 +29,10 @@ namespace units::si { struct kelvin : named_unit {}; -struct dim_temperature : physical::dim_temperature {}; +struct dim_thermodynamic_temperature : physical::dim_thermodynamic_temperature {}; template -using temperature = quantity; +using temperature = quantity; inline namespace literals { diff --git a/src/include/units/physical/si/voltage.h b/src/include/units/physical/si/voltage.h index 5e729032..7138e728 100644 --- a/src/include/units/physical/si/voltage.h +++ b/src/include/units/physical/si/voltage.h @@ -32,7 +32,7 @@ namespace units::si { struct volt : named_unit {}; -struct dim_voltage : physical::dim_voltage {}; +struct dim_voltage : physical::dim_voltage {}; template using voltage = quantity; From d5f7de8ecbf9449d5908f0475322379e1798a4a0 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 17 Dec 2019 08:36:31 +0100 Subject: [PATCH 39/44] unknown_dimension definition changed to contain at least one exponent --- src/include/units/bits/dimension_op.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/include/units/bits/dimension_op.h b/src/include/units/bits/dimension_op.h index 8b01b2cd..0405efae 100644 --- a/src/include/units/bits/dimension_op.h +++ b/src/include/units/bits/dimension_op.h @@ -68,10 +68,11 @@ inline constexpr bool equivalent_dim = detail::equivalent_dim_impl::valu * dimension. In such a case an `unknown_dimension` is created with a coherent unit of `unknown_unit` * and ratio<1>. * - * @tparam Es zero or more exponents of a derived dimension + * @tparam E the list of exponents of ingredient dimensions + * @tparam ERest the list of exponents of ingredient dimensions */ -template -struct unknown_dimension : derived_dimension, scaled_unit, unknown_unit>, Es...> { +template +struct unknown_dimension : derived_dimension, scaled_unit, unknown_unit>, E, ERest...> { using coherent_unit = scaled_unit, unknown_unit>; }; From 1b2d27a64ce109645099bb1f8c74f6acb6a07ff2 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 17 Dec 2019 09:12:11 +0100 Subject: [PATCH 40/44] measurement example updated with a starship operator --- example/measurement.cpp | 156 +++++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 74 deletions(-) diff --git a/example/measurement.cpp b/example/measurement.cpp index 3ef98288..f517cbaa 100644 --- a/example/measurement.cpp +++ b/example/measurement.cpp @@ -26,95 +26,105 @@ namespace { - // root sum of squares - template - T rss(const T& v1, const T& v2) +// root sum of squares +template +T rss(const T& v1, const T& v2) +{ + return std::sqrt(std::pow(v1, 2) + std::pow(v2, 2)); +} + +template +class measurement { +public: + using value_type = T; + + measurement() = default; + + constexpr /* explicit */ measurement(const value_type& val, const value_type& err = {}) : + // cannot be explicit as `magma` concept requires implicit conversions :-( + value_(val), + uncertainty_(std::abs(err)) { - return std::sqrt(std::pow(v1, 2) + std::pow(v2, 2)); } - template - class measurement { - public: - using value_type = T; + constexpr const value_type& value() const { return value_; } + constexpr const value_type& uncertainty() const { return uncertainty_; } - measurement() = default; + constexpr value_type relative_uncertainty() const { return uncertainty() / value(); } + constexpr value_type lower_bound() const { return value() - uncertainty(); } + constexpr value_type upper_bound() const { return value() + uncertainty(); } - constexpr /* explicit */ measurement(const value_type& val, const value_type& err = {}): // cannot be explicit as `magma` concept requires implicit conversions :-( - value_(val), uncertainty_(std::abs(err)) - { - } + [[nodiscard]] constexpr measurement operator-() const { return measurement(-value(), uncertainty()); } - constexpr const value_type& value() const { return value_; } - constexpr const value_type& uncertainty() const { return uncertainty_; } + [[nodiscard]] friend constexpr measurement operator+(const measurement& lhs, const measurement& rhs) + { + return measurement(lhs.value() + rhs.value(), rss(lhs.uncertainty(), rhs.uncertainty())); + } - constexpr value_type relative_uncertainty() const { return uncertainty() / value(); } - constexpr value_type lower_bound() const { return value() - uncertainty(); } - constexpr value_type upper_bound() const { return value() + uncertainty(); } + [[nodiscard]] friend constexpr measurement operator-(const measurement& lhs, const measurement& rhs) + { + return measurement(lhs.value() - rhs.value(), rss(lhs.uncertainty(), rhs.uncertainty())); + } - [[nodiscard]] constexpr measurement operator-() const { return measurement(-value(), uncertainty()); } + [[nodiscard]] friend constexpr measurement operator*(const measurement& lhs, const measurement& rhs) + { + const auto val = lhs.value() * rhs.value(); + return measurement(val, val * rss(lhs.relative_uncertainty(), rhs.relative_uncertainty())); + } - [[nodiscard]] friend constexpr measurement operator+(const measurement& lhs, const measurement& rhs) - { - return measurement(lhs.value() + rhs.value(), rss(lhs.uncertainty(), rhs.uncertainty())); - } + [[nodiscard]] friend constexpr measurement operator/(const measurement& lhs, const measurement& rhs) + { + const auto val = lhs.value() / rhs.value(); + return measurement(val, val * rss(lhs.relative_uncertainty(), rhs.relative_uncertainty())); + } - [[nodiscard]] friend constexpr measurement operator-(const measurement& lhs, const measurement& rhs) - { - return measurement(lhs.value() - rhs.value(), rss(lhs.uncertainty(), rhs.uncertainty())); - } +#if __GNUC__ >= 10 - [[nodiscard]] friend constexpr measurement operator*(const measurement& lhs, const measurement& rhs) - { - const auto val = lhs.value() * rhs.value(); - return measurement(val, val * rss(lhs.relative_uncertainty(), rhs.relative_uncertainty())); - } + [[nodiscard]] friend constexpr auto operator<=>(const measurement& lhs, const measurement& rhs) = default; + [[nodiscard]] friend constexpr bool operator==(const measurement& lhs, const measurement& rhs) = default; - [[nodiscard]] friend constexpr measurement operator/(const measurement& lhs, const measurement& rhs) - { - const auto val = lhs.value() / rhs.value(); - return measurement(val, val * rss(lhs.relative_uncertainty(), rhs.relative_uncertainty())); - } +#else - [[nodiscard]] friend constexpr bool operator==(const measurement& lhs, const measurement& rhs) - { - return lhs.value() == rhs.value() && lhs.uncertainty() == rhs.uncertainty(); - } + [[nodiscard]] friend constexpr bool operator==(const measurement& lhs, const measurement& rhs) + { + return lhs.value() == rhs.value() && lhs.uncertainty() == rhs.uncertainty(); + } - [[nodiscard]] friend constexpr bool operator!=(const measurement& lhs, const measurement& rhs) - { - return !(lhs == rhs); - } + [[nodiscard]] friend constexpr bool operator!=(const measurement& lhs, const measurement& rhs) + { + return !(lhs == rhs); + } - [[nodiscard]] friend constexpr bool operator<(const measurement& lhs, const measurement& rhs) - { - return lhs.value() == rhs.value() ? lhs.uncertainty() < rhs.uncertainty() : lhs.value() < rhs.value(); - } + [[nodiscard]] friend constexpr bool operator<(const measurement& lhs, const measurement& rhs) + { + return lhs.value() == rhs.value() ? lhs.uncertainty() < rhs.uncertainty() : lhs.value() < rhs.value(); + } - [[nodiscard]] friend constexpr bool operator>(const measurement& lhs, const measurement& rhs) - { - return rhs < lhs; - } + [[nodiscard]] friend constexpr bool operator>(const measurement& lhs, const measurement& rhs) { return rhs < lhs; } - [[nodiscard]] friend constexpr bool operator<=(const measurement& lhs, const measurement& rhs) - { - return !(rhs < lhs); - } + [[nodiscard]] friend constexpr bool operator<=(const measurement& lhs, const measurement& rhs) + { + return !(rhs < lhs); + } - [[nodiscard]] friend constexpr bool operator>=(const measurement& lhs, const measurement& rhs) - { - return !(lhs < rhs); - } + [[nodiscard]] friend constexpr bool operator>=(const measurement& lhs, const measurement& rhs) + { + return !(lhs < rhs); + } - friend std::ostream& operator<<(std::ostream& os, const measurement& v) - { - return os << v.value() << " ± " << v.uncertainty(); - } +#endif - private: - value_type value_{}; - value_type uncertainty_{}; - }; + friend std::ostream& operator<<(std::ostream& os, const measurement& v) + { + return os << v.value() << " ± " << v.uncertainty(); + } + +private: + value_type value_{}; + value_type uncertainty_{}; +}; + +static_assert(units::Scalar>); } // namespace @@ -137,17 +147,15 @@ void example() std::cout << "10 * " << length << " = " << 10 * length << '\n'; } -} // namespace +} // namespace int main() { try { example(); - } - catch (const std::exception& ex) { + } catch (const std::exception& ex) { std::cerr << "Unhandled std exception caught: " << ex.what() << '\n'; - } - catch (...) { + } catch (...) { std::cerr << "Unhandled unknown exception caught\n"; } } From 0bd7951175bd3ef39926cc73f58bf930a10a8d81 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 17 Dec 2019 09:14:05 +0100 Subject: [PATCH 41/44] Design documentation updated --- doc/DESIGN.md | 1158 ++++++++++++++++++++++---------------------- doc/design.png | Bin 0 -> 27339 bytes doc/downcast_1.png | Bin 6620 -> 10899 bytes doc/downcast_2.png | Bin 22448 -> 32810 bytes doc/nomnoml.md | 46 ++ doc/units.png | Bin 0 -> 33552 bytes doc/units_uml.png | Bin 30286 -> 0 bytes 7 files changed, 626 insertions(+), 578 deletions(-) create mode 100644 doc/design.png create mode 100644 doc/nomnoml.md create mode 100644 doc/units.png delete mode 100644 doc/units_uml.png diff --git a/doc/DESIGN.md b/doc/DESIGN.md index 9c3a1db9..2d543005 100644 --- a/doc/DESIGN.md +++ b/doc/DESIGN.md @@ -1,9 +1,9 @@ -# `mp-units` - A Units Library for C++ +# `mp-units` - Design Overview ## Summary -`Units` is a compile-time enabled Modern C++ library that provides compile-time dimensional +`mp-units` is a compile-time enabled Modern C++ library that provides compile-time dimensional analysis and unit/quantity manipulation. The basic idea and design heavily bases on `std::chrono::duration` and extends it to work properly with many dimensions. @@ -46,48 +46,237 @@ static_assert(10km / 5km == 2); ## Basic Concepts -Below UML diagram shows the most important entities in the library design and how they relate to -each other: +The most important concepts in the library are `Unit`, `Dimension`, and `Quantity`: -![UML](units_uml.png) +![Design UML](design.png) -### `Dimensions` +`Unit` is a basic building block of the library. Every dimension works with a concrete +hierarchy of units. Such hierarchy defines a reference unit and often a few scaled versions of +it. -`units::dimension` represents a derived dimension and is implemented as a type-list like type that -stores an ordered list of exponents of one or more base dimensions: +`Dimension` concept matches a dimension of either a base or derived quantity. `base_dimension` +is instantiated with a unique symbol identifier and a base unit. `derived_unit` is a list of +exponents of either base or other derived dimensions. + +`Quantity` is a concrete amount of a unit for a specified dimension with a specific +representation. + + +## `Unit` + +All units are represented in the framework by a `scaled_unit` class template: ```cpp -template -struct dimension : downcast_base> {}; +template +struct scaled_unit : downcast_base> { + using ratio = R; + using reference = U; +}; ``` -`units::Dimension` is a concept that is satisfied by a type that is empty and publicly -derived from `units::dimension` class template: +The above type is a framework's private type and the user should never instantiate it directly. +The public user interface to create units consists of: + +![Units UML](units.png) + +All below class templates indirectly derive from a `scaled_unit` class template and satisfy a +`Unit` concept: +- `unit` + - Defines a new unnamed, in most cases coherent derived unit of a specific derived + dimension and it should be passed in this dimension's definition. +- `named_unit` + - Defines a named, in most cases base or coherent unit that is then passed to a dimension's + definition. + - A named unit may be used by other units defined with the prefix of the same type, unless + `no_prefix` is provided for `PrefixType` template parameter (in such a case it is impossible + to define a prefixed unit based on this one). +- `named_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 `PrefixType` template parameter (in such a case it is impossible + to define a prefixed unit based on this one). +- `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. +- `deduced_unit` + - 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 the derived dimension. + - All of the units provided should also be a named ones so it is possible to create a deduced + symbol text. + +Some of the above types depend on `PrefixType` and `no_prefix`. `PrefixType` is a concept that +is defined as: ```cpp template -concept Dimension = - std::is_empty_v && - detail::is_dimension>; // exposition only +concept PrefixType = std::derived_from; ``` -#### `Exponents` +where `prefix_type` is just an empty tag type used to identify the beginning of prefix types +hierarchy and `no_prefix` is one of its children: -`units::Exponent` concept is satisfied if provided type is an instantiation of `units::exp` class -template: +```cpp +struct prefix_type {}; +struct no_prefix : prefix_type {}; +``` + +Concrete prefix derives from a `prefix` class template: + +```cpp +template + requires (!std::same_as) +struct prefix; +``` + +You could notice that both units and above `prefix` class template take `Child` as a first +template parameter. `mp-units` library heavily relies on CRTP (Curiously Recurring Template +Parameter) idiom to provide the best user experience in terms of readability of compilation +errors and during debugging. It is possible thanks to the downcasting facility described later +in the design documentation. + +Coming back to units, here are a few examples of unit definitions: + +```cpp +namespace units::si { + +// prefixes +struct prefix : prefix_type {}; +struct centi : units::prefix> {}; +struct kilo : units::prefix> {}; + +// length +struct metre : named_unit {}; +struct centimetre : prefixed_unit {}; +struct kilometre : prefixed_unit {}; +struct yard : named_scaled_unit, metre> {}; +struct mile : named_scaled_unit, yard> {}; + +// time +struct second : named_unit {}; +struct hour : named_scaled_unit, second> {}; + +// velocity +struct metre_per_second : unit {}; +struct kilometre_per_hour : deduced_unit {}; +struct mile_per_hour : deduced_unit {}; + +} +``` + +Please note that thanks to C++20 features we are able to provide all information about the unit +(including text output) in a single line of its type definition. There is no need to specialize +additional type traits or use preprocessor macros. + + +## `Dimension` + +`Dimension` is either a `BaseDimension` or a `DerivedDimension`: ```cpp template -concept Exponent = - detail::is_exp; // exposition only +concept Dimension = BaseDimension || DerivedDimension; ``` -`units::exp` provides an information about a single dimension and its (possibly fractional) -exponent in a derived dimension. +### `BaseDimension` + +According to ISO 80000 a base quantity is a quantity in a conventionally chosen subset of a +given system of quantities, where no quantity in the subset can be expressed in terms of the +other quantities within that subset. They are referred to as being mutually independent since a +base quantity cannot be expressed as a product of powers of the other base quantities. Base unit +is a measurement unit that is adopted by convention for a base quantity in a specific system of +units. + +`base_dimension` represents a dimension of a base quantity and is identified with a pair of +an unique compile-time text describing the dimension symbol and a base unit adopted for this +dimension: ```cpp -template - requires BaseDimension || Dimension +template + requires U::is_named +struct base_dimension { + static constexpr auto symbol = Symbol; + using base_unit = U; +}; +``` + +Pair of symbol and unit template parameters form an unique identifier of the base dimension. +These identifiers provide total ordering of exponents of base dimensions in a derived dimension. + +The SI physical units system defines 7 base dimensions: + +```cpp +namespace units::si { + +struct dim_length : base_dimension<"L", metre> {}; +struct dim_mass : base_dimension<"M", kilogram> {}; +struct dim_time : base_dimension<"T", second> {}; +struct dim_electric_current : base_dimension<"I", ampere> {}; +struct dim_thermodynamic_temperature : base_dimension<"Θ", kelvin> {}; +struct dim_substance : base_dimension<"N", mole> {}; +struct dim_luminous_intensity : base_dimension<"J", candela> {}; + +} +``` + +All other physical dimensions are derived from those. + +There are two reasons why `base_dimension` gets a unit as its template parameter. First, the +base unit is needed for the text output of unnamed derived units. Second, there is more than +one system of physical units. For example CGS definitions look as follows: + +```cpp +namespace units::cgs { + +using si::centimetre; +using si::gram; +using si::second; + +struct dim_length : base_dimension<"L", centimetre> {}; +struct dim_mass : base_dimension<"M", gram> {}; +using si::dim_time; + +} +``` + +Equivalent base dimensions in different systems have the same symbol identifier and get units +from the same hierarchy (with the same reference in `scaled_unit`). Thanks to that we have +the ability to explicitly cast quantities of the same dimension from different systems or +even mix them in one `derived_dimension` definition. + + +### `DerivedDimension` + +According to ISO 80000 a derived quantity is a quantity, in a system of quantities, defined in +terms of the base quantities of that system. Dimension of such quantity is an expression of the +dependence of a quantity on the base quantities 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. Each factor is the dimension of a base +quantity. + +A derived dimension used internally in a library framework is implemented as a type-list like +type that stores an ordered list of exponents of one or more base dimensions: + +```cpp +namespace detail { + +template + requires (BaseDimension && ... && BaseDimension) +struct derived_dimension_base; + +} +``` + +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`). + +Exponents are implemented with `exp` class template that provides an information about a single +dimension and its (possibly fractional) exponent in a derived dimension. + +```cpp +template struct exp { using dimension = Dim; static constexpr int num = Num; @@ -95,434 +284,236 @@ struct exp { }; ``` -Both a base dimension and a derived dimension can be provided to `units::exp` class template. +In order to be able to perform computations on an arbitrary set of exponents, +`derived_dimension_base` class template have to obey the following rules: +- it contains only base dimensions in the list of exponents, +- base dimensions are not repeated in a list (the exponent of each base dimension is provided + at most once), +- exponents of base dimensions are consistently ordered, +- in case the numerator of the exponent equals zero such base dimension is erased from the list. -`units::base_dimension` represents a base dimension and has assigned a unique compile-time text -describing the dimension name: +Above is needed for the framework to provide dimensional analysis. However, sometimes it is +useful to define derived units in terms of other derived units. To support this both a base +dimension and a derived dimension can be provided to `exp` class template. + +As it was stated above `derived_dimension_base` is a private utility of the framework. In order +to define a new derived dimension the user has to instantiate the following class template: ```cpp -template -struct base_dimension { - using name = Name; - using symbol = Symbol; +template +struct derived_dimension : downcast_child> { + using recipe = exp_list; + using coherent_unit = U; + using base_units_ratio = /* see below */; }; ``` -`units::BaseDimension` is a concept to match all types derived from `base_dimension` instantiations: +There are a few important differences between `detail::derived_dimension_base` and +`derived_dimension`. First, the latter one gets the coherent unit of the derived dimension. + +According to ISO 80000 a 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. + +The other difference is that `derived_dimension` allows to provide other derived dimensions in +the list of its exponents. This is called a "recipe" of the dimension and among others is used +to print unnamed coherent units of this dimension. + +In case a derived dimension appears on the list of exponents, such derived dimension will be +unpacked, sorted, and consolidated by a `detail::make_dimension` helper to form a valid list +of exponents of only base dimensions later provided to `detail::derived_dimension_base`. + +Sometimes units of equivalent quantities in different systems of units do not share the same +reference so they cannot be easily converted to each other. An example can be a pressure for +which a coherent unit in SI is pascal and in CGS barye. Those two units are not directly +related with each other with some ratio. As they both are coherent units of their dimensions, +the ratio between them is directly determined by the ratios of base units defined in base +dimensions end their exponents in the derived dimension recipe. To provide interoperability of +such quantities of different systems `base_units_ratio` is being used. The result of the +division of two `base_units_ratio` of two quantities of equivalent dimensions in two different +systems gives a ratio between their coherent units. Alternatively, the user would always have to +directly define a barye in terms of pascal or vice versa. + +Below are a few examples of derived dimension definitions: ```cpp -template -concept BaseDimension = std::is_empty_v && - requires { - typename T::name; - typename T::symbol; - } && - std::derived_from>; +namespace units::si { + +struct dim_velocity : derived_dimension, exp> {}; + +struct dim_acceleration : derived_dimension, exp> {}; + +struct dim_force : derived_dimension, exp> {}; + +struct dim_energy : derived_dimension, exp> {}; + +struct dim_power : derived_dimension, exp> {}; + +} ``` -For example here is a list of SI base dimensions: +If as a result of dimensional computation the library framework will generate a derived +dimension that was not predefined by the user than the instance of +`unknown_dimension`. The coherent unit of such an unknown dimension is +`scaled_unit, unknown_unit>`. -```cpp -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"> {}; -``` -In order to be able to perform computations on arbitrary sets of base dimensions, an important -property of `units::dimension` is that its base dimensions: -- are not repeated in a list (each base dimension is provided at most once), -- are consistently ordered, -- having zero exponent are elided. +## `Quantity` - -#### `derived_dimension` - -Above design of dimensions is created with the ease of use for end users in mind. Compile-time -errors should provide as short as possible template instantiations strings that should be easy to -understand by every engineer. Also types visible in a debugger should be easy to understand. -That is why `units::dimension` type for derived dimensions always stores information about only -those base dimensions that are used to form that derived dimension. - -However, such an approach have some challenges: - -```cpp -constexpr Velocity auto v1 = 1_m / 1s; -constexpr Velocity auto v2 = 2 / 2s * 1m; - -static_assert(std::same_as); -static_assert(v1 == v2); -``` - -Above code, no matter what is the order of the base dimensions in an expression forming our result, -must produce the same `Velocity` type so that both values can be easily compared. In order to achieve -that, `dimension` class templates should never be instantiated manually but through a `derived_dimension` -helper: - -```cpp -template -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 -described later in this document. - -So for example to create a `velocity` type we have to do: - -```cpp -struct velocity : derived_dimension, exp> {}; -``` - -In order to make `derived_dimension` work as expected it has to provide unique ordering for -contained base dimensions. Beside providing ordering to base dimensions it also has to: -- aggregate two arguments of the same base dimension but different exponents -- 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. In such a case units defined with a -`coherent_derived_unit` and `deduced_derived_unit` helper will get symbols of units for those -derived dimension (if those are named units) rather than system base units. - -For example to form `pressure` user can provide the following two definitions: - -```cpp -struct pressure : derived_dimension, exp, exp> {}; -``` - -or - -```cpp -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 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` - -`units::merge_dimension` is a type alias that works similarly to `derived_dimension` but instead -of sorting the whole list of base dimensions from scratch it assumes that provided input `dimension` -types are already sorted as a result of `derived_dimension`. Also contrary to `derived_dimension` -it works only with exponents of bas dimensions (no derived dimensions allowed). - -Typical use case for `merge_dimension` is to produce final `dimension` return type of multiplying -two different dimensions: - -```cpp -template -struct dimension_multiply; - -template -struct dimension_multiply, dimension> { - using type = downcast_traits_t, dimension>>; -}; - -template -using dimension_multiply = dimension_multiply::type; -``` - - -### `Units` - -`units::unit` is a class template that expresses the unit of a specific physical dimension: - -```cpp -template - requires (R::num * R::den > 0) -struct unit : downcast_base> { - using dimension = D; - using ratio = R; -}; -``` - -`units::Unit` is a concept that is satisfied by a type that is empty and publicly -derived from `units::unit` class template: - -```cpp -template -concept Unit = - std::is_empty_v && - detail::is_unit>; // exposition only -``` - -The library provides a bunch of helpers to create the derived unit: -- `named_coherent_derived_unit` -- `coherent_derived_unit` -- `named_scaled_derived_unit` -- `named_deduced_derived_unit` -- `deduced_derived_unit` -- `prefixed_derived_unit` - -Coherent derived units (units with `ratio<1>`) are created with a `named_coherent_derived_unit` -or `coherent_derived_unit` class templates: - -```cpp -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 = /* ... */; - using prefix_type = no_prefix; -}; -``` - -The above exposes public `prefix_type` member type and `symbol` used to print unit symbol -names. `prefix_type` is a tag type used to identify the type of prefixes to be used (i.e. SI, -data) and should satisfy the following concept: - -```cpp -template -concept PrefixType = std::derived_from; -``` - -For example to define the named coherent unit of `length`: - -```cpp -struct metre : named_coherent_derived_unit {}; -``` - -Again, similarly to `derived_dimension`, the first class template parameter is a CRTP idiom used -to provide downcasting facility (described below). - -`coherent_derived_unit` also provides a synthetized unit symbol. If all coherent units of -the recipe provided in a `derived_dimension` are named than the recipe is used to built a -unit symbol. Otherwise, the symbol will be created based on ingredient base units. - -```cpp -struct surface_tension : derived_dimension, exp> {}; -struct newton_per_metre : coherent_derived_unit {}; -``` - -To create scaled unit the following template should be used: - -```cpp -template -struct named_scaled_derived_unit : downcast_child> { - static constexpr bool is_named = true; - static constexpr auto symbol = Symbol; - using prefix_type = PT; -}; -``` - -For example to define `minute`: - -```cpp -struct minute : named_scaled_derived_unit> {}; -``` - -The `mp-units` library provides also a helper class templates to simplify the above process. -For example to create a prefixed unit the following may be used: - -```cpp -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; -}; -``` - -where `Prefix` is a concept requiring the instantiation of the following class template: - -```cpp -template -struct prefix : downcast_child> { - static constexpr auto symbol = Symbol; -}; -``` - -With this to create prefixed units user does not have to specify numeric value of the prefix ratio -or its symbol and just has to do the following: - -```cpp -struct kilometre : prefixed_derived_unit {}; -``` - -SI prefixes are predefined in the library and the user may easily provide his/her own with: - -```cpp -struct data_prefix : units::prefix_type {}; - -struct kibi : units::prefix, "Ki"> {}; -struct mebi : units::prefix, "Mi"> {}; -``` - -For the cases where determining the exact ratio is not trivial another helper can be used: - -```cpp -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 = /* even more magic to get the correct unit symbol */; - using prefix_type = no_prefix; -}; -``` - -This will deduce the ratio based on the ingredient units and their relation defined in the -dimension. For example to create a deduced velocity unit a user can do: - -```cpp -struct kilometre_per_hour : deduced_derived_unit {}; -``` - -`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` - -`units::quantity` is a class template that expresses the quantity/amount of a specific dimension +`quantity` is a class template that expresses the quantity/amount of a specific dimension expressed in a specific unit of that dimension: ```cpp -template -class quantity; +template U, Scalar Rep = double> +class quantity ``` -`units::Quantity` is a concept that is satisfied by a type that is an instantiation of -`units::quantity` class template: +`quantity` provides a similar interface to `std::chrono::duration`. The difference is that it +uses `double` as a default representation and has a few additional member types and +functions as below: ```cpp -template -concept Quantity = - detail::is_quantity; // exposition only -``` - -`units::quantity` provides the interface really similar to `std::chrono::duration`. The difference is that -it uses `double` as a default representation and has a few additional member types and functions as below: - -```cpp -template +template U, Scalar Rep = double> class quantity { public: + using dimension = D; using unit = U; using rep = Rep; - using dimension = U::dimension; - - [[nodiscard]] static constexpr quantity one() noexcept { return quantity(quantity_values::one()); } - - template - requires std::same_as> - [[nodiscard]] constexpr Scalar operator*(const quantity& lhs, - const quantity& rhs); - - template - requires (!std::same_as>) && - (treat_as_floating_point || - (std::ratio_multiply::den == 1)) - [[nodiscard]] constexpr Quantity operator*(const quantity& lhs, - const quantity& rhs); - - template - [[nodiscard]] constexpr Quantity operator/(const Rep1& v, - const quantity& q); - - template - requires std::same_as - [[nodiscard]] constexpr Scalar operator/(const quantity& lhs, - const quantity& rhs); - - template - requires (!std::same_as) && - (treat_as_floating_point || - (ratio_divide::den == 1)) - [[nodiscard]] constexpr Quantity operator/(const quantity& lhs, - const quantity& rhs); + [[nodiscard]] static constexpr quantity one() noexcept; // ... }; + +template + requires detail::basic_arithmetic && equivalent_dim> +[[nodiscard]] constexpr Scalar auto operator*(const quantity& lhs, + const quantity& rhs); + +template + requires detail::basic_arithmetic && (!equivalent_dim>) +[[nodiscard]] constexpr Quantity auto operator*(const quantity& lhs, + const quantity& rhs); + +template + requires std::magma +[[nodiscard]] constexpr Quantity auto operator/(const Value& v, + const quantity& q); + +template + requires detail::basic_arithmetic && equivalent_dim +[[nodiscard]] constexpr Scalar auto operator/(const quantity& lhs, + const quantity& rhs); + +template + requires detail::basic_arithmetic && (!equivalent_dim) +[[nodiscard]] constexpr Quantity AUTO operator/(const quantity& lhs, + const quantity& rhs); ``` -Additional functions provide the support for operations that result in a different dimension type -than those of their arguments. +Additional functions provide the support for operations that result in a different dimension +type than those of their arguments. `equivalent_dim` constraint requires two dimensions to be +either the same or have convertible units of base dimension (with the same reference unit). Beside adding new elements a few other changes where applied compared to the `std::chrono::duration` class: 1. The `duration` is using `std::common_type_t` to find a common representation - for a calculation result. Such a design was reported as problematic by numerics study group members - as sometimes we want to provide a different type in case of multiplication and different in case of - division. `std::common_type` lacks that additional information. That is why `units::quantity` uses - the resulting type of a concrete operator operation and provides it directly to `units::common_quantity_t` - type trait. + for a calculation result. Such a design was reported as problematic by SG6 (numerics study group) members + as sometimes we want to provide a different type in case of multiplication and different in case of + division. `std::common_type` lacks that additional information. That is why `units::quantity` uses + the resulting type of a concrete operator operation. 2. `operator %` is constrained with `treat_as_floating_point` type trait to limit the types to integral representations only. Also `operator %(Rep)` takes `Rep` as a template argument to limit implicit conversions. +To simplify writing efficient generic code quantities of each dimension have associated: +1. Concept (i.e. `units::Length`) that matches a length dimension of any physical systems. +2. Per-system quantity alias (i.e. `units::si::length` for + `units::quantity`). -#### `quantity_cast` +Also, to help instantiate quantities with compile-time known values every unit in the library +has an associated UDL. For example: + +```cpp +namespace si::inline literals { + +// m +constexpr auto operator"" m(unsigned long long l) { return length(l); } +constexpr auto operator"" m(long double l) { return length(l); } + +// km +constexpr auto operator"" km(unsigned long long l) { return length(l); } +constexpr auto operator"" km(long double l) { return length(l); } + +} +``` + + +### `quantity_cast` To explicitly force truncating conversions `quantity_cast` function is provided which is a direct counterpart of `std::chrono::duration_cast`. As a template argument user can provide here either -a `quantity` type or only its template parameters (`Unit`, `Rep`): +a `quantity` type or only its template parameters (`Dimension`, `Unit`, or `Rep`): ```cpp -template - requires same_dim -[[nodiscard]] constexpr To quantity_cast(const quantity& q); +template + requires QuantityOf && + detail::basic_arithmetic> +[[nodiscard]] constexpr auto quantity_cast(const quantity& q); -template -[[nodiscard]] constexpr quantity quantity_cast(const quantity& q); +template + requires equivalent_dim +[[nodiscard]] constexpr auto quantity_cast(const quantity& q); -template -[[nodiscard]] constexpr quantity quantity_cast(const quantity& q); +template + requires UnitOf +[[nodiscard]] constexpr auto quantity_cast(const quantity& q); -template -[[nodiscard]] constexpr quantity quantity_cast(const quantity& q); +template + requires detail::basic_arithmetic> +[[nodiscard]] constexpr auto quantity_cast(const quantity& q); ``` -#### `operator<<` +## Text output -The library tries its best to print a correct unit of the quantity. This is why it performs a series -of checks: -1. If the user predefined a unit with a `named_XXX_derived_unit` class templates, the symbol provided +### Unit Symbol + +The library tries its best to print a correct unit of the quantity. This is why it performs +a series of checks: +1. If the user predefined a unit with a `named_XXX_unit` class templates, the symbol provided by the user will be used (i.e. `60 W`). -2. If a quantity has an unknown unit for a dimension predefined by the user with `derived_dimension`, - the symbol of a coherent unit of this dimension will be used. Additionally: - - if `Prefix` template parameter of a `coherent_derived_unit` is different than `no_prefix` then - the prefix symbol (i.e. `8 cJ`) defined by the specialization of `units::prefix_symbol` will be - aded wherever possible (`Ratio` matches the prefix ratio), +2. If a unit was created with a `deduced_unit` class template, the symbol of deduced unit is + printed (i.e. `70 km/h`). +3. Otherwise, the library tries to print a prefix and symbol of an unknown unit for this derived + dimension: + - prefix: + - if ratio of the scaled unit is `1`, than no prefix is being printed, + - otherwise, if `PrefixType` template parameter of a reference unit is different than + `no_prefix`, and if the ratio of scaled unit matches the ratio of a prefix of a specified + type, than the symbol of this prefix will be used, - otherwise, non-standard ratio (i.e. `2 [60]Hz`) will be printed. -3. If a quantity has an unknown dimension, the symbols of base dimensions will be used to construct - a unit symbol (i.e. `2 m/kg^2`). In this case no prefix symbols are added. + - symbol: + - if a reference unit has a user-predefined or deduced symbol, than this symbol it is being + printed, + - otherwise, the symbol is constructed from names and exponents of base dimensions + (i.e. `2 m/kg^2`). -#### Text Formatting + +### `operator<<` + +`quantity::operator<<()` provides only a basic support to print a quantity. It prints its count +and a symbol separated with one space character. + + +### Text Formatting `mp-units` supports new C++20 formatting facility (currently provided as a dependency on -[`fmt`](https://github.com/fmtlib/fmt) library). `parse` member functions of facility -formatters interpret the format specification as a `units-format-spec` according to the +[`fmt`](https://github.com/fmtlib/fmt) library). `parse()` member functions of +`fmt::formatter, CharT>` class template partial +specialization interprets the format specification as a `units-format-spec` according to the following syntax: ```text @@ -545,7 +536,7 @@ type: one of The productions `fill-and-align`, `sign`, `width`, and `precision` are described in [Format string](https://wg21.link/format.string.std) chapter of the C++ standard. Giving a `precision` specification in the `units-format-spec` is valid only for `units::quantity` types -where the representation type `Rep` is a floating point type. For all other `Rep` types, an +where the representation type `Rep` is a floating-point type. For all other `Rep` types, an exception of type `format_error` is thrown if the `units-format-spec` contains a precision specification. An `format_error` is also thrown if `sign` is provided with a `conversion-spec` to print quantity unit but not its value. @@ -570,141 +561,95 @@ std::string s = fmt::format("{:=>12}", 120_kmph); // value of s is "====120 km/h ``` -## Strong types instead of aliases, and type downcasting facility +## Improving user's experience -Most of the important design decisions in the library are dictated by the requirement of providing -the best user experience as possible. +Most of the important design decisions in the library are dictated by the requirement of +providing the best user experience as possible. -For example with template aliases usage the following code: +Most of C++ libraries in the world use template aliases to provide a friendly name for a +developer. Unfortunately, such aliases are quickly lost in a compilation process and as a +result the potential error log contains a huge source type rather than a short alias for it. +The same can be observed during debugging of a code using template aliases. + +Let's assume that we want to provide a user friendly name for a capacitance derived dimension. +Other libraries will do it in the following way: ```cpp -const Velocity auto t = 20s; +using dim_capacitance = detail::derived_dimension_base, + exp, + exp, + exp>; ``` -could generate a following compile time error: +The above solution does provide a good developer's experience but a really poor one for the end +user. If we will get a compilation error message containing `dim_capacitance` in most cases +the compiler will print the following type instead of the alias: ```text -\example\example.cpp:39:22: error: deduced initializer does not satisfy placeholder constraints - const Velocity auto t = 20s; - ^~~~ -In file included from \example\example.cpp:23: -/src/include/units/si/velocity.h:41:16: note: within 'template concept const bool units::Velocity [with T = units::quantity >, std::ratio<1> >, long long int>]' - concept Velocity = Quantity && std::same_as; - ^~~~~~~~ -In file included from /src/include/units/bits/tools.h:25, - from /src/include/units/dimension.h:25, - from /src/include/units/si/base_dimensions.h:25, - from /src/include/units/si/velocity.h:25, - from \example\example.cpp:23: -/src/include/units/bits/stdconcepts.h:33:18: note: within 'template concept const bool std::same_as [with T = units::dimension >; U = units::dimension,units::exp >]' - concept same_as = std::is_same_v; - ^~~~ -/src/include/units/bits/stdconcepts.h:33:18: note: 'std::is_same_v' evaluated to false +units::detail::derived_dimension_base, +units::exp, units::exp, +units::exp > ``` -Time and velocity are not that complicated dimensions and there are much more complicated dimensions -out there, but even for those dimensions +You can notice that even this long syntax was carefully selected to provide quite good user +experience (some other units libraries produce a type that cannot easily fit on one slide) +but it is not questionable less readable than just `dim_capacitance`. -```text -[with T = units::quantity >, std::ratio<1> >, long long int>] -``` +NOTE: To better understand how the framework works and not clutter the text and graphs with +long types in the following examples we will switch from `dim_capacitance` to `dim_area`. +The latter one has much shorter definition but the end result for both will be exactly the same. +User-friendly, short name printed by the compiler and the debugger. -and - -```text -[with T = units::dimension >; U = units::dimension,units::exp >] -``` - -starts to be really hard to analyze or debug. - -That is why it was decided to provide automated downcasting capability when possible. Thanks to this feature the -same code will result with such an error: - -```text -\example\example.cpp:40:22: error: deduced initializer does not satisfy placeholder constraints - const Velocity t = 20s; - ^~~~ -In file included from \example\example.cpp:23: -/src/include/units/si/velocity.h:48:16: note: within 'template concept const bool units::Velocity [with T = units::quantity]' - concept Velocity = Quantity && std::same_as; - ^~~~~~~~ -In file included from /src/include/units/bits/tools.h:25, - from /src/include/units/dimension.h:25, - from /src/include/units/si/base_dimensions.h:25, - from /src/include/units/si/velocity.h:25, - from \example\example.cpp:23: -/src/include/units/bits/stdconcepts.h:33:18: note: within 'template concept const bool std::same_as [with T = units::time; U = units::velocity]' - concept same_as = std::is_same_v; - ^~~~ -/src/include/units/bits/stdconcepts.h:33:18: note: 'std::is_same_v' evaluated to false -``` - -Now - -```text -[with T = units::quantity] -``` - -and - -```text -[with T = units::time; U = units::velocity] -``` - -are not arguably much easier to understand thus provide better user experience. - -When dealing with simple types, aliases can be easily replaced with inheritance: +To fix it we have to provide a strong type. As we do not have opaque/strong typedefs +in the language we have to use inheritance: ![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. +This gives us a nice looking strong type but does not solve the problem of how to switch from +a long instantiation of a `derived_dimension_base` class template that was generated by the +framework as a result of dimensional calculation to a child class assigned by the user for this +instantiation. -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. +### Downcasting facility -Here is the overview of resulting class hierarchy for our example: +To support this `mp-units` library introduces a new downcasting facility implemented fully as +a library feature. It creates 1-to-1 link between a long class template instantiation and a +strong type provided by the user. This means that only one child class can be created for a +specific base class template instantiation. + +Downcasting facility is provided by injecting two classes into our hierarchy: ![UML](downcast_2.png) -In the above example `metre` is a downcasting target (child class) and a specific `unit` 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. +In the above example `dim_area` is a downcasting target (child class) and a specific +`detail::derived_dimension` class template instantiation is a downcasting source (base class). ```cpp template struct downcast_base { - using base_type = BaseType; + using downcast_base_type = BaseType; friend auto downcast_guide(downcast_base); // declaration only (no implementation) }; ``` `units::downcast_base` is a class that implements CRTP idiom, marks the base of downcasting -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_child` and will return a target -type of the downcasting operation there. +facility with a `downcast_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_child` and will return a +target type of the downcasting operation there. ```cpp template concept Downcastable = requires { - typename T::base_type; + typename T::downcast_base_type; } && - std::derived_from>; + std::derived_from>; ``` -`units::Downcastable` is a concepts that verifies if a type implements and can be used in a downcasting -facility. +`units::Downcastable` is a concepts that verifies if a type implements and can be used in a +downcasting facility. ```cpp template @@ -715,27 +660,12 @@ struct downcast_child : T { `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_child> {}; -``` - -```cpp -template -struct derived_unit : downcast_child>> {}; -``` +type of a downcasting operation. With such CRTP types the only thing the user has to do to register a new type to the downcasting facility is to publicly derive from one of those CRTP types and provide its new child type as the first template parameter of the CRTP type. -```cpp -struct metre : named_derived_unit {}; -``` - Above types are used to define base and target of a downcasting operation. To perform the actual downcasting operation a dedicated template alias is provided: @@ -745,19 +675,9 @@ using downcast = decltype(detail::downcast_target_impl()); ``` `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>; -``` - -`detail::downcast_target_impl` checks if a downcasting target is registered for the specific base class. -If yes, it returns the registered type, otherwise it works like a regular identity type returning -a provided base class. +for a given instantiation in a base type. `detail::downcast_target_impl` checks if a downcasting +target is registered for the specific base class. If yes, it returns the registered type, +otherwise it works like a regular identity type returning a provided base class. ```cpp namespace detail { @@ -779,76 +699,159 @@ namespace detail { } ``` -Additionally there is on more simple helper alias provided that is used in the internal +Additionally there is one more simple helper alias provided that is used in the internal library implementation: ```cpp template -using downcast_base_t = T::base_type; +using downcast_base_t = T::downcast_base_type; ``` -## Adding custom dimensions and units +### `unknown_dimension` -In order to extend the library with custom dimensions the user has to: -1. Create a new base dimension if the predefined ones are not enough to form a new derived dimension: +Sometimes dimensional calculation results with a class template instantiation that was not +predefined by the user in the downcasting facility. A typical example of such a case are +temporary results of calculations: + +```cpp +units::Length auto d1 = 123m; +units::Time auto t1 = 10s; +units::Velocity auto v1 = avg_speed(d1, t1); + +auto temp1 = v1 * 50m; // intermediate unknown dimension + +units::Velocity auto v2 = temp1 / 100m; // back to known dimensions again +units::Length auto d2 = v2 * 60s; +``` + +To provide support to form an unknown derived dimension that could be than be converted to a +known one with a correct unit, and also to improve the user experience and clearly state that +it is an unknown dimension the library framework will provide an instance of: + +```cpp +struct unknown_unit : unit {}; + +template +struct unknown_dimension : derived_dimension, + scaled_unit, unknown_unit>, + E, ERest...> { + using coherent_unit = scaled_unit, unknown_unit>; +}; +``` + +with this the error log or a debugger breakpoint involving a `temp1` type will include: + +```text +units::quantity, +units::exp >, units::unknown_unit, long int> +``` + + +## Extensibility + +The library was designed with a simple extensibility in mind. It is easy to add new units, +dimensions, and prefixes. The systems of units are not closed (classes) but open (namespaces) +and can be easily extended, or its content can be partially/fully imported to other systems. + + +### Adding a new system with custom dimensions and units + +A great example of a adding a whole new system can be a `data` system in the library which +adds support for digital information quantities. In summary it adds: +1. New prefix type and its prefixes: ```cpp - struct base_dim_digital_information : units::base_dimension<"digital information", "b"> {}; - ``` + namespace units::data { -2. Create a new dimension type with the recipe of how to construct it from base dimensions and - register it for a downcasting facility: + struct prefix : prefix_type {}; - ```cpp - struct digital_information : units::derived_dimension> {}; - ``` + struct kibi : units::prefix> {}; + struct mebi : units::prefix> {}; -3. Define a concept that will match a new dimension: - - ```cpp - template - concept DigitalInformation = units::QuantityOf; - ``` - -4. If non-SI prefixes should be applied to the unit symbol, define a new prefix tag and define - new prefixes using this tag and provide their ratio and symbol: - - ```cpp - struct data_prefix; - - struct kibi : units::prefix, "Ki"> {}; - struct mebi : units::prefix, "Mi"> {}; - ``` - -5. Define units and register them to a downcasting facility: - - ```cpp - struct bit : units::named_coherent_derived_unit {}; - struct kilobit : units::prefixed_derived_unit {}; - - struct byte : units::named_derived_unit> {}; - struct kilobyte : units::prefixed_derived_unit {}; - ``` - -6. Provide user-defined literals for the most important units: - - ```cpp - inline namespace literals { - constexpr auto operator""_b(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_b(long double l) { return units::quantity(l); } - - constexpr auto operator""_B(unsigned long long l) { return units::quantity(l); } - constexpr auto operator""_B(long double l) { return units::quantity(l); } } ``` -## Adding custom representations +2. New units for `information`: + + ```cpp + namespace units::data { + + struct bit : named_unit {}; + struct kibibit : prefixed_unit {}; + + struct byte : named_scaled_unit, bit> {}; + struct kibibyte : prefixed_unit {}; + + } + ``` + +3. New base dimension, its concept, and quantity alias: + + ```cpp + namespace units::data { + + struct dim_information : base_dimension<"information", bit> {}; + + template + concept Information = QuantityOf; + + template + using information = quantity; + + } + ``` + +4. UDLs for new units + + ```cpp + namespace units::data::inline literals { + + // bits + constexpr auto operator""b(unsigned long long l) { return information(l); } + constexpr auto operator""Kib(unsigned long long l) { return information(l); } + + // bytes + constexpr auto operator""B(unsigned long long l) { return information(l); } + constexpr auto operator""KiB(unsigned long long l) { return information(l); } + + } + ``` + +5. A new `bitrate` derived dimension, its units, concept, quantity helper, and UDLs + + ```cpp + namespace units::data { + + struct bit_per_second : unit {}; + struct dim_bitrate : derived_dimension, exp> {}; + + struct kibibit_per_second : deduced_unit {}; + + template + concept Bitrate = QuantityOf; + + template + using bitrate = quantity; + + inline namespace literals { + + // bits + constexpr auto operator""_bps(unsigned long long l) { return bitrate(l); } + constexpr auto operator""_Kibps(unsigned long long l) { return bitrate(l); } + + } + + } + ``` + + +### Using custom representations In theory `quantity` can take any arithmetic-like type as a `Rep` template parameter. In practice some interface is forced by numeric concepts. -To provide basic library functionality the type should satisfy the following concept: +To provide basic library functionality the type should satisfy the `Scalar` concept: ```cpp template @@ -898,7 +901,6 @@ the equivalent operation for `Rep` type. Here is an additional list of opt-in op - `operator*=(Rep)` - `operator/=(Rep)` - `operator%=(Rep)` -- `operator%=(Rep)` - `operator%(Rep, Rep)` `quantity` also has 4 static functions `zero()`, `one()`, `min()`, and `max()` which can diff --git a/doc/design.png b/doc/design.png new file mode 100644 index 0000000000000000000000000000000000000000..256bbd4af7aeada85fed4fdbfcf5b00d6a8c9698 GIT binary patch literal 27339 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 literal 0 HcmV?d00001 diff --git a/doc/downcast_1.png b/doc/downcast_1.png index 227681970d93e0b4f3a6aa9ad243820316fc9329..d8eaa468d26224b69aedebcc616a6bb83768058c 100644 GIT binary patch literal 10899 zcmeAS@N?(olHy`uVBq!ia0y~yU}9ikV3@lVUX6 z&3Es=+E(SAkNJMC_TAj)=k}Jrd;W9z8JqK#_doxe_k7OzpYz^V9%G7AabSWVNB09l zCAw+No((J>VC2Afzz;6z(ZHm@G54I>WCqR&AjHb>8$^LPEQ*X=4GJYzvwff@i7>oj zhM1`$(88j@&~)KtjuA-Z1P0Ckh7J5+12`u*2na1;;BZ>L`6N`a1LFZZvMgLF#xV8J zp;dRdcFvuu0eAnUX$~G9C)VD$(UNbw@@g_;z?O`QI^Un~E0<5ueCK(zrVZ*(j{`hR z*St3@@bd9_#8~e2IfB8(rNwj`!^L+Y$Ex9`ol#FK zU+Ow(SjhSJuI4EdMuVgb`H=NtN9XRU)Q1Muga&pNzxj6E-{w}oWNF}6W)NYJtC-La z4ZQ|-77YfQhGXuK$nsFQz!|{6vWq>_9;&^E|TJ(sxFj8{#Q zCrvUa`?I^_{$_FJdoLPqZC$l=mruOtgWhdFCjYNJdUf{Kdpin+jnmH_d}S*AA?f$D z7dQ5`ny*g28K?H)`26LDN<~I~b1Ygr1DylcPNlTk&bu2qSKYJPx;okaP~8`K8@8XH zo;rp+QLqVFG9fzEYFFgG>ozr4RQ{BFF!=TDW!tZ>Sxa~MXeRtVcei7ffo!7asicJc zawkgTS7<(67SdsUE9w3J=7#h>6yN3+vhDBcF)-u1>@vvjrdpZztzC^HcI20q;XowVwU6E|NXlC zUcxAeDdj_b^`8P8sjzG6$&C@udp{aoZFs|FP<>`o)LM&guir=P{FPnvzn16R-2EMQ zb~;^ru=7^NwIy~=&hJfkbNZcL%;3TL>hb>hhc--nT=?lq=IsM7diiq8*ssj`^K;J8 z8QTk5S0)`5^~>LKG>&18W%WP3{W~_DzF)Wh;p|+mBPW*fUfQ>5BGUo$C;G9wdITp< z-jM%aF<9KZkEKEXSW|*1!|!kMM-C`HF8KSacehp8UMtfJudnK5ZtLL9V*GI4{<`7Y zz5WW#xxcray|^zD3wKQ-mZF5c*kuiGO;3#%6-)Gg=z zS@KOOX7@M#Tify$ZJT#@XHyhIMREf75{?FI*`sT!FFP?Txhggx(&x`!nfyKK_cA_T z&Az@N>FV`}O*y7|@%x(6dgr@-pKd)v$jpE3UUmP!oSh+W?}#?fHqTFdvx0HOrwW&> z?yoN@-31x?xr`nv=J!mWH*0Iz-!z}fbTK*2SfOM$UWbefzx5mp)xY!Z?djyVpVd8e z^>x+mAGx>o$WC5%uan8EE_!xYZ=gc`zTLCT)9)G880KplrS4+SjW%_i>{gn)ZFiDj z0n>x+kJS|(u8Orkrs)1HC*$54JHPq=yj3+`7Bjfy9)EkYUDZ2WIq|*!j@sX5a(a<6 zvT+r1Q?*+k{&=sR`#Yh^BXs_Xmd3x;kFKn=->6k3`$+hk^Q1JRv@;E#QoSFyZaXXZ zFT*lIN6f;DukgykuM?Xt9{#P^K)zY@x zY&WxASp9#%N&DyeF-LZMyd3y#VvvO87JZ@b|5^f*m#^5qGQ?KQH6Gu&9#xgl+tp__n>({0ky39;*0R|2Dw#QQ*_; zojo7^`|*kJRD<8$svZ6}+UIO*tNSOucyOV2aRsmWlcYq8@M~o^mgsKn(dE&OZ0Tk>e7ic zN{)GwFh%$ChDR2uzmDJd_V#{$=`?owv8= zC7<4YU;X#b+Jy@{q>MR!e!7=``e4=HtLt9PWAzSu^yg=HS9<@uxdBT)Ft^=bK82;h zj!B{T#>2zM4XWP>nchFSL$N3I&#B`2yl6wCbEmBB(%%J5Rag7$uv)D5;+DYJl=Jg! zU3W8iG0d^~%I?-Pk0)?LpPny6%DFj`ug&s~ZYj49)1Uiq665FI>$@LLG~&Pgt@ACb z!}~b>hll(31td!S_I{AX+_L!Un#Fu;!|(j!jJdu0n)bQ1xAz_YvHkw;(|Kq4-d4_> za=-e&@z&bJ`jZJ(RW9}-ntvAPo^-RCW;XM5eQNyAC-U`b!FxQz*Co8@=zOIrZ}h_S z%BsreTN=~;?<{_9SocOiL@PufQ23s!(1VhGrC!`X{P^5NRGN}JNpw@I7l-PwA6`Oou_(OW`S zzxjPjee%E3cYA{0U0T1pB&Rt{SL)%VrTasF*V`3|+}xDf`bPEjdakFZH4+Qz^O7Gr z?PVx&tYGm(zFcNw9eJ zC}WSwBYnx+J^QjR{l7o!$2zZTWj6VDB%hn*-h8*g{uOIOk9^Ynf0z0z-Hsdmtu^B8 zmob)9s#v<_Vg9#`x%MY&jSikxD*JY4X;$H*oTd7I-yFaDF>9iFH}}WDcJZhMesf=) zyvP1~*X{CmU+?#?xu$7Y*!lNP=JB~tC$fjbT9#Yf-FeuTJc)1Ki5Mny(-(?kC)h^tsrQwWqi`U$!H}}?p*x6}(-!}&fGw#sUzP^2# zqMDwde`u}D-m0=jjgP6Pudhh4ojGR<=ab`it3#S5@c1?V`W{m4xk)HUd&+wA+c{VG zPb7-U9b8biUg`UO=KpQJ_tk_MA~&ZU-EC}l^t$x62RnE9FAhJyZq?>Y{aM#6x98k# zEKjd%n`Jt$MI-5^w)OApmZGXZ63cDc_TFTldg!!tUiZay$yfesax$#+USwk=k$!p} z@B2N!4Y%dpUg?u@bJ0YrM`e$W$iHfxV)xZ;{qze9{uvvbicVI2YML9uFKfN!&{yf} zxjC=wf|>Wm|CZ;Iv00;5At;yZ&L0ve{POf8{mut({=dv)Tp(L6(s+G+x%m{`Xs+<} zaYx_mJbi^LUTx{d#q29RCaE<%iGBJ1nD&Z*btcK%x8?0@qJUx!#gv#~x z{^cRHi;VjIR^8cF9{GP~)QhW&S4W8+Kcc!Is6yh?o%!Bti?+Z2Be?ju-`1PcU+jXt-wl|)4`acS%D^!15vuf_9 z=~}DfD*IKNqtxo%s~5P{ZvTJayo6QKo~|!nUVb%NGT-}4TEYM80h^BrpHxyj5pDYg$j!A7k_-rKX~$jFNYax;~hI6xf+)L zGt!9>>J8!-5C41fMW>Sc*I>V+PQhWC-)j|`Z1y3$?q@Ko9EwdJfE%~ zB)D7Tas^_m=C9$A|&-Lo=Wj`mAll~47WzQy*@io^MT%fyfE``TBv z^SJB1nKMk>j0`JDLFjReiJ|Hx>$Pubr3<_-HXo62{` zk8DqOaJrfsZSH#AFE=Mz*k0al)u~CLvv^(@6=pCw2pb&wapq?6tBi*exM z?^u!-dwb+(O6;cT{T7OPbY<`NwD82!uBXG44GZ^6HEi*&elJ(EfWhux4&$`7 zKfGGCrR~1+%v%{N%P(3Pu(t2(^6WWZc{88CG)|w&P_XUO^?9bRtZ!D$_F(YiT?N(^7@%BLNE{4#Fe z;^#fTPQTc_>@54H2q~it;-8+J?9Df;U;e94C+XMMm&?LVxUn=yzMuT8zOw$y#8-T8 z=Y>z+x_R-uZ^FBc85M3juHJX2p^|&r>KDPggc-i2PkhVTVD{2d+Qx;`t?xzqZ>Rs! zoi9#bdH>f^OIeqt;opnti>Lc#Z{BQXmKPyzyQ{Qh$D}Ls3a>o;I=Ll>`GmLfZ6=|A z{ZhseYa>h@AFRo{DZl1Z#gEwZ-lui`lNo}gc;5bh&o4heZjX+yXCFQ;d^^AX%Hv;*_htFE=iLwE z?A+w8*Suj{zMi0KF870TzaPwTzNRxX`0HQR^vRnRs~niJjp0jjauf%{;(tcJ*2V6x zyZlnjVZB`Jg(ZcyPqUSo4)lEYd_DbFuAb+o$>s647hU;Y{d?+uj(g8%n+8P3{_2>% z*I~lPC09T9Uf7U0_rQIof@S+yrx-Inc$I8(F7CyAKAA8@<(rjpOvWj%EoKKGAr*?Vu2YM0ZbS4Z^S zlo)PRPf|WU_lfpzl>=G+VPVI2sci?TuAG|PW z_Rk0G;RkrY0lN2YO7k7xAVVTduWIC{{DksB>9skbTzY?BnFZ)CC{FOSc^DE}sL{Y0z3aa(NE zpGQ8|k6IeHrit~FR9?3r&T*qE>U@{?#O z-;ZBkU!P=Yu*+HZcCQ!TnfvwX5C2AP{&;rT`zza;RqF3eHe7t2rQzEBd250=?dAWm zPF_~C;2!799jaGWE{( zzp~Kun12=j$Len@YoEW<-yYtR(yAOddF}18h2OJdL+@XS&$^v=)=ek!-%=_5EiGH} zx4$>~vM$4Owb8ng`jxi#W?M#ot<`-Qu%F{Xkzz~pp7r^@vlbS)%~3t*+2Z81DQeNo zImYV)_HD`9Zp>%o=J{ylUIv3%%nRnfIBNd(mf7*AOH^w9ykxp9HgBSC!oxlP**bsSvZ5H7T7HMyMulRM9+@?~5{wGH6C8_vyztDr zA5wqv<_YiVdJj9b1hp1AhwPU*KhrpW<>76wi#fI&F?#EE^K{MPaC4v89}R`R%N6|C zvzzI_3(bZ(mhVH;9q<27DBD!El)2-v>P&ZTN6$-BRzyjv$j08zZj9{BN`?sK^Q~yL7 z>K<%JR};Q;pl-dBno4?Nl(7;+pV6Vz15+Mn7!|$}-ux!FWJ>ko{qDbZPAk}6_t?y5 zuHETGx&E>c^XhGD_;&aGZEr}qRQz?_6pg?T*N7)yT3cGB%~xf=-Y--uXCH_xe z`+nfjD!#tD{QrszwEAsd6T6 zulHAn#7+J;>Ex!6l|mVpHuQPfOt*CI{*x8??-6^sa+ce^jS2S8Z+sWc&5xgS?Wwav zto8Q2+DS|5-F^#9`1{d8`Eiy5Cqw20AFoBTQXfseaE*KItc5WQ zRXfOP{~EC{*Y)A<8Y(_YiSm&zPZz4ZoiX9fK~J$|GS?@r&U~DHwQyB%{hv;M`}h@` z{-xS)jGH@4BS1kut=2|7H_9w<=B-=h3}wgP1m3?SemwK}_NRBg-?-R()%;Gc8-ogq zN?G@#|9`q&Uu8^wBl~ZCvh%PKO%>c#1sSY}tK)(f-8U5WijQsS6BSczqe=MKIR9f^n>Ot+w`=hrH0$l&!nTFBLCU*S8ulc>%TAY z@NLh^u>NmF+{r)N4o}ZE(^5?RFUy)<_xtmC;~D!amcEsK)VIU>)$~hYZ!d2O+Gf3+ zVT(PR%JchAIo5Ot-`sRzpSHsCB$ZzZ)63q)ob&YlKlMG!mW*2th11Hts@0#IxcsM8 z`^xXs|Gj*im$~GBro8HUQM&CtL&t^n!MmlT%vU}CCVoEVXP)bq>pv2%iNBJVR@M`0 zFL-j%!rZS+}L;S z#nsnWHMP>GZ+>F8xc1cC-%NfCJ1$#5M)lJKJ#xeLMV4)ldCcPCku~wewzab~9QCfe@lOz1DFdevWJLTNl%m1(Lo-%9I{a+r> zJua=OTz4+ITk)3Nii`bhOJ{zxThYOEK%(StyrIua&foUClGl`ce|M=q`{2dN>pq#? zS?OEE(bhX_!KC=7{)gEQxiSiH#CaVz3u5?jZl?&tyN%b26jS13=1V_{(|q|quU?43 zDf;({*(%e#)=i0-`p^GXM@z$`3@5g@n6!0%?2^WsTHWc9UU89i6zXop87FPNP!W8K66GSzLl1q zZE4O(LJUEH6%{+(*M@oP zm1{lStKz`4PgNjk;~wVjr-6G#!&ZOYbTupYP4wvqYt9J`)huhwA3gq_TmF8gb@}or zhKeA~`HGA}A7mT5L{E#H6`1sLg);Y=?Q3I`9t+!lpIf?SCX0u{T8;pQC8C+voSEvJ z6qJz@G(({Lx)N5q3E^1V&Hrc1O=e`id3DQN79PO`3YMbR?Mtm`Wb?J$8>dnZ94Ix^RcGAJ8Njfkw}*d% z!vn#BDSyH@xm--)-C1MVd9tDLc~q1SlbrAZh7S_5@+yr76ea|HnqSL3gGWNbZs}B& z1U5!424js)JVGCmnZBK@KX1Oef0choS;ohS@pVVfL>B-1EjyX5rR9az{A&VmI&sOf zfu+W=f$4zwl}L5YAB>G&4@IvXPFlHoS^es-H90X!dG-d~Y7-bZmk2U)HJC7+&GI;K zALNeN?iu`?OLQ5z8n!$=d&}cMK1i@vJA;GsM+*aMga3xjK|&vF8Mzql|85YQ(4Y@; z+a$Z!YK-zi3m85q)`a;o)qy4<`R6zYdmIpF(O{@?oLf4n;lIEFh7a=b7led7f|*1Z zGP`C^o!D?+Z~?;y>**I1ggm4{f`?~rozSo!WKj6|3wlByj6jK>Iqjp$gXf^YyyIxn z&$7q0f$2bWN~9hq3mYR>!+{UNYgHI8a|STX=$pbi$-zNz0Rx9d`Naq!gMJ3qhDx1C zAx;)0My>{jFH*T`jKZM2!7$Nvf&+u_0tSwR=NC(aJa`$o8m?S;yjexy2a5&+)BW}w zM$QVC2BrhbIW-+Dmq5}bkG;%VSneo0Fg}pezvs*JvX_C?q3W~7ZD%Gn&Hx4pmZP^l z6y^#oVDMPpm%7>GfG8suLwL)NY9WvNEE)`#w%6aZ5-RX&U}~7#%+fTQfz_el_x!c| zNlYRPj&Ewr=CUvZFl>;X;4lF+JFe2^cawpE%MY{`An5q!n<^jl8MzqBScN!(KYyd>?GPDkUjC-NNW9>qZ744k3_V9%YzajscZ8_qImpte+XJ7*tSy`tZYzCR9!O(Q_cFyWd7OmFyz5Lu&Gr|(2N-*}Y(`qIx+nM4@k*v>q<{lWP7n7{i?cdx`J7xtZ)Wy+m)ZdUWw*Jg<^PZH)>eij$gi)+2_ zKga3kr_>Kc*UKt4Brxj4{}f$xeNFq7%Ny!sx8&cJS-9}KR9WBEtfTWPKg_tk&C+X{#;Q}7-elihWY{IH-NbJ{vwKbCri3dW{%ORlP~eld zY-8;XS2WXOoUR}5xG~A~!lFp#E2|1X_3vUZ%*r9af{^W*YCJ}}=vMLW48U0xr ze5$&qxjdeHUfgPJpf^*@?mFXTdyKjJr|5MWE@o3Ge&#d7>aTLntxe3|TiYHTI@*2m z)QP#;A~TIr!xlN!Z_T*JpkG?aC~2H#a^)0gb;*wvoOKbW0-qUP2ZR|5 zUiDv=Fe_rJv#t|5JNugZbicKQUtgJS{q_IT>fTd!%1jFGeK{3dtJQ5{7i&Fw^gZd$ zkIWrKU!}NgBLtX#?Q0S`+MV?7Pin*t6{)3idVL!gvrE~zDhU7F_I}@S=T`2885a-j zcs{R3V7I&5zTU;h`;u?Wt_|2+B|4iyVD|i#e?{V6Uu6BBa;8JtJU{8v75>nc2VdvK z2L8Vpbz*`d^EZ`u$A5GhPq3{H>yfqP`2PG{>I%7Vzr_p%t{wZ;8Mzp~F$x(>ZBS=Q zfGqfMTl-+~F`h$*Rl7B3%g8eOXfj>cfq`WBy9H|sCa>ic$g1NO)1hAs=6uD+b( z+}!;QSL4?m-}b&tbK|1@TdYiecZuruY+TH?>gxWO&o*#fj^>}g-&1eb?X_ldh<<6!l)nXQcsT68H2qD@$yNL3 zOlj%2k8R++pA-B|@+9MjWeft=YJT%LIp6lnU6il<{Nz~Q`(%UCUpiB-JojRV*;OK$ ze6NZfG*Bbc`c8kl`@G-9k8f?Q-(X$Fzb)@}>%H}*N6*C?w!JERY67kI7BD2pD=-Fg zRwyxmRy{<{KXbe*G<)(*rNoapH#ezTv&{(NYcOYRh+AU8^vsLlgz;~8R&S8he@>RG>j?)%Gb zSa8BZfxH>YPB zx38@TJ@&6TLU8Z7Z?X+cKR++@dz+PUXN6qR^IiV6><2t-*X1ljOOnf(4umL2NN6zJ z;Pg09%=AZK!t*yjQUpLvQU2$_3_sfCGh7ouZ2+JB7qjGrIaC-IbGW!nnV;x*Vs+HtRQXB? zN#m3cQ!O2%-}|%oKRDJaU4AS7D8FGLXK0fco3tNOp3nk@0%3`KbwTiongNu8#_9VIjAWK=G(+68~Hy920$N#0lHh%q- RT|%HWr=G5UF6*2Ung9V^#18-f literal 6620 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 BrB?s| diff --git a/doc/downcast_2.png b/doc/downcast_2.png index 7dbfc6c23bebc0e7aa28c5b554c9c8f9d5de5f4e..8a37faabbbec6aede29fbabc22807ef89a5a5d0b 100644 GIT binary patch literal 32810 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 E0J)D~F#rGn literal 22448 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 My85}Sb4q9e06gAz2LJ#7 diff --git a/doc/nomnoml.md b/doc/nomnoml.md new file mode 100644 index 00000000..878d4ac9 --- /dev/null +++ b/doc/nomnoml.md @@ -0,0 +1,46 @@ +# nomnoml + +Graphs in the documentation are created with . + +## Concepts + +```text +[Dimension| +[base_dimension]<-[exp] +[derived_dimension]<-[exp] +[exp]<-[derived_dimension] +] + +[Quantity| +[quantity] +] + +[Unit]<-[Dimension] +[Dimension]<-[Quantity] +[Unit]<-[Quantity] +``` + +## Units + +```text +#direction: right + +[scaled_unit]<:-[unit] +[scaled_unit]<:-[named_unit] +[scaled_unit]<:-[named_scaled_unit] +[scaled_unit]<:-[prefixed_unit] +[scaled_unit]<:-[deduced_unit] +``` + +## Downcasting 1 + +```text +[detail::derived_dimension_base>]<:-[dim_area] +``` + +## Downcasting 2 + +```text +[downcast_base>>]<:-[detail::derived_dimension_base>] +[detail::derived_dimension_base>]<:-[downcast_child>>] +[downcast_child>>]<:-[dim_area]``` \ No newline at end of file diff --git a/doc/units.png b/doc/units.png new file mode 100644 index 0000000000000000000000000000000000000000..311abd6f1a6ed2ae972c8da5d5fc19df001dce9f GIT binary patch literal 33552 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

FFEGpk<$&Y=trouGQZ;>D zbyDJzL+TZSU41WKX$!8`_pOz_9$(JeC8F8%_ICM7DT~f!qMAV#lM`%eeyq5?)5W1z z>R$ZKC%z`4x^`tb(gnib+?6W-J>Pp`CSU4N{FT-n$=~1A&iMCjPLE#1%B8bE|4Xt7_-}VrHMhjcJ1Rh_R%l}Nrs(QBF}tQ* z|G6~Man}<2$e&AJoe-P&^Zwo6A+k%>@Bi{BxAxh&r^)1VqX`#XI<vSKG-q`@E=$ z{1M&ciq*!7Zdt$DeCNx;0W4 z=Po?qw{OSUS$jDiALnyiTK(?J`2{vr=B&wbr83ifnN$j%a=7eDIHOmq6=xVU+a~4L zecN4+f6Iqjm7Hj}_bUBw#ZNzlD>o$5lczza1-_key1(|cb$O7%GuimUr78|gicSqp zA0NH<+HHNlugUPW+SD&guB4uJ~c{H#vftFJ{C|L492T><5aJFbOavdx9s`C~)%)=<8(8vH9l@{z`)??>gTe~DWM4f9C^}4 literal 0 HcmV?d00001 diff --git a/doc/units_uml.png b/doc/units_uml.png deleted file mode 100644 index f0f1e262e482bf83ba43618d0fa433adf2ce8156..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30286 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%0D@_z&0&zXZ8mmnonjd&$>0;v7|Z4p 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 From 7617e3c374277a2b3515313813833acf2c0ace2a Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 17 Dec 2019 09:14:31 +0100 Subject: [PATCH 42/44] Installation documentation now used 0.5.0 --- doc/INSTALL.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index b573f519..52215520 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -26,18 +26,18 @@ steps may be done: ``` - add `units` as a dependency to your `conan` file. For example to use testing version of - `0.4.0` of `mp-units` add: + `0.5.0` of `mp-units` add: - `conanfile.txt` ```text [requires] - mp-units/0.4.0@mpusz/testing + mp-units/0.5.0@mpusz/testing ``` - `conanfile.py` ```python - requires = "mp-units/0.4.0@mpusz/testing" + requires = "mp-units/0.5.0@mpusz/testing" ``` - link your `cmake` target with units @@ -79,5 +79,5 @@ conan create . / -s cppstd=20 -b=outdated --all mp-units/0.4.0@/ +conan upload -r --all mp-units/0.5.0@/ ``` From a384fbd75acf57ad9cd30038bd1699b2f86b1ea1 Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 17 Dec 2019 09:15:31 +0100 Subject: [PATCH 43/44] README updated --- README.md | 149 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 114 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index da45f8f0..0ccf03fc 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,32 @@ # `mp-units` - A Units Library for C++ +## TL;DR + +```cpp +constexpr Velocity auto avg_speed(Length auto d, Time auto t) +{ + return d / t; +} + +int main() +{ + using namespace si::literals; + Velocity auto v1 = avg_speed(220km, 2h); + Velocity auto v2 = avg_speed(140mi, 2h); + + std::cout << v1 << '\n'; // 110 km/h + std::cout << quantity_cast(speed) << '\n'; // 30.5556 m/s + std::cout << v2 << '\n'; // 70 mi/h +} +``` + +Try it on [Compiler Explorer](https://godbolt.org/z/OVpkT1). + + ## Summary -`Units` is a compile-time enabled Modern C++ library that provides compile-time dimensional +`mp-units` is a compile-time enabled Modern C++ library that provides compile-time dimensional analysis and unit/quantity manipulation. The basic idea and design heavily bases on `std::chrono::duration` and extends it to work properly with many dimensions. @@ -31,74 +54,120 @@ static_assert(1000 / 1s == 1kHz); static_assert(10km / 5km == 2); ``` -## Usage Overview -The library framework consists of a few concepts: quantities, units, dimensions and their exponents. From the user's -point of view the most important is a `quantity`. +## Getting Started -Quantity is a concrete amount of a unit for a specified dimension with a specific representation: +The library framework consists of a few concepts: quantities, units, dimensions and their +exponents. From the user's point of view the most important is a `quantity`. + +A quantity is a concrete amount of a unit for a specified dimension with a specific representation: ```cpp -units::quantity d1(123); -auto d2 = 123km; // units::quantity +units::quantity d(123); ``` -There are C++ concepts provided for each such quantity type: +To simplify quantity creation the library provides helper aliases for quantities of different +dimensions. Thanks to then the above example can be rewritten as follows: ```cpp -template -concept Length = QuantityOf; +units::si::length d(123); ``` -With that we can easily write a function template like this: +To simplify creations of compile-time known constants the library provides UDLs for each unit. +Thanks to them the same code can be as simple as: ```cpp -constexpr units::Velocity auto avg_speed(units::Length auto d, units::Time auto t) +using namespace units::si::literals; +auto d = 123km; // units::length +``` + +For brevity, the next examples will assume: + +```cpp +using namespace units; +``` + +Let's assume that the user wants to write the following code: + +```cpp +int main() +{ + using namespace si::literals; + auto v1 = avg_speed(220km, 2h); + auto v2 = avg_speed(140mi, 2h); + // ... +} +``` + +`avg_speed` is a simple function calculating an average speed from a distance and duration. It can +be implemented as: + +```cpp +constexpr si::velocity avg_speed(si::length d, si::time t) { return d / t; } ``` -Concepts usage in the function above guarantee correctness enforced in compile-time and the -guarantee that no unneeded intermediate conversions will ever be applied no matter which -units the user will decide to pass to such function template: +However, this function will perform unnecessary intermediate conversions (from kilometers to meters, +from hours to seconds, and from meters per second to kilometers per hour). To eliminate all that +overhead we have to write a template function: ```cpp -using namespace units; - -constexpr quantity distance(220); -constexpr quantity time(2); -constexpr Velocity speed = avg_speed(distance, time); - -static_assert(std::same_as, quantity>); -static_assert(speed.count() == 110); +template +constexpr auto avg_speed(si::length d, si::time t) +{ + return d / t; +} ``` -The units library also tries really hard to printing any quantity in the most user friendly fashion: +This function will work for every SI unit and representation without any unnecessary overhead. +It is also simple enough to ensure that the returned type is actually a velocity. However, +it might not always be the case. For more complicated calculations we would like to ensure +that we are returning a correct type and also inform the user of that fact in the function +template interface. Also we might want to implement a truly generic function that will work +efficiently not only with SI units but also with other systems of units like CGS. The solution +to this are C++20 concepts and generic functions. ```cpp -std::cout << speed << '\n'; -std::cout << quantity_cast(speed) << '\n'; -std::cout << avg_speed(140.mi, 2h) << '\n'; +constexpr Velocity auto avg_speed(Length auto d, Time auto t) +{ + return d / t; +} ``` -Try it on [Compiler Explorer](https://godbolt.org/z/OVpkT1). +The units library also tries really hard to print any quantity in the most user friendly +fashion: + +```cpp +int main() +{ + using namespace si::literals; + Velocity auto v1 = avg_speed(220km, 2h); + Velocity auto v2 = avg_speed(140mi, 2h); + + std::cout << v1 << '\n'; // 110 km/h + std::cout << quantity_cast(speed) << '\n'; // 30.5556 m/s + std::cout << v2 << '\n'; // 70 mi/h +} +``` ## Library design -`mp-units` library design rationale and documentation can be found in [doc/DESIGN.md](doc/DESIGN.md) +A detailed `mp-units` library design rationale and documentation can be found in +[doc/DESIGN.md](doc/DESIGN.md) ## Repository structure -That repository contains the following independent `cmake`-based projects: -- `./src` - header-only project for `units` -- `.` - project used for development needs that wraps `./src` project together with - usage examples and unit tests -- `./test_package` - library installation and Conan package verification +This repository contains three independent `cmake`-based projects: +1. `./src` - header-only project containing whole `mp-units` library +2. `.` - project used as an entry point for library development (it wraps `./src` project + together with usage examples and tests) +3. `./test_package` - library installation and Conan package verification -Please note that the projects depend on `cmake` git submodule in the `./cmake/common` +NOTE: Please note that this repository depends on a git submodule in the `./cmake/common` subdirectory. @@ -112,6 +181,16 @@ NOTE: This library as of now compiles correctly only with gcc-9.1 and newer. ## Release notes +- 0.5.0 ??? + - Major refactoring and rewrite of the library + - Units are now independent from dimensions + - Dimensions are now depended on units (base or coherent units are provided in a class template) + - Quantity gets a Dimension template parameter again (as unit does not provide information about + its dimension anymore) + - Added official CGS system support + - Added official data information system support + - Repository file tree cleanup + - 0.4.0 Nov 17, 2019 - Support for derived dimensions in `exp` added - Added `pow()` and `sqrt()` operations on quantities From 27f6f806dbbc4e4d4cf77a90c730bd5d589842aa Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 17 Dec 2019 09:16:04 +0100 Subject: [PATCH 44/44] .clang-format updated --- .clang-format | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.clang-format b/.clang-format index 6b47c3fc..b37a7280 100644 --- a/.clang-format +++ b/.clang-format @@ -27,13 +27,13 @@ AccessModifierOffset: -2 # BinPackParameters: true BraceWrapping: # AfterCaseLabel: false -# AfterClass: false + AfterClass: false # AfterControlStatement: false # AfterEnum: false AfterFunction: true # AfterNamespace: false # AfterObjCDeclaration: false -# AfterStruct: false + AfterStruct: false # AfterUnion: false # AfterExternBlock: false # BeforeCatch: false @@ -66,6 +66,7 @@ ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH + - SECTION - GIVEN - WHEN - THEN