From a5c7934e0e0a3fb125b6349af536a8aef678dc5b Mon Sep 17 00:00:00 2001 From: Mateusz Pusz Date: Tue, 18 Oct 2022 21:24:09 +0200 Subject: [PATCH] refactor: units nearly done --- example/v2_framework.cpp | 164 +++------ src/core/include/units/unit.h | 426 ++++++++++-------------- src/systems/si/include/units/si/units.h | 13 + test/unit_test/static/CMakeLists.txt | 5 +- test/unit_test/static/unit_test.cpp | 391 ++++++++++++++++++---- 5 files changed, 565 insertions(+), 434 deletions(-) diff --git a/example/v2_framework.cpp b/example/v2_framework.cpp index 5328425e..34c0a3c8 100644 --- a/example/v2_framework.cpp +++ b/example/v2_framework.cpp @@ -22,16 +22,6 @@ #include - -template -consteval bool print(); - -template -constexpr bool is_of_type(Expr) -{ - return std::is_same_v; -} - namespace { using namespace units; @@ -42,6 +32,18 @@ inline constexpr struct activity_dim : decltype(1 / isq::time_dim) {} activity_d inline constexpr struct activity : system_reference {} activity; // clang-format on +// check for invalid prefixes + +template +concept can_not_be_prefixed = !requires { typename si::milli_; }; + +static_assert(can_not_be_prefixed); +static_assert(can_not_be_prefixed); +static_assert(can_not_be_prefixed); +static_assert(can_not_be_prefixed); +static_assert(can_not_be_prefixed); +static_assert(can_not_be_prefixed); +static_assert(can_not_be_prefixed); // Named quantity/dimension and unit static_assert( @@ -68,24 +70,28 @@ static_assert(is_same_v -concept invalid_comparison = requires { - requires !requires { 2 * R1 == 2 * R2; }; - requires !requires { 2 * R2 == 2 * R1; }; - }; +concept invalid_comparison = !requires { 2 * R1 == 2 * R2; } && !requires { 2 * R2 == 2 * R1; }; static_assert(invalid_comparison); -// static_assert(print()); - // Arithmetics // Named and derived dimensions (same units) @@ -124,14 +130,28 @@ static_assert(is_same_v consteval bool invalid_arithmetic(Ts... ts) { - return requires { - requires !requires { (... + ts); }; - requires !requires { (... - ts); }; - }; + return !requires { (... + ts); } && !requires { (... - ts); }; } static_assert(invalid_arithmetic(5 * activity[Bq], 5 * si::frequency[Hz])); static_assert(invalid_arithmetic(5 * activity[Bq], 10 / (2 * si::time[s]), 5 * si::frequency[Hz])); +// Implicit conversions allowed between quantities of `convertible` references +constexpr quantity speed = 120 * si::length[km] / (2 * si::time[h]); + +// Explicit casts allow changing all or only a part of the type +static_assert( + std::is_same_v< + decltype(quantity_cast(120 * si::length[km] / (2 * si::time[h]))), + quantity)>, per>>{}, + int>>); +auto q3 = quantity_cast(120 * si::length[km] / (2 * si::time[h])); +auto q4 = quantity_cast(120 * si::length[km] / (2 * si::time[h])); +auto q5 = quantity_cast(120 * si::length[km] / (2 * si::time[h])); +auto q6 = quantity_cast>(120 * si::length[km] / (2 * si::time[h])); + +// cast 1 / time_dim to use Hz + // static_assert(quantity_of); // static_assert(quantity_of); // static_assert(quantity_of); @@ -160,105 +180,6 @@ static_assert(invalid_arithmetic(5 * activity[Bq], 10 / (2 * si::time[s]), 5 * s } // namespace -namespace units::si { - -// derived unit expression template syntax verification -static_assert(is_of_type>>(1 / second)); -static_assert(is_of_type(1 / (1 / second))); - -static_assert(is_of_type(one * second)); -static_assert(is_of_type(second * one)); -static_assert(is_of_type>>(one * (1 / second))); -static_assert(is_of_type>>(1 / second * one)); - -static_assert(is_of_type>(metre * second)); -static_assert(is_of_type>>(metre * metre)); - -static_assert(is_of_type, struct second>>(metre * metre * second)); -static_assert(is_of_type, struct second>>(metre * second * metre)); - -static_assert(is_of_type, struct second>>(metre * (second * metre))); -static_assert(is_of_type, struct second>>(second * (metre * metre))); - -static_assert(is_of_type>>(1 / second * metre)); -static_assert(is_of_type(1 / second * second)); - -static_assert(is_of_type(second / one)); -static_assert(is_of_type>>(1 / second / one)); - -static_assert(is_of_type(metre / second * second)); -static_assert(is_of_type>>>(1 / second * (1 / second))); -static_assert(is_of_type>>>(1 / (second * second))); -static_assert(is_of_type>>(1 / (1 / (second * second)))); - -static_assert(is_of_type>>>(metre / second * - (1 / second))); -static_assert(is_of_type, per>>>( - metre / second * (metre / second))); -static_assert(is_of_type(metre / second * (second / metre))); - -static_assert(is_of_type>>(watt / joule)); -static_assert(is_of_type>>(joule / watt)); - -// comparisons of equivalent units -static_assert(metre / metre == one); -// static_assert(metre * metre == square_metre); -// static_assert(second * second == second_squared); -// static_assert(second * second * second == second_cubed); -// static_assert(second * (second * second) == second_cubed); -// static_assert(second_squared * second == second_cubed); -// static_assert(second * second_squared == second_cubed); - -// static_assert(1 / second * metre == metre / second); -// static_assert(metre * (1 / second) == metre / second); -// static_assert((metre / second) * (1 / second) == metre / second / second); -// static_assert((metre / second) * (1 / second) == metre / (second * second)); -// static_assert((metre / second) * (1 / second) == metre / second_squared); - -// static_assert(hertz == 1 / second); -// static_assert(newton == kilogram * metre / second_squared); -// static_assert(joule == kilogram * square_metre / second_squared); -// static_assert(joule == newton * metre); -// static_assert(watt == joule / second); -// static_assert(watt == kilogram * square_metre / second_cubed); - -// static_assert(1 / frequency_dim == second); -// static_assert(frequency_dim * second == one); - -// static_assert(metre * metre == area_dim); -// static_assert(metre * metre != volume_dim); -// static_assert(area_dim / metre == metre); - -// static_assert(metre * metre * metre == volume_dim); -// static_assert(area_dim * metre == volume_dim); -// static_assert(volume_dim / metre == area_dim); -// static_assert(volume_dim / metre / metre == metre); -// static_assert(area_dim * area_dim / metre == volume_dim); -// static_assert(area_dim * (area_dim / metre) == volume_dim); -// static_assert(volume_dim / (metre * metre) == metre); - -// static_assert(metre / second == speed_dim); -// static_assert(metre * second != speed_dim); -// static_assert(metre / second / second != speed_dim); -// static_assert(metre / speed_dim == second); -// static_assert(speed_dim * second == metre); - -// static_assert(metre / second / second == acceleration_dim); -// static_assert(metre / (second * second) == acceleration_dim); -// static_assert(speed_dim / second == acceleration_dim); -// static_assert(speed_dim / acceleration_dim == second); -// static_assert(acceleration_dim * second == speed_dim); -// static_assert(acceleration_dim * (second * second) == metre); -// static_assert(acceleration_dim / speed_dim == frequency_dim); - - -// Bq + Hz should not compile - -// Bq + Hz + 1/s should compile? - - -} // namespace units::si - namespace units { template @@ -331,3 +252,6 @@ int main() // type of Rep{1} * (mag * mag_power<10, -34> * energy[joule] * time[second]) // and inline constexpr auto planck_constant = Rep{1} * mag_planck * energy[joule] * time[second]; + + +// quantity_cast on equivalent dimensions \ No newline at end of file diff --git a/src/core/include/units/unit.h b/src/core/include/units/unit.h index a7b4af77..12e499d1 100644 --- a/src/core/include/units/unit.h +++ b/src/core/include/units/unit.h @@ -38,50 +38,12 @@ namespace units { -// namespace detail { - -// template -// inline constexpr bool can_be_prefixed = false; - -// } // namespace detail - -/** - * @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 M a Magnitude representing the (relative) size of this unit - * @tparam U a unit to use as a reference for this dimension - * - * @note U cannot be constrained with Unit as for some specializations (i.e. named_unit) - * it gets the incomplete child's type with the CRTP idiom. - */ -template -struct scaled_unit { - static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = M; - using reference = U; -}; - -// TODO: Remove when P1985 accepted namespace detail { -template -void to_base_scaled_unit(const volatile scaled_unit*); - template -inline constexpr bool is_specialization_of_scaled_unit = false; +inline constexpr bool is_unit = false; -template -inline constexpr bool is_specialization_of_scaled_unit> = true; - -} // namespace detail +} /** * @brief A concept matching all unit types in the library @@ -89,129 +51,12 @@ inline constexpr bool is_specialization_of_scaled_unit> = true * Satisfied by all unit types derived from an specialization of :class:`scaled_unit`. */ template -concept Unit = requires(T* t) { detail::to_base_scaled_unit(t); }; - -namespace detail { - -template -inline constexpr bool is_named = false; - -} - -template -concept NamedUnit = Unit && detail::is_named; - -template -struct same_unit_reference : is_same {}; - -namespace detail { - -template -struct unit_less : std::bool_constant() < type_name()> {}; - -template -using type_list_of_unit_less = expr_less; - -/** - * @brief Unpacks the list of potentially derived dimensions to a list containing only base dimensions - * - * @tparam Es Exponents of potentially derived dimensions - */ -// template -// struct unit_extract; - -// template<> -// struct unit_extract, type_list<>> { -// using num = type_list<>; -// using den = type_list<>; -// }; - -// template -// requires BaseUnit || BaseUnit -// struct unit_extract, type_list> { -// using impl = unit_extract, type_list>; -// using num = type_list_push_front; -// using den = TYPENAME impl::den; -// }; - -// template -// requires BaseUnit || BaseUnit -// struct unit_extract, type_list> { -// using impl = unit_extract, type_list>; -// using num = TYPENAME impl::num; -// using den = type_list_push_front; -// }; - -// template -// struct unit_extract, type_list> : -// unit_extract, -// type_list_push_back> {}; - -// template -// struct unit_extract, NRest...>, type_list> : -// unit_extract::num, -// power::den>::type, -// NRest...>, -// type_list_push_back::num, -// power::den>::type, -// Dens...>> {}; - - -// template -// struct unit_extract, type_list> : -// unit_extract> {}; - -// template -// struct unit_extract, type_list, DRest...>> : -// unit_extract::num, power::den>::type, -// type_list_push_back::num, -// power::den>::type, -// DRest...>> {}; - -/** - * @brief Converts user provided derived dimension specification into a valid units::normalized_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 -struct normalized_unit : detail::expr_fractions { - // private: - // using base = detail::expr_fractions; - // using extracted = unit_extract; - // using num_list = expr_consolidate>; - // using den_list = expr_consolidate>; - // using simple = expr_simplify; - // public: - // using normalized_num = TYPENAME simple::num; - // using normalized_den = TYPENAME simple::den; -}; - -} // namespace detail - -// TODO add checking for `per` and power elements as well -template -concept UnitSpec = Unit || is_specialization_of || detail::is_specialization_of_power; +concept Unit = detail::is_unit; // User should not instantiate this type!!! // It should not be exported from the module -template -struct derived_unit : detail::normalized_unit, Us...>, scaled_unit, derived_unit> { - static constexpr bool is_base = false; -}; - -/** - * @brief Unit one - * - * Unit of a dimensionless quantity. - */ -inline constexpr struct one : derived_unit<> { -} one; +template +struct scaled_unit {}; /** * @brief A named unit @@ -225,85 +70,186 @@ template struct named_unit; template -struct named_unit : scaled_unit, named_unit> { +struct named_unit { static constexpr auto symbol = Symbol; - static constexpr bool is_base = true; }; template -struct named_unit : decltype(U) { +struct named_unit { static constexpr auto symbol = Symbol; - static constexpr bool is_base = decltype(U)::is_base; }; +namespace detail { + +template +void to_base_specialization_of_named_unit(const volatile named_unit*); + +} // namespace detail + +template +concept NamedUnit = Unit && requires(T* t) { detail::to_base_specialization_of_named_unit(t); }; + +template +inline constexpr bool unit_can_be_prefixed = NamedUnit>; + /** * @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. + * coherent_unit 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 + * @tparam P prefix to be appied to the coherent_unit unit + * @tparam U coherent_unit unit */ template -// requires detail::can_be_prefixed -struct prefixed_unit : scaled_unit { - // static constexpr auto symbol = symbol + decltype(U)::symbol; - static constexpr bool is_base = decltype(U)::is_base; + requires unit_can_be_prefixed +struct prefixed_unit : std::remove_const_t { + static constexpr auto symbol = Symbol + U.symbol; }; -/** - * @brief A coherent unit of a derived quantity - * - * Defines a new coherent unit of a derived quantity. It should be passed as a coherent unit - * in the dimension's definition for such a quantity. - * - * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) - */ -// template -// struct derived_unit : downcast_dispatch(), Child>> {}; - namespace detail { -template -void is_named_impl(const volatile named_unit*); +template +inline constexpr bool is_power_of_unit = + requires { requires is_specialization_of_power && Unit; }; -template -void is_named_impl(const volatile prefixed_unit*); +template +concept UnitLike = Unit || is_power_of_unit; -template -inline constexpr bool is_named = requires(U * u) { is_named_impl(u); }; +template +inline constexpr bool is_per_of_units = false; -// template -// void can_be_prefixed_impl(const volatile named_unit*); - -// template -// void can_be_prefixed_impl(const volatile named_scaled_unit*); - -// template -// void can_be_prefixed_impl(const volatile alias_unit*); - -// template -// inline constexpr bool can_be_prefixed = requires(U * u) { can_be_prefixed_impl(u); }; - -// template -// inline constexpr bool can_be_prefixed> = can_be_prefixed; +template +inline constexpr bool is_per_of_units> = (... && UnitLike); } // namespace detail +template +concept UnitSpec = detail::UnitLike || detail::is_per_of_units; + +// User should not instantiate this type!!! +// It should not be exported from the module +template +struct derived_unit : detail::expr_fractions, Us...> {}; + +/** + * @brief Unit one + * + * Unit of a dimensionless quantity. + */ +inline constexpr struct one : derived_unit<> { +} one; + + +namespace detail { + +template +void is_unit_impl(const volatile scaled_unit*); + +template +void is_unit_impl(const volatile named_unit*); + +template +void is_unit_impl(const volatile derived_unit*); + +template + requires requires(T* t) { is_unit_impl(t); } +inline constexpr bool is_unit = true; + +/** + * @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 coherent_unit unit + * (i.e. all length units are expressed in terms of meter, all mass units are expressed in + * terms of gram, ...) + * + * @tparam M a Magnitude representing the (relative) size of this unit + * @tparam U a unit to use as a coherent_unit for this dimension + * + * @note U cannot be constrained with Unit as for some specializations (i.e. named_unit) + * it gets the incomplete child's type with the CRTP idiom. + */ +template +struct canonical_unit { + U reference_unit; + M mag; +}; + +[[nodiscard]] constexpr auto get_canonical_unit(UnitLike auto u); + +template +[[nodiscard]] constexpr auto get_canonical_unit_impl(T, const volatile scaled_unit&) +{ + auto base = get_canonical_unit(U{}); + return canonical_unit{base.reference_unit, M * base.mag}; +} + +template +[[nodiscard]] constexpr auto get_canonical_unit_impl(T t, const volatile named_unit&) +{ + return canonical_unit{t, mag<1>}; +} + +template +[[nodiscard]] constexpr auto get_canonical_unit_impl(T, const volatile named_unit&) +{ + return get_canonical_unit(U); +} + +template +[[nodiscard]] constexpr auto get_canonical_unit_impl(T, const volatile power&) +{ + auto base = get_canonical_unit(F{}); + return canonical_unit{ + derived_unit, power::exponent>>{}, + pow::exponent>(base.mag)}; +} + +template +[[nodiscard]] constexpr auto get_canonical_unit_impl(T, const volatile derived_unit&) +{ + if constexpr (type_list_size::_den_> != 0) { + auto num = get_canonical_unit(type_list_map::_num_, derived_unit>{}); + auto den = get_canonical_unit(type_list_map::_den_, derived_unit>{}); + return canonical_unit{num.reference_unit / den.reference_unit, num.mag / den.mag}; + } else { + auto num = (one * ... * get_canonical_unit(Us{}).reference_unit); + auto mag = (units::mag<1> * ... * get_canonical_unit(Us{}).mag); + return canonical_unit{num, mag}; + } +} + +[[nodiscard]] constexpr auto get_canonical_unit(UnitLike auto u) { return get_canonical_unit_impl(u, u); } + + +template +struct unit_less : std::bool_constant() < type_name()> {}; + +template +using type_list_of_unit_less = expr_less; + +} // namespace detail + + +// Operators + template [[nodiscard]] consteval Unit auto operator*(M mag, U) { - return scaled_unit{}; + // TODO Try passing magnitude parameters rather than magnitude type itself in case of a trivial magnitude + // (single integer, ratio...) + return scaled_unit>{}; } -template -[[nodiscard]] consteval Unit auto operator*(M1 mag, scaled_unit) -{ - return scaled_unit{}; -} +template +[[nodiscard]] consteval Unit auto operator*(U, M) = delete; template [[nodiscard]] consteval Unit auto operator*(U1, U2) @@ -324,72 +270,46 @@ template return detail::expr_invert(); } +template +[[nodiscard]] consteval Unit auto operator/(U, int) = delete; + template -[[nodiscard]] consteval bool operator==(U1, U2) +[[nodiscard]] consteval bool operator==(U1 lhs, U2 rhs) { - return is_same_v; + auto canonical_lhs = detail::get_canonical_unit(lhs); + auto canonical_rhs = detail::get_canonical_unit(rhs); + return is_same_v && + canonical_lhs.mag == canonical_rhs.mag; } -// template -// constexpr bool operator==(D1, D2) -// { -// return D1::symbol == D2::symbol; -// } - -// template -// requires(type_list_size == 0) && (type_list_size == 1) && -// BaseDimension> -// constexpr bool operator==(D1, D2) -// { -// return D1::symbol == type_list_front::symbol; -// } - -// template -// requires(type_list_size == 0) && (type_list_size == 1) && -// BaseDimension> -// constexpr bool operator==(D1, D2) -// { -// return type_list_front::symbol == D2::symbol; -// } - -// template -// constexpr bool operator==(D1, D2) -// { -// return std::is_same_v && -// std::is_same_v; -// } - -// TODO implement this -// template -// [[nodiscard]] consteval bool equivalent(D1, D2) -// { -// return is_same_v, detail::dim_type>; -// } - +// Convertible template -[[nodiscard]] consteval bool convertible(U1, U2) +[[nodiscard]] consteval bool convertible(U1 lhs, U2 rhs) { - // TODO implement this - return std::derived_from || std::derived_from; + auto canonical_lhs = detail::get_canonical_unit(lhs); + auto canonical_rhs = detail::get_canonical_unit(rhs); + return is_same_v; } +// Helper types and variable factories template struct square_ : decltype(U{} * U{}) {}; -template -inline constexpr square_> square; - template struct cubic_ : decltype(U{} * U{} * U{}) {}; +// it is not allowed to use the same name for a variable and class template +// (even though it works for objects of regular class types) +template +inline constexpr square_> square; + template inline constexpr cubic_> cubic; } // namespace units namespace std { - // TODO implement this template requires(units::convertible(U1{}, U2{})) diff --git a/src/systems/si/include/units/si/units.h b/src/systems/si/include/units/si/units.h index 2768c444..26b46db8 100644 --- a/src/systems/si/include/units/si/units.h +++ b/src/systems/si/include/units/si/units.h @@ -86,3 +86,16 @@ inline constexpr struct electronvolt : named_unit<"eV", mag +inline constexpr bool unit_can_be_prefixed = false; +template<> +inline constexpr bool unit_can_be_prefixed = false; +template<> +inline constexpr bool unit_can_be_prefixed = false; +template<> +inline constexpr bool unit_can_be_prefixed = false; + +} // namespace units diff --git a/test/unit_test/static/CMakeLists.txt b/test/unit_test/static/CMakeLists.txt index 6abc8d0a..5d48f53d 100644 --- a/test/unit_test/static/CMakeLists.txt +++ b/test/unit_test/static/CMakeLists.txt @@ -35,6 +35,7 @@ cmake_minimum_required(VERSION 3.2) add_library( unit_tests_static dimension_test.cpp + # angle_test.cpp # cgs_test.cpp # chrono_test.cpp @@ -48,6 +49,7 @@ add_library( # iec80000_test.cpp # kind_test.cpp magnitude_test.cpp + # math_test.cpp # point_origin_test.cpp # prime_test.cpp @@ -59,7 +61,8 @@ add_library( # si_hep_test.cpp # symbol_text_test.cpp # type_list_test.cpp - # unit_test.cpp + unit_test.cpp + # us_test.cpp ) diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index 4c3c4569..907f461b 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -20,22 +20,26 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "test_tools.h" -#include -#include -#include -#include -#include +#include #include namespace { +using namespace units; +using namespace units::detail; + +template +inline constexpr bool is_of_type = std::is_same_v, T>; + +using one_ = struct one; + // clang-format off // base units inline constexpr struct second_ : named_unit<"s"> {} second; inline constexpr struct metre_ : named_unit<"m"> {} metre; inline constexpr struct gram_ : named_unit<"g"> {} gram; -inline constexpr struct kilogram_ : decltype(kilo) {} kilogram; +inline constexpr struct kilogram_ : decltype(si::kilo) {} kilogram; +inline constexpr struct kelvin_ : named_unit<"K"> {} kelvin; // derived named units inline constexpr struct radian_ : named_unit<"rad", metre / metre> {} radian; @@ -46,104 +50,371 @@ inline constexpr struct newton_ : named_unit<"N", kilogram * metre / square> {} pascal; inline constexpr struct joule_ : named_unit<"J", newton * metre> {} joule; inline constexpr struct watt_ : named_unit<"W", joule / second> {} watt; +inline constexpr struct degree_Celsius_ : named_unit {} degree_Celsius; inline constexpr struct minute_ : named_unit<"min", mag<60> * second> {} minute; inline constexpr struct hour_ : named_unit<"h", mag<60> * minute> {} hour; inline constexpr struct day_ : named_unit<"d", mag<24> * hour> {} day; inline constexpr struct astronomical_unit_ : named_unit<"au", mag<149'597'870'700> * metre> {} astronomical_unit; inline constexpr struct degree_ : named_unit * radian> {} degree; -inline constexpr struct are_ : named_unit<"a", square>> {} are; -inline constexpr struct hectare_ : decltype(hecto) {} hectare; -inline constexpr struct litre_ : named_unit<"l", cubic>> {} litre; +inline constexpr struct are_ : named_unit<"a", square>> {} are; +inline constexpr struct hectare_ : decltype(si::hecto) {} hectare; +inline constexpr struct litre_ : named_unit<"l", cubic>> {} litre; inline constexpr struct tonne_ : named_unit<"t", mag<1000> * kilogram> {} tonne; inline constexpr struct dalton_ : named_unit<"Da", mag * mag_power<10, -27> * kilogram> {} dalton; inline constexpr struct electronvolt_ : named_unit<"eV", mag * mag_power<10, -19> * joule> {} electronvolt; -inline constexpr struct kilometre_ : decltype(kilo) {} kilometre; +inline constexpr struct kilometre_ : decltype(si::kilo) {} kilometre; +inline constexpr struct kilojoule_ : decltype(si::kilo) {} kilojoule; // clang-format on -} - // concepts verification static_assert(Unit); static_assert(Unit); static_assert(Unit); static_assert(Unit); static_assert(Unit); -static_assert(Unit)>); +static_assert(Unit)>); static_assert(Unit)>); static_assert(Unit)>); static_assert(Unit * second)>); static_assert(Unit); static_assert(NamedUnit); -static_assert(NamedUnit); static_assert(NamedUnit); static_assert(NamedUnit); static_assert(NamedUnit); -static_assert(!NamedUnit)>); +static_assert(NamedUnit); +static_assert(!NamedUnit); +static_assert(!NamedUnit); +static_assert(!NamedUnit); +static_assert(!NamedUnit)>); static_assert(!NamedUnit)>); static_assert(!NamedUnit)>); static_assert(!NamedUnit * second)>); static_assert(!NamedUnit); +template +constexpr bool print(); -static_assert(kilo == kilometre); -static_assert(mag<1000> * metre == kilo); +// named unit +static_assert(is_of_type); +static_assert(is_of_type); +static_assert(get_canonical_unit(metre).mag == mag<1>); +static_assert(convertible(metre, metre)); +static_assert(!convertible(metre, second)); +static_assert(metre == metre); +static_assert(metre != second); + +static_assert(is_of_type); +static_assert(is_of_type); +static_assert(get_canonical_unit(degree_Celsius).mag == mag<1>); +static_assert(convertible(degree_Celsius, kelvin)); +static_assert(degree_Celsius == kelvin); + +static_assert(is_of_type); +static_assert(is_of_type); +static_assert(get_canonical_unit(radian).mag == mag<1>); +static_assert(convertible(minute, second)); +static_assert(minute != second); + +static_assert(is_of_type); +static_assert(is_of_type); +static_assert(get_canonical_unit(steradian).mag == mag<1>); +static_assert(convertible(radian, steradian)); // !!! +static_assert(radian == steradian); // !!! + +static_assert(is_of_type); +static_assert(is_of_type); +static_assert(get_canonical_unit(minute).mag == mag<60>); +static_assert(convertible(minute, second)); +static_assert(minute != second); + +static_assert(is_of_type); +static_assert(is_of_type); +static_assert(get_canonical_unit(hour).mag == mag<3600>); +static_assert(convertible(hour, second)); + +static_assert(convertible(hour, minute)); +static_assert(convertible(hour, hour)); +static_assert(hour != second); +static_assert(hour != minute); +static_assert(hour == hour); + +static_assert(is_of_type); +static_assert( + is_of_type>>>); +static_assert(get_canonical_unit(newton).mag == mag<1000>); // !!! (because of kilogram) +static_assert(convertible(newton, newton)); +static_assert(newton == newton); + +static_assert(is_of_type); +static_assert( + is_of_type, per>>>); +static_assert(get_canonical_unit(joule).mag == mag<1000>); // !!! (because of kilogram) +static_assert(convertible(joule, joule)); +static_assert(joule == joule); +static_assert(joule != newton); + + +// prefixed_unit +static_assert(is_of_type); +static_assert(is_of_type); +static_assert(get_canonical_unit(kilometre).mag == mag<1000>); +static_assert(convertible(kilometre, metre)); +static_assert(kilometre != metre); +static_assert(kilometre.symbol == "km"); + +static_assert(is_of_type); +static_assert(is_of_type, per>>>); +static_assert(get_canonical_unit(kilojoule).mag == mag<1'000'000>); +static_assert(convertible(kilojoule, joule)); +static_assert(kilojoule != joule); +static_assert(kilojoule.symbol == "kJ"); + +static_assert(is_of_type, si::kilo_>); +static_assert(is_of_type, si::kilo_>); + + +// prefixes +static_assert(si::yocto.symbol == "ym"); +static_assert(si::zepto.symbol == "zm"); +static_assert(si::atto.symbol == "am"); +static_assert(si::femto.symbol == "fm"); +static_assert(si::pico.symbol == "pm"); +static_assert(si::nano.symbol == "nm"); +static_assert(si::micro.symbol == basic_symbol_text{"µm", "um"}); +static_assert(si::milli.symbol == "mm"); +static_assert(si::centi.symbol == "cm"); +static_assert(si::deci.symbol == "dm"); +static_assert(si::deca.symbol == "dam"); +static_assert(si::hecto.symbol == "hm"); +static_assert(si::kilo.symbol == "km"); +static_assert(si::mega.symbol == "Mm"); +static_assert(si::giga.symbol == "Gm"); +static_assert(si::tera.symbol == "Tm"); +static_assert(si::peta.symbol == "Pm"); +static_assert(si::exa.symbol == "Em"); +static_assert(si::zetta.symbol == "Zm"); +static_assert(si::yotta.symbol == "Ym"); + + +// scaled_unit +constexpr auto u1 = mag<1> * metre; +static_assert(is_of_type, metre_>>); +static_assert(is_of_type); +static_assert(get_canonical_unit(u1).mag == mag<1>); + +constexpr auto u2 = mag<2> * kilometre; +static_assert(is_of_type, kilometre_>>); +static_assert(is_of_type); +static_assert(get_canonical_unit(u2).mag == mag<2000>); + +constexpr auto u3 = mag<42> * si::kilo; +static_assert(is_of_type, si::kilo_>>); +static_assert( + is_of_type, per>>>); +static_assert(get_canonical_unit(u3).mag == mag<42'000'000>); + + +// derived unit expression template syntax verification +static_assert(is_of_type<1 / second, derived_unit>>); +static_assert(is_of_type<1 / (1 / second), second_>); + +static_assert(is_of_type); +static_assert(is_of_type); +static_assert(is_of_type>>); +static_assert(is_of_type<1 / second * one, derived_unit>>); + +static_assert(is_of_type>); +static_assert(is_of_type>>); + +static_assert(is_of_type, second_>>); +static_assert(is_of_type, second_>>); + +static_assert(is_of_type, second_>>); +static_assert(is_of_type, second_>>); + +static_assert(is_of_type<1 / second * metre, derived_unit>>); +static_assert(is_of_type<1 / second * second, one_>); + +static_assert(is_of_type); +static_assert(is_of_type<1 / second / one, derived_unit>>); + +static_assert(is_of_type); +static_assert(is_of_type<1 / second * (1 / second), derived_unit>>>); +static_assert(is_of_type<1 / (second * second), derived_unit>>>); +static_assert(is_of_type<1 / (1 / (second * second)), derived_unit>>); + +static_assert(is_of_type>>>); +static_assert(is_of_type, per>>>); +static_assert(is_of_type); + +static_assert(is_of_type>>); +static_assert(is_of_type>>); + + +// derived unit normalization +constexpr auto u4 = metre / second; +static_assert(is_of_type>>); +static_assert(get_canonical_unit(u4).mag == mag<1>); + +constexpr auto u5 = kilometre / second; +static_assert(is_of_type>>); +static_assert(is_of_type>>); +static_assert(get_canonical_unit(u5).mag == mag<1000>); + +constexpr auto u6 = kilometre / hour; +static_assert(is_of_type>>); +static_assert(is_of_type>>); +static_assert(get_canonical_unit(u6).mag == mag); + +constexpr auto u7 = mag<1000> * kilometre / hour; +static_assert(is_of_type, kilometre_>, per>>); +static_assert(is_of_type>>); +static_assert(get_canonical_unit(u7).mag == mag); + +constexpr auto u8 = mag<1000> * (kilometre / hour); +static_assert(is_of_type, derived_unit>>>); +static_assert(is_of_type>>); +static_assert(get_canonical_unit(u8).mag == mag); + +constexpr auto u9 = 1 / hour * (mag<1000> * kilometre); +static_assert(is_of_type, kilometre_>, per>>); +static_assert(is_of_type>>); +static_assert(get_canonical_unit(u9).mag == mag); + +// comparisons of the same units +static_assert(second == second); +static_assert(metre / second == metre / second); + +// comparisons of equivalent units (named vs unnamed/derived) +static_assert(1 / second == hertz); +static_assert(convertible(1 / second, hertz)); + +// comparisons of equivalent but not convertible units +static_assert(hertz == becquerel); +static_assert(convertible(hertz, becquerel)); + +// comparisons of scaled units +static_assert(si::kilo == kilometre); +static_assert(mag<1000> * metre == si::kilo); static_assert(mag<1000> * metre == kilometre); -static_assert(equivalent, kilometre>); -static_assert(equivalent * metre, kilo>); -static_assert(equivalent * metre, kilometre>); +static_assert(convertible(si::kilo, kilometre)); +static_assert(convertible(mag<1000> * metre, si::kilo)); +static_assert(convertible(mag<1000> * metre, kilometre)); static_assert(metre != kilometre); +static_assert(convertible(metre, kilometre)); static_assert(mag<100> * metre != kilometre); -static_assert(milli != kilometre); -static_assert(!equivalent); -static_assert(!equivalent * metre, kilometre>); -static_assert(!equivalent, kilometre>); +static_assert(convertible(mag<100> * metre, kilometre)); +static_assert(si::milli != kilometre); +static_assert(convertible(si::milli, kilometre)); -static_assert(1 / second != hertz); -static_assert(becquerel != hertz); -static_assert(equivalent<1 / second, hertz>); -static_assert(!equivalent); +// one +static_assert(metre / metre == one); +// static_assert(metre * metre == square_metre); +// static_assert(second * second == second_squared); +// static_assert(second * second * second == second_cubed); +// static_assert(second * (second * second) == second_cubed); +// static_assert(second_squared * second == second_cubed); +// static_assert(second * second_squared == second_cubed); -using namespace units; -using namespace units::isq; +// static_assert(1 / second * metre == metre / second); +// static_assert(metre * (1 / second) == metre / second); +// static_assert((metre / second) * (1 / second) == metre / second / second); +// static_assert((metre / second) * (1 / second) == metre / (second * second)); +// static_assert((metre / second) * (1 / second) == metre / second_squared); -struct metre : named_unit {}; -struct centimetre : prefixed_unit {}; -struct kilometre : prefixed_unit {}; -struct yard : named_scaled_unit(), metre> {}; -struct foot : named_scaled_unit(), yard> {}; -struct dim_length : base_dimension<"length", metre> {}; +// static_assert(hertz == 1 / second); +// static_assert(newton == kilogram * metre / second_squared); +// static_assert(joule == kilogram * square_metre / second_squared); +// static_assert(joule == newton * metre); +// static_assert(watt == joule / second); +// static_assert(watt == kilogram * square_metre / second_cubed); -struct second : named_unit {}; -struct hour : named_scaled_unit(), second> {}; -struct dim_time : base_dimension<"time", second> {}; +// static_assert(1 / frequency_dim == second); +// static_assert(frequency_dim * second == one); -struct kelvin : named_unit {}; +// static_assert(metre * metre == area_dim); +// static_assert(metre * metre != volume_dim); +// static_assert(area_dim / metre == metre); -#if !UNITS_COMP_MSVC -static_assert([](P) { - return !requires { typename prefixed_unit; }; -}(si::kilo{})); // no prefix allowed -#endif +// static_assert(metre * metre * metre == volume_dim); +// static_assert(area_dim * metre == volume_dim); +// static_assert(volume_dim / metre == area_dim); +// static_assert(volume_dim / metre / metre == metre); +// static_assert(area_dim * area_dim / metre == volume_dim); +// static_assert(area_dim * (area_dim / metre) == volume_dim); +// static_assert(volume_dim / (metre * metre) == metre); -struct metre_per_second : derived_unit {}; -struct dim_speed : - derived_dimension, units::exponent> {}; -struct kilometre_per_hour : derived_scaled_unit {}; +// static_assert(metre / second == speed_dim); +// static_assert(metre * second != speed_dim); +// static_assert(metre / second / second != speed_dim); +// static_assert(metre / speed_dim == second); +// static_assert(speed_dim * second == metre); -static_assert(equivalent); -static_assert(equivalent); -static_assert(compare(), metre>>, metre>); -static_assert(compare(), metre>>, centimetre>); -static_assert(compare>, yard>); -static_assert(compare(), metre>>, foot>); -static_assert(compare>, kilometre_per_hour>); +// static_assert(metre / second / second == acceleration_dim); +// static_assert(metre / (second * second) == acceleration_dim); +// static_assert(speed_dim / second == acceleration_dim); +// static_assert(speed_dim / acceleration_dim == second); +// static_assert(acceleration_dim * second == speed_dim); +// static_assert(acceleration_dim * (second * second) == metre); +// static_assert(acceleration_dim / speed_dim == frequency_dim); + + +// milli / milli == micro / micro; +// milli * kilo == deci * deca; + +// Bq + Hz should not compile + +// Bq + Hz + 1/s should compile? + + +// using namespace units; +// using namespace units::isq; + +// struct metre : named_unit {}; +// struct centimetre : prefixed_unit {}; +// struct kilometre : prefixed_unit {}; +// 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 : named_scaled_unit(), second> {}; +// struct dim_time : base_dimension<"time", second> {}; + +// struct kelvin : named_unit {}; + +// #if !UNITS_COMP_MSVC +// static_assert([](P) { +// return !requires { typename prefixed_unit; }; +// }(si::kilo{})); // no prefix allowed +// #endif + +// struct metre_per_second : derived_unit {}; +// struct dim_speed : +// derived_dimension, units::exponent> {}; +// struct kilometre_per_hour : derived_scaled_unit {}; + +// static_assert(equivalent); +// static_assert(equivalent); +// static_assert(compare(), metre>>, metre>); +// static_assert(compare(), metre>>, centimetre>); +// static_assert(compare>, yard>); +// static_assert(compare(), metre>>, foot>); +// static_assert(compare>, kilometre_per_hour>); + +// static_assert(centimetre::symbol == "cm"); +// static_assert(kilometre::symbol == "km"); +// static_assert(kilometre_per_hour::symbol == "km/h"); + + +// static_assert(si::metre != si::kilometre); +// static_assert(!equivalent(si::metre, si::kilometre)); +// static_assert(convertible(si::metre, si::kilometre)); -static_assert(centimetre::symbol == "cm"); -static_assert(kilometre::symbol == "km"); -static_assert(kilometre_per_hour::symbol == "km/h"); } // namespace