// 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 "units/chrono.h" #include "units/isq/si/cgs/speed.h" #include "units/isq/si/area.h" #include "units/isq/si/frequency.h" #include "units/isq/si/speed.h" #include "units/isq/si/fps/speed.h" #include "units/quantity_point.h" #include "units/quantity_point_kind.h" #include #include #include namespace { using namespace units; namespace si = isq::si; using namespace si; using namespace references; using sys_seconds = std::chrono::time_point; constexpr auto cgs_cm = cgs::references::cm; using namespace std::chrono_literals; struct width_kind : kind {}; struct height_kind : kind {}; struct abscissa_kind : point_kind {}; struct ordinate_kind : point_kind {}; struct distance_kind : kind {}; struct cgs_width_kind : kind {}; struct cgs_height_kind : kind {}; struct rate_of_climb_kind : derived_kind {}; struct altitude_kind : point_kind {}; struct apple : kind {}; struct orange : kind {}; struct nth_apple_kind : point_kind {}; struct nth_orange_kind : point_kind {}; struct time_kind : kind {}; struct time_point_kind : point_kind {}; template using width = quantity_kind; template using height = quantity_kind; template using abscissa = quantity_point_kind; template using ordinate = quantity_point_kind; template using distance = quantity_kind; template using cgs_width = quantity_kind; template using cgs_height = quantity_kind; template using rate_of_climb = quantity_kind; template using altitude = quantity_point_kind; template using apples = quantity_kind; template using oranges = quantity_kind; template using nth_apple = quantity_point_kind; template using nth_orange = quantity_point_kind; ///////////// // concepts ///////////// static_assert(QuantityPointKind>); static_assert(QuantityPointKind>); static_assert(!QuantityPointKind); static_assert(!QuantityPointKind>); static_assert(!QuantityPointKind>); static_assert(!QuantityPointKind>); static_assert(QuantityPointKindOf, abscissa_kind>); static_assert(!QuantityPointKindOf, ordinate_kind>); static_assert(!QuantityPointKindOf, metre>); static_assert(!QuantityPointKindOf, abscissa_kind>); static_assert(!QuantityPointKindOf, metre>); static_assert(!QuantityPointKindOf, abscissa_kind>); static_assert(!QuantityPointKindOf, width_kind>); static_assert(!QuantityPointKindOf, metre>); static_assert(!QuantityPointKindOf, width_kind>); static_assert(!QuantityPointKindOf, dim_length>); static_assert(!QuantityPointKindOf, metre>); /////////////// // invariants /////////////// static_assert(sizeof(abscissa) == sizeof(double)); static_assert(sizeof(ordinate) == sizeof(short)); template concept invalid_types = requires { requires !requires { typename quantity_point_kind; }; // width_kind is not a point kind requires !requires { typename quantity_point_kind; }; // unit of a different dimension requires !requires { typename quantity_point_kind>; }; // quantity used as Rep requires !requires { typename quantity_point_kind>; }; // quantity point used as Rep requires !requires { typename quantity_point_kind>; }; // quantity kind used as Rep requires !requires { typename quantity_point_kind>; }; // quantity point kind used as Rep requires !requires { typename quantity_point_kind; }; // reordered arguments requires !requires { typename quantity_point_kind; }; // reordered arguments }; static_assert(invalid_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>); static_assert(!std::is_aggregate_v>); /////////////////// // member aliases /////////////////// static_assert(is_same_v::point_kind_type, abscissa_kind>); static_assert(is_same_v::kind_type, width_kind>); static_assert(is_same_v::quantity_kind_type, width>); static_assert(is_same_v::quantity_type, length>); static_assert(is_same_v::dimension, dim_length>); static_assert(is_same_v::unit, metre>); static_assert(is_same_v::rep, double>); ////////////////////// // relative observer ////////////////////// static_assert(same(abscissa{}.relative(), width{})); //////////////////////////// // static member functions //////////////////////////// static_assert(abscissa::min().relative().common() == 0 * m); static_assert(abscissa::max().relative().common() == std::numeric_limits::max() * m); static_assert(abscissa::min().relative().common().number() == std::numeric_limits::lowest()); static_assert(abscissa::max().relative().common().number() == std::numeric_limits::max()); //////////////////////// // default constructor //////////////////////// // default initialization #if !defined(UNITS_COMP_MSVC) static_assert([] { const auto read_uninitialized_quantity = [] { abscissa w; ++w; }; return !require_constant_invocation; }()); #endif // value initialization static_assert(abscissa{}.relative().common() == 0 * m); ///////// // CTAD ///////// static_assert(same(quantity_point_kind(width(0 * m)), abscissa{})); static_assert(same(quantity_point_kind(abscissa(0 * m)), abscissa{})); //////////////////////////// // construction from a rep //////////////////////////// static_assert(construct_from_only>(1.0).relative().common() == 1); static_assert(construct_from_only>(1.0f).relative().common() == 1); static_assert(construct_from_only>(1).relative().common() == 1); static_assert(construct_from_only>(short{1}).relative().common() == 1); static_assert(construct_from_only>(1).relative().common() == 1); static_assert(construct_from_only>(1).relative().common() == 1); static_assert(construct_from_only>(1ULL).relative().common().number() == 1); static_assert(construct_from_only>(1).relative().common().number() == 1); static_assert(!constructible_or_convertible_from>(1.0)); static_assert(!constructible_or_convertible_from>(1.0)); static_assert(!constructible_or_convertible_from>(1.0)); static_assert(!constructible_or_convertible_from>(1.0f)); static_assert(!constructible_or_convertible_from>(1)); static_assert(!constructible_or_convertible_from>(short{1})); static_assert(!constructible_or_convertible_from>(1)); ///////////////////////////////// // construction from a quantity ///////////////////////////////// // clang-format off static_assert(construct_from_only>(1 * m).relative().common() == 1 * m); static_assert(construct_from_only>(1 * m).relative().common() == 1 * m); static_assert(construct_from_only>(1 * km).relative().common() == 1 * km); static_assert(construct_from_only>(1ULL * m).relative().common() == 1 * m); // static_assert(construct_from_only>(1 * cgs_cm).relative().common() == 1 * cm); // TODO: Fix #210 static_assert(construct_from_only>(1 * m).relative().common() == 1 * m); static_assert(construct_from_only>(1.0 * km).relative().common() == 1 * km); static_assert(construct_from_only>(1 * cgs_cm).relative().common() == 1 * cm); static_assert(construct_from_only>(1.0 * cgs_cm).relative().common() == 1 * cm); static_assert(!constructible_or_convertible_from>(1 * mm)); static_assert(!constructible_or_convertible_from>(1.0 * m)); static_assert(!constructible_or_convertible_from>(1.0 * km)); static_assert(!constructible_or_convertible_from>(1 * cgs_cm)); static_assert(!constructible_or_convertible_from>(quantity(1))); static_assert(!constructible_or_convertible_from>(1 * s)); static_assert(!constructible_or_convertible_from>(1s)); static_assert(construct_from_only>(quantity(1)).relative().common() == 1); static_assert(construct_from_only>(dimensionless(1)).relative().common() == 0.01); static_assert(construct_from_only>(dimensionless(1)).relative().common() == 0.01); static_assert(construct_from_only>(dimensionless(1)).relative().common().number() == 1); static_assert(construct_from_only>(quantity(1)).relative().common().number() == 100); static_assert(!constructible_or_convertible_from>(quantity(1.0))); static_assert(!constructible_or_convertible_from>(dimensionless(1))); static_assert(!constructible_or_convertible_from>(quantity(1.0))); static_assert(!constructible_or_convertible_from>(dimensionless(1))); static_assert(!constructible_or_convertible_from>(1 * m)); static_assert(!constructible_or_convertible_from>(1 * s)); static_assert(!constructible_or_convertible_from>(1s)); // clang-format on /////////////////////////////////////// // construction from a quantity point /////////////////////////////////////// static_assert(construct_from_only>(1 * m).relative().common() == 1 * m); static_assert(construct_from_only>(quantity_point(short{1} * m)).relative().common() == 1 * m); static_assert(construct_from_only>(quantity_point(1 * m)).relative().common() == 1 * m); static_assert(construct_from_only>(quantity_point(1 * km)).relative().common() == 1 * km); static_assert(construct_from_only>(quantity_point(1 * m)).relative().common() == 1 * m); static_assert(construct_from_only>(quantity_point(1 * km)).relative().common() == 1 * km); static_assert(construct_from_only>(quantity_point(1.0 * m)).relative().common() == 1 * m); static_assert(construct_from_only>(quantity_point(1.0 * mm)).relative().common() == 1 * mm); static_assert(!constructible_or_convertible_from>(quantity_point(1 * mm))); static_assert(!constructible_or_convertible_from>(quantity_point(1.0 * m))); static_assert(!constructible_or_convertible_from>(quantity_point(1.0 * km))); static_assert(!constructible_or_convertible_from>(quantity_point(1.0 * (m * m)))); static_assert(!constructible_or_convertible_from>(quantity_point(1.0 * s))); // clang-format off static_assert(construct_from_only>(quantity_point(1)).relative().common() == 1); static_assert(construct_from_only>(quantity_point(1)).relative().common() == 1); static_assert(construct_from_only>(quantity_point(1)).relative().common() == 1); static_assert(construct_from_only>(quantity_point(dimensionless(1))).relative().common() == 0.01); static_assert(construct_from_only>(quantity_point(1.0)).relative().common() == 1); static_assert(construct_from_only>(quantity_point(dimensionless(1.0))).relative().common() == 0.01); static_assert(construct_from_only>(quantity_point(1)).relative().common().number() == 100); static_assert(construct_from_only>(quantity_point(dimensionless(1))).relative().common().number() == 1); static_assert(!constructible_or_convertible_from>(quantity_point(1.0))); static_assert(!constructible_or_convertible_from>(quantity_point(dimensionless(1)))); static_assert(!constructible_or_convertible_from>(quantity_point(1.0))); static_assert(!constructible_or_convertible_from>(quantity_point(dimensionless(1)))); static_assert(!constructible_or_convertible_from>(quantity_point(dimensionless(1)))); static_assert(!constructible_or_convertible_from>(quantity_point(1.0 * s))); static_assert(construct_from_only>(sys_seconds{42s}).relative().common() == 42 * s); // clang-format on ////////////////////////////////////// // construction from a quantity kind ////////////////////////////////////// // clang-format off static_assert(construct_from_only>(width(1 * m)).relative().common() == 1 * m); static_assert(construct_from_only>(width(1ULL * km)).relative().common() == 1 * km); static_assert(construct_from_only>(width(1 * cgs_cm)).relative().common() == 1 * cm); static_assert(construct_from_only>(width(1 * cgs_cm)).relative().common() == 1 * cm); static_assert(construct_from_only>(width(1 * m)).relative().common() == 1 * m); static_assert(construct_from_only>(width(1.0 * mm)).relative().common() == 1 * mm); static_assert(construct_from_only>(width(1ULL * km)).relative().common() == 1 * km); static_assert(!constructible_or_convertible_from>(width(1.0 * m))); static_assert(!constructible_or_convertible_from>(width(1 * mm))); static_assert(!constructible_or_convertible_from>(height(1 * m))); static_assert(!constructible_or_convertible_from>(abscissa_kind{}, width(1.0 * m))); static_assert(!constructible_or_convertible_from>(abscissa_kind{}, width(1 * mm))); static_assert(!constructible_or_convertible_from>(abscissa_kind{}, height(1 * m))); static_assert(construct_from_only>(apples(1)).relative().common() == 1); static_assert(construct_from_only>(apples(dimensionless(1))).relative().common() == 0.01); static_assert(construct_from_only>(apples(1)).relative().common().number() == 100); static_assert(construct_from_only>(apples(dimensionless(1))).relative().common().number() == 1); static_assert(!constructible_or_convertible_from>(apples(1.0))); static_assert(!constructible_or_convertible_from>(apples(dimensionless(1)))); static_assert(!constructible_or_convertible_from>(apples(1.0))); static_assert(!constructible_or_convertible_from>(apples(dimensionless(1)))); static_assert(!constructible_or_convertible_from>(oranges(1))); // clang-format on ////////////////////////////////////////////////// // construction from another quantity point kind ////////////////////////////////////////////////// // clang-format off static_assert(construct_and_convert_from>(abscissa(1 * m)).relative().common() == 1 * m); static_assert(construct_and_convert_from>(abscissa(1ULL * km)).relative().common() == 1 * km); static_assert(construct_and_convert_from>(abscissa(1ULL * m)).relative().common() == 1 * m); static_assert(construct_and_convert_from>(abscissa(1 * cgs_cm)).relative().common() == 1 * cm); static_assert(construct_and_convert_from>(abscissa(1 * cgs_cm)).relative().common() == 1 * cm); static_assert(!constructible_or_convertible_from>(abscissa(1.0 * m))); static_assert(!constructible_or_convertible_from>(abscissa(1 * m))); static_assert(!constructible_or_convertible_from>(ordinate(1 * m))); static_assert(!constructible_or_convertible_from>(quantity_point_kind(1 * s))); static_assert(construct_and_convert_from>(nth_apple(1)).relative().common() == 1); static_assert(construct_and_convert_from>(nth_apple(dimensionless(1))).relative().common() == 0.01); static_assert(construct_and_convert_from>(nth_apple(1)).relative().common().number() == 100); static_assert(construct_and_convert_from>(nth_apple(dimensionless(1))).relative().common().number() == 1); static_assert(!constructible_or_convertible_from>(nth_apple(1.0))); static_assert(!constructible_or_convertible_from>(nth_apple(dimensionless(1)))); static_assert(!constructible_or_convertible_from>(nth_apple(1.0))); static_assert(!constructible_or_convertible_from>(nth_apple(dimensionless(1)))); static_assert(!constructible_or_convertible_from>(nth_orange(1))); static_assert(!constructible_or_convertible_from>(abscissa(1 * m))); // clang-format on ////////////////////// // other conversions ////////////////////// static_assert(!std::is_convertible_v, int>); static_assert(!std::is_convertible_v, dimensionless>); static_assert(!std::is_convertible_v, length>); static_assert(!std::is_convertible_v, width>); static_assert(!std::is_convertible_v, height>); static_assert(!std::is_convertible_v, quantity_point>); //////////////////////// // assignment operator //////////////////////// // clang-format off static_assert((abscissa(2 * m) = abscissa(1 * m)).relative().common() == 1 * m); static_assert((abscissa(2 * m) = abscissa(1 * km)).relative().common() == 1 * km); static_assert(!std::is_assignable_v, abscissa>); static_assert(!std::is_assignable_v, abscissa>); // clang-format on ///////////////////// // member operators ///////////////////// #if !defined(UNITS_COMP_MSVC) || defined(NDEBUG) static_assert([]() { const width w(1 * m); [[maybe_unused]] quantity_point_kind x(w); assert(&++x == &x && x.relative().common() == 2 * m); assert(&--x == &x && x.relative().common() == 1 * m); assert((x++).relative().common() == 1 * m && x.relative().common() == 2 * m); assert((x--).relative().common() == 2 * m && x.relative().common() == 1 * m); assert(&(x += w) == &x && x.relative().common() == 2 * m); assert(&(x -= w) == &x && x.relative().common() == 1 * m); return true; }()); #endif template concept invalid_compound_assignments_ = requires(quantity_point_kind x, Qx q) { requires !requires { x += q; }; requires !requires { x -= q; }; }; template concept invalid_compound_assignments = requires(quantity_point_kind x) { requires !requires { x += 1; }; requires !requires { x -= 1; }; requires invalid_compound_assignments_>; requires invalid_compound_assignments_>; requires invalid_compound_assignments_>; requires invalid_compound_assignments_>; requires invalid_compound_assignments_; }; static_assert(invalid_compound_assignments); static_assert(invalid_compound_assignments_); #if __cpp_lib_chrono >= 201907L static_assert(invalid_compound_assignments_); #endif ///////////////////////// // non-member operators ///////////////////////// // clang-format off static_assert(same(abscissa(2 * m) + width(3 * m), abscissa(5 * m))); static_assert(same(abscissa(2 * m) + width(3. * m), abscissa(5. * m))); static_assert(same(abscissa(2. * m) + width(3 * m), abscissa(5. * m))); static_assert(same(abscissa(2 * km) + width(3e3 * m), abscissa(5e3 * m))); static_assert(same(abscissa(2e3 * m) + width(3 * km), abscissa(5e3 * m))); static_assert(same(width(2 * m) + abscissa(3 * m), abscissa(5 * m))); static_assert(same(width(2 * m) + abscissa(3. * m), abscissa(5. * m))); static_assert(same(width(2. * m) + abscissa(3 * m), abscissa(5. * m))); static_assert(same(width(2 * km) + abscissa(3e3 * m), abscissa(5e3 * m))); static_assert(same(width(2e3 * m) + abscissa(3 * km), abscissa(5e3 * m))); static_assert(!std::is_invocable_v, abscissa, double>); static_assert(!std::is_invocable_v, abscissa, length>); static_assert(!std::is_invocable_v, abscissa, quantity_point>); static_assert(!std::is_invocable_v, abscissa, height>); static_assert(!std::is_invocable_v, abscissa, abscissa>); static_assert(!std::is_invocable_v, abscissa, abscissa>); static_assert(!std::is_invocable_v, abscissa, abscissa>); static_assert(!std::is_invocable_v, height, abscissa>); static_assert(!std::is_invocable_v, quantity_point, abscissa>); static_assert(!std::is_invocable_v, length, abscissa>); static_assert(!std::is_invocable_v, double, abscissa>); static_assert(same(abscissa(2 * m) - width(3 * m), abscissa(-1 * m))); static_assert(same(abscissa(2 * m) - width(3. * m), abscissa(-1. * m))); static_assert(same(abscissa(2. * m) - width(3 * m), abscissa(-1. * m))); static_assert(same(abscissa(2 * km) - width(3e3 * m), abscissa(-1e3 * m))); static_assert(same(abscissa(2e3 * m) - width(3 * km), abscissa(-1e3 * m))); static_assert(same(abscissa(2 * m) - abscissa(3 * m), width(-1 * m))); static_assert(same(abscissa(2 * m) - abscissa(3. * m), width(-1. * m))); static_assert(same(abscissa(2. * m) - abscissa(3 * m), width(-1. * m))); static_assert(same(abscissa(2 * km) - abscissa(3e3 * m), width(-1e3 * m))); static_assert(same(abscissa(2e3 * m) - abscissa(3 * km), width(-1e3 * m))); static_assert(!std::is_invocable_v, abscissa, double>); static_assert(!std::is_invocable_v, abscissa, length>); static_assert(!std::is_invocable_v, abscissa, quantity_point>); static_assert(!std::is_invocable_v, abscissa, height>); static_assert(!std::is_invocable_v, abscissa, ordinate>); static_assert(!std::is_invocable_v, ordinate, abscissa>); static_assert(!std::is_invocable_v, height, abscissa>); static_assert(!std::is_invocable_v, quantity_point, abscissa>); static_assert(!std::is_invocable_v, length, abscissa>); static_assert(!std::is_invocable_v, double, abscissa>); // clang-format on ///////////////////////// // comparison operators ///////////////////////// // clang-format off static_assert(abscissa(1 * m) == abscissa(1 * m)); static_assert(abscissa(1 * m) == abscissa(1.0 * m)); static_assert(abscissa(1 * m) == abscissa(1000 * mm)); static_assert(abscissa(1 * m) == abscissa(1e3 * mm)); static_assert(abscissa(2 * m) != abscissa(1 * m)); static_assert(abscissa(2 * m) != abscissa(1.0 * cgs_cm)); static_assert(std::equality_comparable_with, abscissa>); static_assert(std::equality_comparable_with, abscissa>); static_assert(std::equality_comparable_with, abscissa>); static_assert(std::equality_comparable_with, abscissa>); // clang-format on template concept invalid_equality = requires(quantity_point_kind x) { requires !requires { x == 1; }; requires !requires { x != 1.0; }; requires !requires { x == 1 * m; }; requires !requires { x != 1.0 * cgs_cm; }; requires !requires { x == 1 * km; }; requires !requires { x != quantity(1); }; requires !requires { x == dimensionless(1.0); }; requires !requires { x != width(1 * m); }; requires !requires { x == width(1.0 * km); }; requires !requires { x != height(1 * m); }; requires !requires { x == height(1.0 * km); }; requires !requires { x == rate_of_climb(1.0 * (km / h)); }; requires !requires { x != quantity_point(1 * m); }; requires !requires { x == quantity_point(1.0 * mm); }; requires !requires { x != quantity_point(quantity(1)); }; requires !requires { x == quantity_point(dimensionless(1.0)); }; requires !requires { x != quantity_point_kind(cgs_width(1 * m)); }; requires !requires { x == ordinate(1 * m); }; }; static_assert(invalid_equality); // clang-format off static_assert(abscissa(1 * m) < abscissa(2 * m)); static_assert(abscissa(1 * m) <= abscissa(2.0 * m)); static_assert(abscissa(1 * m) <= abscissa(1 * km)); static_assert(abscissa(1 * m) >= abscissa(1e3 * mm)); static_assert(abscissa(2 * m) >= abscissa(1 * mm)); static_assert(abscissa(2 * m) > abscissa(1 * cgs_cm)); static_assert(std::three_way_comparable_with, abscissa>); static_assert(std::three_way_comparable_with, abscissa>); static_assert(std::three_way_comparable_with, abscissa>); static_assert(std::three_way_comparable_with, abscissa>); // clang-format on template concept invalid_relational = requires(quantity_point_kind x) { requires !requires { x < 1; }; requires !requires { x <= 1.0; }; requires !requires { x >= 1 * m; }; requires !requires { x > 1.0 * cgs_cm; }; requires !requires { x <=> 1 * km; }; requires !requires { x < quantity(1); }; requires !requires { x <= dimensionless(1.0); }; requires !requires { x >= width(1 * m); }; requires !requires { x > width(1.0 * km); }; requires !requires { x <=> height(1 * m); }; requires !requires { x < height(1.0 * km); }; requires !requires { x <= rate_of_climb(1.0 * (km / h)); }; requires !requires { x >= quantity_point(1 * m); }; requires !requires { x > quantity_point(1.0 * mm); }; requires !requires { x <=> quantity_point(quantity(1)); }; requires !requires { x < quantity_point(dimensionless(1.0)); }; requires !requires { x <= quantity_point_kind(cgs_width(1 * m)); }; requires !requires { x >= ordinate(1 * m); }; }; static_assert(invalid_relational); ///////////////////////////// // quantity_point_kind_cast ///////////////////////////// // clang-format off static_assert(same(quantity_point_kind_cast>(abscissa(1 * m)), abscissa(1 * m))); static_assert(same(quantity_point_kind_cast>(abscissa(1 * m)), abscissa(1.0 * m))); static_assert(same(quantity_point_kind_cast>(abscissa(999 * m)), abscissa(0 * km))); static_assert(same(quantity_point_kind_cast>(abscissa(1000 * m)), abscissa(1 * km))); static_assert(same(quantity_point_kind_cast>(abscissa(999 * m)), abscissa(0.999 * km))); static_assert(same(quantity_point_kind_cast>(abscissa(1 * m)), abscissa(1 * m))); static_assert(same(quantity_point_kind_cast>(abscissa(1 * m)), abscissa(1.0 * m))); static_assert(same(quantity_point_kind_cast>(abscissa(999 * m)), abscissa(0 * km))); static_assert(same(quantity_point_kind_cast>(abscissa(1000 * m)), abscissa(1 * km))); static_assert(same(quantity_point_kind_cast>(abscissa(999 * m)), abscissa(0.999 * km))); static_assert(same(quantity_point_kind_cast(abscissa(1 * m)), abscissa(1.0 * m))); static_assert(same(quantity_point_kind_cast(abscissa(1 * m)), abscissa(1 * m))); static_assert(same(quantity_point_kind_cast(abscissa(999 * m)), abscissa(0 * km))); static_assert(same(quantity_point_kind_cast(abscissa(1000 * m)), abscissa(1 * km))); static_assert(same(quantity_point_kind_cast>(abscissa(1 * m)), ordinate(1 * m))); static_assert(same(quantity_point_kind_cast>(abscissa(1 * m)), ordinate(1.0 * m))); static_assert(same(quantity_point_kind_cast>(abscissa(999 * m)), ordinate(0 * km))); static_assert(same(quantity_point_kind_cast>(abscissa(1000 * m)), ordinate(1 * km))); static_assert(same(quantity_point_kind_cast>(abscissa(999 * m)), ordinate(0.999 * km))); static_assert(same(quantity_point_kind_cast>(abscissa(1 * m)), ordinate(1 * m))); static_assert(same(quantity_point_kind_cast>(abscissa(1 * m)), ordinate(1.0 * m))); static_assert(same(quantity_point_kind_cast>(abscissa(999 * m)), ordinate(0 * km))); static_assert(same(quantity_point_kind_cast>(abscissa(1000 * m)), ordinate(1 * km))); static_assert(same(quantity_point_kind_cast>(abscissa(999 * m)), ordinate(0.999 * km))); static_assert(same(quantity_point_kind_cast(abscissa(1 * m)), ordinate(1 * m))); static_assert(same(quantity_point_kind_cast(abscissa(1 * m)), ordinate(1 * m))); static_assert(same(quantity_point_kind_cast(abscissa(999 * m)), ordinate(0 * km))); static_assert(same(quantity_point_kind_cast(abscissa(1000 * m)), ordinate(1 * km))); static_assert(same(quantity_point_kind_cast>(abscissa(1 * cm)), quantity_point_kind(cgs_width(1 * cgs_cm)))); static_assert(same(quantity_point_kind_cast(abscissa(1 * cm)), quantity_point_kind(cgs_width(1 * cgs_cm)))); static_assert(same(quantity_point_kind_cast(abscissa(1 * cm)), altitude(1 * cgs_cm))); static_assert(same(quantity_point_kind_cast(abscissa(1 * cm)), altitude(1 * cgs_cm))); static_assert(same(quantity_point_kind_cast(abscissa(1 * m)), quantity_point_kind(cgs_width(1 * m)))); static_assert(same(quantity_point_kind_cast(abscissa(1 * m)), altitude(1 * m))); static_assert(same(quantity_point_kind_cast(abscissa(1 * m)), altitude(1 * m))); static_assert(same(quantity_point_kind_cast(abscissa(1 * cm)), abscissa(1 * cgs_cm))); static_assert(same(quantity_point_kind_cast>(abscissa(1 * m)), abscissa(0 * km))); static_assert(same(quantity_point_kind_cast>(abscissa(1 * m)), abscissa(100 * cm))); static_assert(same(quantity_point_kind_cast>(abscissa(0.01 * m)), abscissa(1 * cm))); static_assert(same(quantity_point_kind_cast>(abscissa(1 * cgs_cm)), abscissa(1 * cgs_cm))); // clang-format on template concept invalid_cast = requires(Int i) { requires !requires { quantity_point_kind_cast>(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast>(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast>(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast>(abscissa(i * m)); }; requires !requires { quantity_point_kind_cast>(abscissa(i * m)); }; }; static_assert(invalid_cast); ///////////////////////// // extensible interface ///////////////////////// namespace mylib { struct width_kind : kind {}; struct height_kind : kind {}; struct abscissa_kind : point_kind {}; struct ordinate_kind : point_kind {}; struct point {}; template Abscissa, units::QuantityPointKindOf Ordinate> point operator+(Abscissa, Ordinate); } // namespace mylib namespace yourapp { static_assert(is_same_v(1 * m)) + quantity_point_kind(quantity_kind(1 * m)))>); } // namespace yourapp } // namespace