// 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 "test_tools.h" #include #include #include #include #include #include #include namespace { using namespace mp_units; using namespace mp_units::si::unit_symbols; using namespace std::chrono_literals; using sys_seconds = std::chrono::time_point; inline constexpr struct mean_sea_level : absolute_point_origin { } mean_sea_level; inline constexpr quantity_point ground_level{42 * isq::height[m]}; inline constexpr quantity_point everest_base_camp{5364 * m}; QUANTITY_SPEC(special_height, isq::height); QUANTITY_SPEC(activity, 1 / isq::time); ///////////////////// // class invariants ///////////////////// static_assert(sizeof(quantity_point) == sizeof(double)); static_assert(sizeof(quantity_point) == sizeof(double)); static_assert(sizeof(quantity_point{}, short>) == sizeof(short)); static_assert(sizeof(quantity_point{}, short>) == sizeof(short)); template typename QP> concept invalid_types = requires { // unit of a different dimension requires !requires { typename QP{}, int>; }; requires !requires { typename QP>{}, int>; }; // not compatible quantity_spec requires !requires { typename QP{}, int>; }; requires !requires { typename QP{}, int>; }; // quantity used as Rep requires !requires { typename QP{}, quantity>; }; // quantity point used as Rep requires !requires { typename QP{}, quantity_point>; }; // reordered arguments requires !requires { typename QP{}, si::metre, double>; }; // quantity_spec used as origin requires !requires { typename QP; }; // quantity_spec used as a reference requires !requires { typename QP{}, int>; }; // dimension used as a reference requires !requires { typename QP{}, int>; }; // bool used as a representation type requires !requires { typename QP{}, bool>; }; // incompatible quantity_spec in the origin and quantity_point requires !requires { typename QP{}, int>; }; requires !requires { typename QP; }; requires !requires { typename QP; }; }; static_assert(invalid_types); template typename QP> concept valid_types = requires { typename QP>{}>; typename QP{}>; typename QP{}>; typename QP{}>; typename QP{}>; typename QP>{}>; typename QP>{}>; typename QP{}>; typename QP; typename QP; typename QP; typename QP; typename QP; typename QP; }; static_assert(valid_types); static_assert(std::is_trivially_default_constructible_v>); static_assert(std::is_trivially_copy_constructible_v>); static_assert(std::is_trivially_move_constructible_v>); static_assert(std::is_trivially_copy_assignable_v>); static_assert(std::is_trivially_move_assignable_v>); static_assert(std::is_trivially_destructible_v>); static_assert(std::is_nothrow_default_constructible_v>); static_assert(std::is_nothrow_copy_constructible_v>); static_assert(std::is_nothrow_move_constructible_v>); static_assert(std::is_nothrow_copy_assignable_v>); static_assert(std::is_nothrow_move_assignable_v>); static_assert(std::is_nothrow_destructible_v>); static_assert(std::is_trivially_copyable_v>); static_assert(std::is_standard_layout_v>); static_assert(std::default_initializable>); static_assert(std::move_constructible>); static_assert(std::copy_constructible>); static_assert(std::equality_comparable>); static_assert(std::totally_ordered>); static_assert(std::regular>); static_assert(std::three_way_comparable>); ////////////////// // member values ////////////////// static_assert(quantity_point::reference == si::metre); static_assert(quantity_point::quantity_spec == kind_of); static_assert(quantity_point::dimension == isq::dim_length); static_assert(quantity_point::unit == si::metre); static_assert(is_of_type::point_origin, absolute_point_origin>>); static_assert(quantity_point::reference == isq::length[m]); static_assert(quantity_point::quantity_spec == isq::length); static_assert(quantity_point::dimension == isq::dim_length); static_assert(quantity_point::unit == si::metre); static_assert(is_of_type::point_origin, absolute_point_origin>); static_assert(is_of_type::absolute_point_origin, absolute_point_origin>); static_assert(quantity_point::reference == isq::height[m]); static_assert(quantity_point::quantity_spec == isq::height); static_assert(quantity_point::dimension == isq::dim_length); static_assert(quantity_point::unit == si::metre); static_assert(is_of_type::point_origin, struct mean_sea_level>); static_assert(is_of_type::absolute_point_origin, struct mean_sea_level>); static_assert(quantity_point::reference == isq::height[m]); static_assert(quantity_point::quantity_spec == isq::height); static_assert(quantity_point::dimension == isq::dim_length); static_assert(quantity_point::unit == si::metre); static_assert( is_of_type::point_origin, std::remove_const_t>); static_assert(is_of_type::absolute_point_origin, struct mean_sea_level>); ////////////////// // member types ////////////////// static_assert(is_same_v::rep, double>); static_assert(is_same_v::quantity_type, quantity>); static_assert(is_same_v{}, int>::rep, int>); static_assert(is_same_v{}, int>::quantity_type, quantity>); static_assert(is_same_v::rep, double>); static_assert(is_same_v::quantity_type, quantity>); static_assert(is_same_v{}, int>::rep, int>); static_assert(is_same_v{}, int>::quantity_type, quantity>); //////////////////////////// // static member functions //////////////////////////// static_assert(quantity_point::zero().relative().number() == 0); static_assert(quantity_point::min().relative().number() == std::numeric_limits::lowest()); static_assert(quantity_point::max().relative().number() == std::numeric_limits::max()); static_assert(quantity_point{}, int>::zero().relative().number() == 0); static_assert(quantity_point{}, int>::min().relative().number() == std::numeric_limits::lowest()); static_assert(quantity_point{}, int>::max().relative().number() == std::numeric_limits::max()); ////////////////////////////// // construction from a value ////////////////////////////// // construction from a value is private static_assert(!std::constructible_from, double>); static_assert(!std::convertible_to>); static_assert(!std::constructible_from{}, int>, int>); static_assert(!std::convertible_to{}, int>>); static_assert(!std::constructible_from, double>); static_assert(!std::convertible_to>); static_assert( !std::constructible_from{}, int>, int>); static_assert( !std::convertible_to{}, int>>); static_assert(!std::constructible_from, double>); static_assert(!std::convertible_to>); static_assert( !std::constructible_from{}, int>, int>); static_assert( !std::convertible_to{}, int>>); ///////////////////////////////// // construction from a quantity ///////////////////////////////// static_assert(std::constructible_from, quantity>); static_assert(!std::convertible_to, quantity_point>); static_assert(std::constructible_from, quantity>); static_assert(!std::convertible_to, quantity_point>); static_assert(std::constructible_from, quantity>); static_assert(!std::convertible_to, quantity_point>); static_assert(std::constructible_from, quantity>); static_assert(!std::convertible_to, quantity_point>); static_assert(std::constructible_from, quantity>); static_assert(!std::convertible_to, quantity_point>); static_assert(std::constructible_from, quantity>); static_assert(!std::convertible_to, quantity_point>); // different dimensions static_assert(!std::constructible_from, quantity>); static_assert(!std::convertible_to, quantity_point>); // non-convertible quantity_specs static_assert(!std::constructible_from, quantity>); static_assert(!std::convertible_to, quantity_point>); // quantity-like static_assert(std::constructible_from, std::chrono::seconds>); static_assert(!std::convertible_to>); /////////////////////////////////////// // construction from a quantity point /////////////////////////////////////// // same origins static_assert(std::constructible_from, quantity_point>); static_assert(std::convertible_to, quantity_point>); static_assert(std::constructible_from, quantity_point>); static_assert(std::convertible_to, quantity_point>); static_assert(std::constructible_from, quantity_point>); static_assert(std::convertible_to, quantity_point>); static_assert(std::constructible_from, quantity_point>); static_assert(std::convertible_to, quantity_point>); static_assert(std::constructible_from, quantity_point>); static_assert(std::convertible_to, quantity_point>); static_assert(std::constructible_from, quantity_point>); static_assert(std::convertible_to, quantity_point>); // different dimensions static_assert(!std::constructible_from, quantity_point>); static_assert(!std::convertible_to, quantity_point>); // non-convertible quantity_specs static_assert(!std::constructible_from, quantity_point>); static_assert(!std::convertible_to, quantity_point>); // implicit conversion from another quantity only if non-truncating // int -> double OK static_assert(std::constructible_from, quantity_point{}, int>>); static_assert(std::convertible_to{}, int>, quantity_point>); // truncating double -> int not allowed static_assert(!std::constructible_from{}, int>, quantity_point>); static_assert(!std::convertible_to, quantity_point{}, int>>); // kilometre -> metre OK static_assert(std::constructible_from{}, int>, quantity_point{}, int>>); static_assert(std::convertible_to{}, int>, quantity_point{}, int>>); // truncating metre -> kilometre not allowed static_assert(!std::constructible_from{}, int>, quantity_point{}, int>>); static_assert(!std::convertible_to{}, int>, quantity_point{}, int>>); // converting to double always OK static_assert(std::constructible_from, quantity_point{}, int>>); static_assert(std::convertible_to{}, int>, quantity_point>); static_assert(std::constructible_from, quantity_point{}, int>>); static_assert(std::convertible_to{}, int>, quantity_point>); // same but not a default origin static_assert(std::constructible_from, quantity_point>); static_assert( std::convertible_to, quantity_point>); static_assert( std::constructible_from, quantity_point>); static_assert( std::convertible_to, quantity_point>); static_assert( std::constructible_from, quantity_point>); static_assert( std::convertible_to, quantity_point>); static_assert(std::constructible_from, quantity_point>); static_assert(std::convertible_to, quantity_point>); static_assert(!std::constructible_from, quantity_point>); static_assert(!std::convertible_to, quantity_point>); // different origins static_assert(!std::constructible_from, quantity_point>); static_assert(!std::convertible_to, quantity_point>); static_assert(!std::constructible_from, quantity_point>); static_assert(!std::convertible_to, quantity_point>); // quantity-point-like static_assert( std::constructible_from>, sys_seconds>); static_assert( !std::convertible_to>>); // incompatible origin static_assert(!std::constructible_from, sys_seconds>); static_assert(!std::convertible_to>); ////////////////////////////////// // obtaining a relative quantity ////////////////////////////////// static_assert(quantity_point(42 * m).relative() == 42 * m); static_assert(quantity_point(isq::height(42 * m)).relative() == 42 * m); static_assert(quantity_point(1 * one).relative() == 1 * one); static_assert(quantity_point(dimensionless(1 * one)).relative() == 1 * one); static_assert(quantity_point(42 * m).relative() == 42 * m); static_assert(quantity_point(42 * m).relative() == 42 * m); static_assert(quantity_point(quantity_point(42 * m)) .relative() == 84 * m); static_assert(quantity_point(quantity_point(84 * m)) .relative() == 42 * m); /////////////////////////////////// // obtaining an absolute quantity /////////////////////////////////// static_assert(quantity_point(42 * m).absolute() == 42 * m); static_assert(quantity_point(isq::height(42 * m)).absolute() == 42 * m); static_assert(quantity_point(1 * one).absolute() == 1 * one); static_assert(quantity_point(dimensionless(1 * one)).absolute() == 1 * one); static_assert(quantity_point(42 * m).absolute() == 42 * m); static_assert(quantity_point(42 * m).absolute() == 84 * m); static_assert(quantity_point(quantity_point(42 * m)) .absolute() == 84 * m); static_assert(quantity_point(quantity_point(84 * m)) .absolute() == 84 * m); /////////////////////////////////// // converting to a different unit /////////////////////////////////// static_assert(quantity_point(2. * km)[km].relative().number() == 2.); static_assert(quantity_point(2. * km)[m].relative().number() == 2000.); static_assert(quantity_point(2000. * m)[km].relative().number() == 2.); static_assert( quantity_point{}, int>(2 * km)[km].relative().number() == 2); static_assert( quantity_point{}, int>(2 * km)[m].relative().number() == 2000); #if MP_UNITS_COMP_GCC != 10 || MP_UNITS_COMP_GCC_MINOR > 2 template typename QP> concept invalid_unit_conversion = requires { requires !requires { QP{}, int>(2000 * m)[km]; }; // truncating conversion requires !requires { QP{}, int>(2 * m)[s]; }; // invalid unit }; static_assert(invalid_unit_conversion); #endif ///////// // CTAD ///////// static_assert(std::is_same_v); static_assert(std::is_same_v); static_assert(quantity_point{123. * m}.unit == si::metre); static_assert(quantity_point{123. * m}.quantity_spec == kind_of); static_assert(quantity_point{123. * h}.unit == si::hour); static_assert(quantity_point{123. * h}.quantity_spec == kind_of); using namespace std::chrono_literals; static_assert(std::is_same_v); static_assert(std::is_same_v, chrono_point_origin_>); static_assert(quantity_point{sys_seconds{24h}}.unit == si::second); static_assert(quantity_point{sys_seconds{24h}}.quantity_spec == kind_of); //////////////////////// // assignment operator //////////////////////// static_assert(([]() { quantity_point l1{1 * m}, l2{2 * m}; return l2 = l1; }()) .relative() == 1 * m); static_assert(([]() { const quantity_point l1{1 * m}; quantity_point l2{2 * m}; return l2 = l1; }()) .relative() == 1 * m); static_assert(([]() { quantity_point l1{1 * m}, l2{2 * m}; return l2 = std::move(l1); }()) .relative() == 1 * m); //////////////////// // unary operators //////////////////// static_assert([](auto v) { auto vv = v++; return std::pair(v, vv); }(quantity_point(123 * m)) == std::pair(quantity_point(124 * m), quantity_point(123 * m))); static_assert([](auto v) { auto vv = ++v; return std::pair(v, vv); }(quantity_point(123 * m)) == std::pair(quantity_point(124 * m), quantity_point(124 * m))); static_assert([](auto v) { auto vv = v--; return std::pair(v, vv); }(quantity_point(123 * m)) == std::pair(quantity_point(122 * m), quantity_point(123 * m))); static_assert([](auto v) { auto vv = --v; return std::pair(v, vv); }(quantity_point(123 * m)) == std::pair(quantity_point(122 * m), quantity_point(122 * m))); //////////////////////// // compound assignment //////////////////////// // same type static_assert((quantity_point(1 * m) += 1 * m).relative().number() == 2); static_assert((quantity_point(2 * m) -= 1 * m).relative().number() == 1); // different types static_assert((quantity_point(2.5 * m) += 3 * m).relative().number() == 5.5); static_assert((quantity_point(123 * m) += 1 * km).relative().number() == 1123); static_assert((quantity_point(5.5 * m) -= 3 * m).relative().number() == 2.5); static_assert((quantity_point(1123 * m) -= 1 * km).relative().number() == 123); template typename QP> concept invalid_compound_assignments = requires() { // truncating not allowed requires !requires(QP{}, int> l) { l += 2.5 * m; }; requires !requires(QP{}, int> l) { l -= 2.5 * m; }; requires !requires(QP{}, int> l) { l += 2 * isq::length[m]; }; requires !requires(QP{}, int> l) { l -= 2 * isq::length[m]; }; // only quantities can be added or subtracted requires !requires(QP{}, int> l) { l += 2; }; requires !requires(QP{}, int> l) { l -= 2; }; // no unit constants requires !requires(QP{}, int> l) { l += m; }; requires !requires(QP{}, int> l) { l -= m; }; }; static_assert(invalid_compound_assignments); //////////////////// // binary operators //////////////////// template typename QP> concept invalid_binary_operations = requires { // can't add more generic quantity (violates point_origin quantity_spec) requires !requires { QP(1 * m) + isq::length(1 * m); }; requires !requires { isq::length(1 * m) + QP(1 * m); }; // can't subtract more generic quantity (violates point_origin quantity_spec) requires !requires { QP(1 * m) - isq::length(1 * m); }; // quantity point can't be subtracted from a quantity requires !requires { 1 * m - QP(1 * m); }; // no crossdimensional addition and subtraction requires !requires { QP(1 * m) + 1 * s; }; requires !requires { QP(1 * m) - 1 * s; }; // unit constants requires !requires { QP(1) + m; }; requires !requires { QP(1) - m; }; requires !requires { m + QP(1); }; requires !requires { m - QP(1); }; }; static_assert(invalid_binary_operations); // same representation type static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type<1 * m + quantity_point(1 * m), quantity_point>{}, int>>); static_assert(is_of_type<1 * m + quantity_point(1 * km), quantity_point>{}, int>>); static_assert(is_of_type<1 * km + quantity_point(1 * m), quantity_point>{}, int>>); static_assert(is_of_type<1 * m + quantity_point(isq::length(1 * m)), quantity_point{}, int>>); static_assert(is_of_type<1 * m + quantity_point(isq::length(1 * km)), quantity_point{}, int>>); static_assert(is_of_type<1 * km + quantity_point(isq::length(1 * m)), quantity_point{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); // check for integral types promotion static_assert( is_same_v); static_assert( is_same_v); static_assert( is_same_v); static_assert((quantity_point{std::uint8_t(128) * m} + std::uint8_t(128) * m).relative().number() == std::uint8_t(128) + std::uint8_t(128)); static_assert((std::uint8_t(128) * m + quantity_point{std::uint8_t(128) * m}).relative().number() == std::uint8_t(128) + std::uint8_t(128)); static_assert((quantity_point{std::uint8_t(0) * m} - std::uint8_t(1) * m).relative().number() == std::uint8_t(0) - std::uint8_t(1)); // different representation types static_assert(is_of_type>{}, double>>); static_assert(is_of_type<1. * m + quantity_point{1 * m}, quantity_point>{}, double>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type<1 * m + quantity_point{1. * km}, quantity_point>{}, double>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type<1 * km + quantity_point{1. * m}, quantity_point>{}, double>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type>{}, double>>); // different units static_assert(is_of_type>{}, int>>); static_assert(is_of_type<1 * m + quantity_point{1 * km}, quantity_point>{}, int>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type<1. * m + quantity_point{1 * km}, quantity_point>{}, double>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type<1 * m + quantity_point{1. * km}, quantity_point>{}, double>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type<1. * m + quantity_point{1. * km}, quantity_point>{}, double>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type<1 * km + quantity_point{1 * m}, quantity_point>{}, int>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type<1. * km + quantity_point{1 * m}, quantity_point>{}, double>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type<1 * km + quantity_point{1. * m}, quantity_point>{}, double>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type<1. * km + quantity_point{1. * m}, quantity_point>{}, double>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type>{}, int>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type>{}, double>>); static_assert(is_of_type>{}, double>>); static_assert((quantity_point{1 * m} + 1 * m).relative().number() == 2); static_assert((1 * m + quantity_point{1 * m}).relative().number() == 2); static_assert((quantity_point{1 * m} + 1 * km).relative().number() == 1001); static_assert((1 * m + quantity_point{1 * km}).relative().number() == 1001); static_assert((quantity_point{1 * km} + 1 * m).relative().number() == 1001); static_assert((1 * km + quantity_point{1 * m}).relative().number() == 1001); static_assert((quantity_point{2 * m} - 1 * m).relative().number() == 1); static_assert((quantity_point{1 * km} - 1 * m).relative().number() == 999); static_assert((quantity_point{1.5 * m} + 1 * m).relative().number() == 2.5); static_assert((1.5 * m + quantity_point{1 * m}).relative().number() == 2.5); static_assert((quantity_point{1.5 * m} + 1 * km).relative().number() == 1001.5); static_assert((1.5 * m + quantity_point{1 * km}).relative().number() == 1001.5); static_assert((quantity_point{1.5 * km} + 1 * m).relative().number() == 1501); static_assert((1.5 * km + quantity_point{1 * m}).relative().number() == 1501); static_assert((quantity_point{2.5 * m} - 1 * m).relative().number() == 1.5); static_assert((quantity_point{1.5 * km} - 1 * m).relative().number() == 1499); static_assert((quantity_point{1 * m} + 1.5 * m).relative().number() == 2.5); static_assert((1 * m + quantity_point{1.5 * m}).relative().number() == 2.5); static_assert((quantity_point{1 * m} + 1.5 * km).relative().number() == 1501); static_assert((1 * m + quantity_point{1.5 * km}).relative().number() == 1501); static_assert((quantity_point{1 * km} + 1.5 * m).relative().number() == 1001.5); static_assert((1 * km + quantity_point{1.5 * m}).relative().number() == 1001.5); static_assert((quantity_point{2 * m} - 1.5 * m).relative().number() == 0.5); static_assert((quantity_point{1 * km} - 1.5 * m).relative().number() == 998.5); // commutativity and associativity static_assert((quantity_point{10 * isq::length[si::metre] / (2 * isq::time[s])} + 5 * isq::speed[m / s]).relative() == 10 * isq::speed[m / s]); static_assert((10 * isq::length[si::metre] / (2 * isq::time[s]) + quantity_point{5 * isq::speed[m / s]}).relative() == 10 * isq::speed[m / s]); static_assert((quantity_point{5 * isq::speed[m / s]} + 10 * isq::length[m] / (2 * isq::time[s])).relative() == 10 * isq::speed[m / s]); static_assert((5 * isq::speed[m / s] + quantity_point{10 * isq::length[m] / (2 * isq::time[s])}).relative() == 10 * isq::speed[m / s]); static_assert((quantity_point{10 * isq::length[m] / (2 * isq::time[s])} - 5 * isq::speed[m / s]).relative() == 0 * isq::speed[m / s]); static_assert((quantity_point{5 * isq::speed[m / s]} - 10 * isq::length[m] / (2 * isq::time[s])).relative() == 0 * isq::speed[m / s]); // NOTE: quantity_spec of the origin is not "upgraded" to a better type static_assert(is_of_type{}, int>>); static_assert(is_of_type<10 * isq::length[m] / (2 * isq::time[s]) + quantity_point{5 * isq::speed[m / s]}, quantity_point{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type<5 * isq::speed[m / s] + quantity_point{10 * isq::length[m] / (2 * isq::time[s])}, quantity_point{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); // NOTE: 1 / isq::time[s] works for quantities but not for quantity_point (origin can't be weakened) static_assert((quantity_point{10 / (2 * isq::period_duration[s])} + 5 * isq::frequency[Hz]).relative() == 10 * isq::frequency[Hz]); static_assert((10 / (2 * isq::period_duration[s]) + quantity_point{5 * isq::frequency[Hz]}).relative() == 10 * isq::frequency[Hz]); static_assert((quantity_point{5 * isq::frequency[Hz]} + 10 / (2 * isq::period_duration[s])).relative() == 10 * isq::frequency[Hz]); static_assert((5 * isq::frequency[Hz] + quantity_point{10 / (2 * isq::period_duration[s])}).relative() == 10 * isq::frequency[Hz]); static_assert((quantity_point{10 / (2 * isq::period_duration[s])} - 5 * isq::frequency[Hz]).relative() == 0 * isq::frequency[Hz]); static_assert((quantity_point{5 * isq::frequency[Hz]} - 10 / (2 * isq::period_duration[s])).relative() == 0 * isq::frequency[Hz]); // NOTE: quantity_spec of the origin is not "upgraded" to a better type static_assert(is_of_type{}, int>>); static_assert(is_of_type<10 / (2 * isq::period_duration[s]) + quantity_point{5 * isq::frequency[Hz]}, quantity_point{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type<5 * isq::frequency[Hz] + quantity_point{10 / (2 * isq::period_duration[s])}, quantity_point{}, int>>); static_assert(is_of_type{}, int>>); static_assert(is_of_type{}, int>>); // Different named dimensions template consteval bool invalid_addition(Ts... ts) { return !requires { (... + ts); }; } template consteval bool invalid_subtraction(Ts... ts) { return !requires { (... - ts); }; } static_assert(invalid_addition(quantity_point{5 * activity[Bq]}, 5 * isq::frequency[Hz])); static_assert(invalid_addition(5 * activity[Bq], quantity_point{5 * isq::frequency[Hz]})); static_assert(invalid_subtraction(quantity_point{5 * activity[Bq]}, 5 * isq::frequency[Hz])); static_assert(invalid_addition(quantity_point{5 * activity[Bq]}, 10 / (2 * isq::time[s]), 5 * isq::frequency[Hz])); static_assert(invalid_addition(5 * activity[Bq], quantity_point{10 / (2 * isq::time[s])}, 5 * isq::frequency[Hz])); static_assert(invalid_addition(5 * activity[Bq], 10 / (2 * isq::time[s]), quantity_point{5 * isq::frequency[Hz]})); static_assert(invalid_subtraction(quantity_point{5 * activity[Bq]}, 10 / (2 * isq::time[s]), 5 * isq::frequency[Hz])); } // namespace