// 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_kind.h" #include #include #include namespace { using namespace units; namespace si = isq::si; using namespace si; using namespace references; constexpr auto cgs_cm = cgs::references::cm; using namespace std::chrono_literals; struct radius_kind : kind {}; struct width_kind : kind {}; struct height_kind : kind {}; struct horizontal_area_kind : derived_kind {}; struct rate_of_climb_kind : derived_kind {}; struct apple : kind {}; struct orange : kind {}; struct time_kind : kind {}; struct cgs_width_kind : kind {}; template using radius = quantity_kind; template using width = quantity_kind; template using height = quantity_kind; template using horizontal_area = quantity_kind; template using rate_of_climb = quantity_kind; template using apples = quantity_kind; template using oranges = quantity_kind; template using cgs_width = quantity_kind; ///////////// // concepts ///////////// static_assert(QuantityKind>); static_assert(QuantityKind>); static_assert(!QuantityKind); static_assert(!QuantityKind>); static_assert(!QuantityKind>); static_assert(QuantityKindOf, width_kind>); static_assert(!QuantityKindOf, height_kind>); static_assert(!QuantityKindOf, metre>); static_assert(!QuantityKindOf, width_kind>); static_assert(!QuantityKindOf, metre>); static_assert(!QuantityKindOf, width_kind>); static_assert(!QuantityKindOf, dim_length>); static_assert(!QuantityKindOf, metre>); /////////////// // invariants /////////////// static_assert(sizeof(width) == sizeof(double)); static_assert(sizeof(height) == sizeof(short)); template concept invalid_types = requires { requires !requires { typename quantity_kind; }; // unit of a different dimension requires !requires { typename quantity_kind>; }; // quantity used as Rep requires !requires { typename quantity_kind>; }; // quantity point used as Rep requires !requires { typename quantity_kind>; }; // quantity kind used as Rep requires !requires { typename quantity_kind; }; // reordered arguments requires !requires { typename quantity_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::kind_type, width_kind>); 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>); //////////////////// // common observer //////////////////// static_assert(same(radius{}.common(), length{})); static_assert(radius{}.common() == // [VIM3] 1.2 kind of quantity height{}.common()); // aspect common to mutually comparable quantities // hence `.common()` static_assert(!std::equality_comparable_with, oranges<>>); //////////////////////////// // static member functions //////////////////////////// static_assert(width::zero().common() == 0 * m); static_assert(width::one().common() == 1 * m); static_assert(width::min().common() == 0 * m); static_assert(width::max().common() == std::numeric_limits::max() * m); static_assert(width::min().common().number() == std::numeric_limits::lowest()); static_assert(width::max().common().number() == std::numeric_limits::max()); //////////////////////// // default constructor //////////////////////// // default initialization #if !defined(UNITS_COMP_MSVC) static_assert([] { const auto read_uninitialized_quantity = [] { width w; ++w; }; return !require_constant_invocation; }()); #endif // value initialization static_assert(width{}.common() == 0 * m); ///////// // CTAD ///////// static_assert(same(quantity_kind(rate_of_climb(0.01 * (km / h))), rate_of_climb(0.01 * (km / h)))); //////////////////////////// // construction from a rep //////////////////////////// static_assert(construct_from_only>(1).common() == 1); static_assert(construct_from_only>(1.0).common() == 1); static_assert(construct_from_only>(1ULL).common().number() == 1); static_assert(construct_from_only>(1.0L).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.0)); static_assert(!constructible_or_convertible_from>(1)); ///////////////////////////////// // construction from a quantity ///////////////////////////////// static_assert(construct_from_only>(1 * m).common() == 1 * m); static_assert(construct_from_only>(1 * km).common() == 1 * km); // static_assert(construct_from_only>(1 * cgs_cm).common() == 1 * cm); // TODO: Fix #210 static_assert(construct_from_only>(1 * cgs_cm).common() == 1 * cm); static_assert(construct_from_only>(1 * mm).common() == 1 * mm); static_assert(construct_from_only>(1 * m).common() == 1 * m); static_assert(construct_from_only>(1 * km).common() == 1 * km); static_assert(construct_from_only>(1.0 * mm).common() == 1 * mm); static_assert(construct_from_only>(1.0 * m).common() == 1 * m); static_assert(construct_from_only>(1.0 * km).common() == 1 * km); static_assert(construct_from_only>(1.0 * mm).common() == 1 * mm); static_assert(construct_from_only>(1.0 * m).common() == 1 * m); static_assert(construct_from_only>(1.0 * km).common() == 1 * km); static_assert(!constructible_or_convertible_from>(1 * mm)); static_assert(!constructible_or_convertible_from>(1.0 * 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 * s)); static_assert(!constructible_or_convertible_from>(1 * (m * m))); static_assert(!constructible_or_convertible_from>(1 * (m / s))); static_assert(construct_from_only>(1.0f * m).common() == 1 * m); static_assert(construct_from_only>(short{1} * m).common() == 1 * m); static_assert(construct_from_only>(1 * m).common() == 1 * m); static_assert(construct_from_only>(quantity(1)).common() == 1); static_assert(construct_from_only>(dimensionless(1)).common() == 0.01); static_assert(construct_from_only>(quantity(1.0)).common().number() == 100); static_assert(construct_from_only>(dimensionless(1)).common().number() == 1); static_assert(construct_from_only>(quantity(1.0)).common() == 1); static_assert(construct_from_only>(quantity(1.0f)).common() == 1); static_assert(construct_from_only>(quantity(1.0)).common() == 1); static_assert(construct_from_only>(quantity(1)).common() == 1); static_assert(construct_from_only>(quantity(short{1})).common() == 1); static_assert(construct_from_only>(quantity(1)).common() == 1); static_assert(construct_from_only>(quantity(1.0)).common().number() == 1e2); static_assert(construct_from_only>(quantity(1.0f)).common().number() == 1e2); static_assert(construct_from_only>(quantity(1.0)).common().number() == 1e2f); static_assert(construct_from_only>(quantity(1)).common().number() == 1e2); static_assert(construct_from_only>(quantity(short{1})).common().number() == 1e2); static_assert(construct_from_only>(quantity(1)).common().number() == 1e2); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(!constructible_or_convertible_from>(apples{})); static_assert(construct_from_only>(42s).common() == 42 * s); static_assert(!constructible_or_convertible_from>(1 * s)); static_assert(!constructible_or_convertible_from>(1 * (m * m))); static_assert(!constructible_or_convertible_from>(1 * (m / s))); static_assert(construct_from_only>(1.0 * cgs_cm).common() == 1 * cm); static_assert(construct_from_only>(1.0 * cm).common() == 1 * cm); //////////////////////////////////////////// // construction from another quantity kind //////////////////////////////////////////// // clang-format off static_assert(construct_and_convert_from>(width(1 * m)).common() == 1 * m); static_assert(construct_and_convert_from>(width(1 * cgs_cm)).common() == 1 * cm); static_assert(construct_and_convert_from>(width(1 * cgs_cm)).common() == 1 * cm); // clang-format on static_assert(construct_and_convert_from>(width(1 * m)).common() == 1 * m); static_assert(!constructible_or_convertible_from>(width(1.0 * m))); static_assert(construct_and_convert_from>(width(1 * km)).common() == 1 * km); static_assert(!constructible_or_convertible_from>(width(1 * m))); static_assert(construct_and_convert_from>(width(1 * km)).common() == 1 * km); static_assert(construct_and_convert_from>(width(1 * m)).common() == 1 * m); static_assert(!constructible_or_convertible_from>(height(1 * m))); static_assert(!constructible_or_convertible_from>(width(1 * m) / (1 * m))); static_assert(!constructible_or_convertible_from>(oranges(1))); ////////////////////////////////// // construction from other types ////////////////////////////////// // clang-format off static_assert(!constructible_or_convertible_from>(quantity_point(1 * m))); static_assert(!constructible_or_convertible_from>(quantity_point(1 * km))); static_assert(!constructible_or_convertible_from>(quantity_point(1 * m))); static_assert(!constructible_or_convertible_from>(quantity_point(1 * km))); 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))); static_assert(!constructible_or_convertible_from>(quantity_point(1.0 * s))); static_assert(!constructible_or_convertible_from>(1s)); static_assert(!constructible_or_convertible_from>(1.0s)); static_assert(!constructible_or_convertible_from>(quantity_point(1))); static_assert(!constructible_or_convertible_from>(quantity_point(dimensionless(1)))); static_assert(!constructible_or_convertible_from>(quantity_point(1))); 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.0)))); static_assert(!constructible_or_convertible_from>(quantity_point(1.0 * m))); static_assert(!constructible_or_convertible_from>(1s)); static_assert(!constructible_or_convertible_from>(1.0s)); // clang-format on //////////////////////// // assignment operator //////////////////////// static_assert((width(2 * m) = width(1 * m)).common() == 1 * m); static_assert((width(2 * m) = width(1 * km)).common() == 1 * km); static_assert(!std::is_assignable_v, width>); static_assert(!std::is_assignable_v, width>); ///////////////////// // member operators ///////////////////// #if !defined(UNITS_COMP_MSVC) || defined(NDEBUG) static_assert([]() { width w(1 * m); assert(+w.common() == 1 * m); assert(-w.common() == -1 * m); assert(&++w == &w && w.common() == 2 * m); assert(&--w == &w && w.common() == 1 * m); assert((w++).common() == 1 * m && w.common() == 2 * m); assert((w--).common() == 2 * m && w.common() == 1 * m); assert(&(w += w) == &w && w.common() == 2 * m); assert(&(w -= w) == &w && w.common() == 0 * m); w = width(3 * m); assert(&(w *= 3) == &w && w.common() == 9 * m); assert(&(w *= quantity(1)) == &w && w.common() == 9 * m); assert(&(w *= (w / w)) == &w && w.common() == 9 * m); assert(&(w /= 2) == &w && w.common() == 4 * m); assert(&(w /= quantity(1)) == &w && w.common() == 4 * m); assert(&(w /= (w / w)) == &w && w.common() == 4 * m); assert(&(w %= 3) == &w && w.common() == 1 * m); assert(&(w %= quantity(3)) == &w && w.common() == 1 * m); assert(&(w %= 3 * (w / w)) == &w && w.common() == 1 * m); assert(&(w %= w) == &w && w.common() == 0 * m); w = width(3 * m); assert(&(w *= 3.9) == &w && w.common() == 11 * m); assert(&(w *= quantity(1.0)) == &w && w.common() == 11 * m); assert(&(w *= 1.0 * (w / w)) == &w && w.common() == 11 * m); assert(&(w /= 3.9) == &w && w.common() == 2 * m); assert(&(w /= quantity(1.0)) == &w && w.common() == 2 * m); assert(&(w /= 1.0 * (w / w)) == &w && w.common() == 2 * m); return true; }()); #endif static_assert((std::uint8_t(255) * m %= 256) == (width(255 * m) %= 256).common()); static_assert((std::uint8_t(255) * m %= quantity(256)) == (width(255 * m) %= quantity(256)).common()); // static_assert((std::uint8_t(255) * m %= 256 * m) == // (width(255 * m) %= // quantity_kind, one, std::uint8_t>(256)).common()); // UB // static_assert((std::uint8_t(255) * m %= 256 * m) != // (width(255 * m) %= width(256 * m)).common()); // UB static_assert((std::uint8_t(255) * m %= 257) == (width(255 * m) %= 257).common()); static_assert((std::uint8_t(255) * m %= quantity(257)) == (width(255 * m) %= quantity(257)).common()); static_assert((std::uint8_t(255) * m %= 257 * m) == (width(255 * m) %= quantity_kind, one, std::uint8_t>(257)).common()); static_assert((std::uint8_t(255) * m %= 257 * m) == (width(255 * m) %= width(257 * m)).common()); static_assert(same((-width(short{1} * m)).common(), int{-1} * m)); template concept invalid_compound_assignments_ = requires(quantity_kind w, Qx q) { requires !requires { w += q; }; requires !requires { w -= q; }; requires !requires { w *= q; }; requires !requires { w /= q; }; requires !requires { w %= q; }; }; template concept invalid_compound_assignments = requires(quantity_kind w, height h) { requires !requires { w += 1; }; requires !requires { w -= 1; }; requires !requires { w *= 1 * (km / m); }; requires !requires { w /= 1 * (km / m); }; requires !requires { w %= 1 * (km / m); }; requires !requires { w += m; }; requires !requires { w -= m; }; requires !requires { w *= m; }; requires !requires { w /= m; }; requires !requires { w %= m; }; requires !requires { w *= quantity_kind, scaled_unit, int>{1}; }; requires !requires { w /= quantity_kind, scaled_unit, int>{1}; }; requires !requires { w %= quantity_kind, scaled_unit, int>{1}; }; requires !requires { w %= 1.0; }; requires !requires { w %= quantity(1.0); }; requires !requires { w %= 1.0 * (w / w); }; requires !requires { w %= 1.0 * w; }; requires !requires { w %= h / h; }; 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_); ///////////////////////// // non-member operators ///////////////////////// static_assert(same(width(2 * m) + width(3 * m), width(5 * m))); static_assert(same(width(2 * m) + width(3. * m), width(5. * m))); static_assert(same(width(2. * m) + width(3 * m), width(5. * m))); static_assert(same(width(2 * km) + width(3e3 * m), width(5e3 * m))); static_assert(same(width(2 * m) - width(3 * m), width(-1 * m))); static_assert(same(width(2 * m) - width(3. * m), width(-1. * m))); static_assert(same(width(2. * m) - width(3 * m), width(-1. * m))); static_assert(same(width(2e3 * m) - width(3 * km), width(-1e3 * m))); static_assert(is_same_v< decltype((width(0 * m) + width(0 * m)).common().number()), int>); static_assert(is_same_v< decltype((width(0 * m) - width(0 * m)).common().number()), int>); static_assert((width(128 * m) + width(128 * m)).common().number() == std::uint8_t(128) + std::uint8_t(128)); static_assert((width(0 * m) - width(1 * m)).common().number() == std::uint8_t(0) - std::uint8_t(1)); static_assert(!std::is_invocable_v, width, double>); static_assert(!std::is_invocable_v, width, length>); static_assert(!std::is_invocable_v, width, quantity_point>); static_assert(!std::is_invocable_v, width, height>); static_assert(!std::is_invocable_v, width, reference>); static_assert(!std::is_invocable_v, width, double>); static_assert(!std::is_invocable_v, width, length>); static_assert(!std::is_invocable_v, width, quantity_point>); static_assert(!std::is_invocable_v, width, height>); static_assert(!std::is_invocable_v, width, reference>); // clang-format off static_assert(!std::is_invocable_v, quantity_kind, one>, quantity_kind, one>>); static_assert(!std::is_invocable_v, quantity_kind, one>, quantity_kind< height_kind, metre>>); static_assert(!std::is_invocable_v, quantity_kind< width_kind, metre>, quantity_kind, one>>); static_assert(!std::is_invocable_v, quantity_kind, day>, quantity_kind< height_kind, metre>>); static_assert(!std::is_invocable_v, quantity_kind< width_kind, metre>, quantity_kind, day>>); static_assert(!std::is_invocable_v, quantity_kind, day>, quantity_kind, day>>); static_assert(!std::is_invocable_v, quantity_kind, one>, quantity_kind, one>>); static_assert(!std::is_invocable_v, quantity_kind, one>, quantity_kind< height_kind, metre>>); static_assert(!std::is_invocable_v, quantity_kind< width_kind, metre>, quantity_kind, one>>); static_assert(!std::is_invocable_v, quantity_kind, day>, quantity_kind< height_kind, metre>>); static_assert(!std::is_invocable_v, quantity_kind< width_kind, metre>, quantity_kind, day>>); static_assert(!std::is_invocable_v, quantity_kind, day>, quantity_kind, day>>); // clang-format on static_assert(same(width(2 * m) * 3, width(6 * m))); static_assert(same(width(2 * m) * 3., width(6. * m))); static_assert(same(width(2. * m) * 3, width(6. * m))); static_assert(same(2 * width(3 * m), width(6 * m))); static_assert(same(2 * width(3. * m), width(6. * m))); static_assert(same(2. * width(3 * m), width(6. * m))); static_assert(same(width(2 * m) * quantity(3), width(6 * m))); static_assert(same(width(2 * m) * quantity(3.), width(6. * m))); static_assert(same(width(2. * m) * quantity(3), width(6. * m))); static_assert(same(quantity(2) * width(3 * m), width(6 * m))); static_assert(same(quantity(2) * width(3. * m), width(6. * m))); static_assert(same(quantity(2.) * width(3 * m), width(6. * m))); static_assert(same(width(2 * m) * quantity_kind, one, int>(3), width(6 * m))); static_assert(same(width(2 * m) * quantity_kind, one, double>(3.), width(6. * m))); static_assert(same(width(2. * m) * quantity_kind, one, int>(3), width(6. * m))); static_assert(same(quantity_kind, one, int>(2) * width(3 * m), width(6 * m))); static_assert(same(quantity_kind, one, int>(2) * width(3. * m), width(6. * m))); static_assert(same(quantity_kind, one, double>(2.) * width(3 * m), width(6. * m))); static_assert(same(height(2 * m) * (3 * Hz), rate_of_climb(6 * (m / s)))); static_assert(same(height(2 * m) * (3. * Hz), rate_of_climb(6. * (m / s)))); static_assert(same(height(2. * m) * (3 * Hz), rate_of_climb(6. * (m / s)))); static_assert(same((2 * Hz) * height(3 * m), rate_of_climb(6 * (m / s)))); static_assert(same((2 * Hz) * height(3. * m), rate_of_climb(6. * (m / s)))); static_assert(same((2. * Hz) * height(3 * m), rate_of_climb(6. * (m / s)))); static_assert(same(quantity_kind(2 * s) * (3 * Hz), quantity_kind, one, int>(6))); static_assert(same((3 * Hz) * quantity_kind(2 * s), quantity_kind, one, int>(6))); static_assert(same(apples(2) * quantity(2), apples(4))); static_assert(same(quantity(2) * apples(2), apples(4))); // clang-format off static_assert(same(width(4 * m) * (1 * m), horizontal_area(4 * (m * m)))); static_assert(same(width(2 * m) * width(2 * m), horizontal_area(4 * (m * m)))); static_assert(same(width(2 * m) * width(2 * m), horizontal_area(4 * (m * m)))); static_assert(same(width(2 * m) * width(2 * m), horizontal_area(4 * (m * m)))); // clang-format on static_assert(same(apples(2) * apples(2), apples(4))); static_assert(same(apples(2) * (2 / apples(1)), apples(4))); static_assert(same(width(4 * m) * (1 * mm), horizontal_area(4 * (m * mm)))); static_assert(same(width(2 * m) * width(2 * m), horizontal_area(4 * (m * m)))); static_assert(same(width(2 * m) * (1 / width(2 * m)), quantity_kind, one>(1))); static_assert(same(width(2 * m) / 3, width(0 * m))); static_assert(same(width(2 * m) / 3., width(2 / 3. * m))); static_assert(same(width(2. * m) / 3, width(2. / 3 * m))); static_assert(same(width(2 * m) / quantity(3), width(0 * m))); static_assert(same(width(2 * m) / quantity(3.), width(2 / 3. * m))); static_assert(same(width(2. * m) / quantity(3), width(2. / 3 * m))); static_assert(same(width(2 * m) / quantity_kind, one, int>(3), width(0 * m))); static_assert(same(width(2 * m) / quantity_kind, one, double>(3.), width(2 / 3. * m))); static_assert(same(width(2. * m) / quantity_kind, one, double>(3), width(2. / 3 * m))); static_assert(same(2 / quantity_kind(3 * s), quantity_kind, hertz, int>(2 / 3 / (1 * s)))); static_assert(same(2 / quantity_kind(3. * s), quantity_kind, hertz, double>(2 / 3. / (1 * s)))); static_assert(same(2. / quantity_kind(3 * s), quantity_kind, hertz, double>(2 / 3. / (1 * s)))); static_assert(same(quantity(2) / quantity_kind(3 * s), quantity_kind, hertz, int>(2 / 3 / (1 * s)))); static_assert(same(quantity(2) / quantity_kind(3. * s), quantity_kind, hertz, double>(2 / 3. / (1 * s)))); static_assert(same(quantity(2.) / quantity_kind(3 * s), quantity_kind, hertz, double>(2 / 3. / (1 * s)))); static_assert( same(quantity_kind, one, int>(2) / quantity_kind(3 * s), quantity_kind, hertz, int>(2 / 3 / (1 * s)))); static_assert( same(quantity_kind, one, int>(2) / quantity_kind(3. * s), quantity_kind, hertz, double>(2 / 3. / (1 * s)))); static_assert( same(quantity_kind, one, double>(2.) / quantity_kind(3 * s), quantity_kind, hertz, double>(2 / 3. / (1 * s)))); static_assert(same(height(2 * m) / (3 * s), rate_of_climb(0 * (m / s)))); static_assert(same(height(2 * m) / (3. * s), rate_of_climb(2 / 3. * (m / s)))); static_assert(same(height(2. * m) / (3 * s), rate_of_climb(2. / 3 * (m / s)))); static_assert(same(width(2 * m) * dimensionless(3), width(6 * cm))); static_assert(same(dimensionless(2) * width(3 * m), width(6 * cm))); static_assert(same(width(2 * m) / dimensionless(3), width(2. / 3 * hm))); static_assert(same(width(2 * m) % dimensionless(3), width(2 * cm))); static_assert(same(height(2 * m) / (3 * m), quantity_kind, one, int>(0))); static_assert(same(height(2 * m) / (3. * m), quantity_kind, one, double>(2 / 3.))); static_assert(same(height(2. * m) / (3 * m), quantity_kind, one, double>(2. / 3))); static_assert(same((2 * m) / height(3 * m), quantity_kind, one, int>(0))); static_assert(same((2 * m) / height(3. * m), quantity_kind, one, double>(2 / 3.))); static_assert(same((2. * m) / height(3 * m), quantity_kind, one, double>(2. / 3))); static_assert(same(width(8 * m) / width(2 * m), quantity_kind, one, int>(4))); static_assert(same(width(8 * m) / width(2 * m), quantity_kind, one, double>(4.0))); static_assert(same(width(8 * m) / width(2 * m), quantity_kind, one, double>(4.0))); static_assert(same(apples(8) / apples(2), apples(4))); static_assert(same(apples(8) / (2 / apples(1)), apples(4))); static_assert(same(horizontal_area(8 * (m * m)) / width(2 * m), width(4 * m))); static_assert(same(horizontal_area(4 * (m * m)) / (1 * m), width(4 * m))); static_assert(same(width(2 * m) % 3, width(2 * m))); static_assert(same(width(3 * m) % width(2 * m), width(1 * m))); static_assert(is_same_v< decltype((width(0 * m) % width(0 * m)).common().number()), decltype(std::uint8_t(0) % std::uint8_t(0))>); static_assert(!std::is_invocable_v, reference, width>); static_assert(!std::is_invocable_v, width, height>); static_assert(!std::is_invocable_v, height, quantity_point>); static_assert(!std::is_invocable_v, quantity_point, height>); static_assert(!std::is_invocable_v, reference, width>); static_assert(!std::is_invocable_v, width, height>); static_assert(!std::is_invocable_v, height, quantity_point>); static_assert(!std::is_invocable_v, quantity_point, height>); static_assert(!std::is_invocable_v, width, reference>); static_assert(!std::is_invocable_v, width, length>); static_assert(!std::is_invocable_v, width, quantity_point>); static_assert(!std::is_invocable_v, width, double>); static_assert(!std::is_invocable_v, width, width>); // clang-format off static_assert(!std::is_invocable_v, quantity_kind, one>, quantity_kind, one>>); static_assert(!std::is_invocable_v, quantity_kind, one>, quantity_kind< height_kind, metre>>); static_assert(!std::is_invocable_v, quantity_kind< width_kind, metre>, quantity_kind, one>>); static_assert(!std::is_invocable_v, quantity_kind, day>, quantity_kind< height_kind, metre>>); static_assert(!std::is_invocable_v, quantity_kind< width_kind, metre>, quantity_kind, day>>); static_assert(!std::is_invocable_v, quantity_kind, day>, quantity_kind, day>>); static_assert(!std::is_invocable_v, quantity_kind, one>, quantity_kind, one>>); static_assert(!std::is_invocable_v, quantity_kind, one>, quantity_kind< height_kind, metre>>); static_assert(!std::is_invocable_v, quantity_kind< width_kind, metre>, quantity_kind, one>>); static_assert(!std::is_invocable_v, quantity_kind, day>, quantity_kind< height_kind, metre>>); static_assert(!std::is_invocable_v, quantity_kind< width_kind, metre>, quantity_kind, day>>); static_assert(!std::is_invocable_v, quantity_kind, day>, quantity_kind, day>>); static_assert(!std::is_invocable_v, quantity_kind, one>, quantity_kind, one>>); static_assert(!std::is_invocable_v, quantity_kind, one>, quantity_kind< height_kind, metre>>); static_assert(!std::is_invocable_v, quantity_kind< width_kind, metre>, quantity_kind, one>>); static_assert(!std::is_invocable_v, quantity_kind, day>, quantity_kind< height_kind, metre>>); static_assert(!std::is_invocable_v, quantity_kind< width_kind, metre>, quantity_kind, day>>); static_assert(!std::is_invocable_v, quantity_kind, day>, quantity_kind, day>>); // clang-format on ///////////////////////// // comparison operators ///////////////////////// static_assert(width(1 * m) == width(1 * m)); static_assert(width(1 * m) == width(1.0 * m)); static_assert(width(1 * m) == width(1000 * mm)); static_assert(width(1 * m) == width(1e3 * mm)); static_assert(width(2 * m) != width(1 * m)); static_assert(width(2 * m) != width(1.0 * cgs_cm)); static_assert(std::equality_comparable_with, width>); static_assert(std::equality_comparable_with, width>); static_assert(std::equality_comparable_with, width>); static_assert(std::equality_comparable_with, width>); template concept invalid_equality = requires(quantity_kind w) { requires !requires { w == 1; }; requires !requires { w != 1.0; }; requires !requires { w == 1 * m; }; requires !requires { w != 1.0 * cgs_cm; }; requires !requires { w == 1 * km; }; requires !requires { w != m; }; requires !requires { w == km; }; requires !requires { w != quantity(1); }; requires !requires { w == dimensionless(1.0); }; requires !requires { w != height(1 * m); }; requires !requires { w == height(1.0 * km); }; requires !requires { w != horizontal_area(1 * (m * m)); }; requires !requires { w == rate_of_climb(1.0 * (km / h)); }; requires !requires { w != quantity_point(1 * m); }; requires !requires { w == quantity_point(1.0 * mm); }; requires !requires { w != quantity_point(quantity(1)); }; requires !requires { w == quantity_point(dimensionless(1.0)); }; }; static_assert(invalid_equality); static_assert(width(1 * m) < width(2 * m)); static_assert(width(1 * m) <= width(2.0 * m)); static_assert(width(1 * m) <= width(1 * km)); static_assert(width(1 * m) >= width(1e3 * mm)); static_assert(width(2 * m) >= width(1 * mm)); static_assert(width(2 * m) > width(1 * cgs_cm)); static_assert(std::three_way_comparable_with, width>); static_assert(std::three_way_comparable_with, width>); static_assert(std::three_way_comparable_with, width>); static_assert(std::three_way_comparable_with, width>); template concept invalid_relational = requires(quantity_kind w) { requires !requires { w < 1; }; requires !requires { w <= 1.0; }; requires !requires { w >= 1 * m; }; requires !requires { w > 1.0 * cgs_cm; }; requires !requires { w <=> 1 * km; }; requires !requires { w < quantity(1); }; requires !requires { w <= dimensionless(1.0); }; requires !requires { w >= height(1 * m); }; requires !requires { w > height(1.0 * km); }; requires !requires { w <=> horizontal_area(1 * (m * m)); }; requires !requires { w < rate_of_climb(1.0 * (km / h)); }; requires !requires { w <= quantity_point(1 * m); }; requires !requires { w >= quantity_point(1.0 * mm); }; requires !requires { w > quantity_point(quantity(1)); }; requires !requires { w <=> quantity_point(dimensionless(1.0)); }; }; static_assert(invalid_relational); /////////////////////// // quantity_kind_cast /////////////////////// // clang-format off static_assert(same(quantity_kind_cast>(width(1 * m)), width(1 * m))); static_assert(same(quantity_kind_cast>(width(1 * m)), width(1.0 * m))); static_assert(same(quantity_kind_cast>(width(999 * m)), width(0 * km))); static_assert(same(quantity_kind_cast>(width(1000 * m)), width(1 * km))); static_assert(same(quantity_kind_cast>(width(999 * m)), width(0.999 * km))); static_assert(same(quantity_kind_cast(width(1 * m)), width(1.0 * m))); static_assert(same(quantity_kind_cast(width(1 * m)), width(1 * m))); static_assert(same(quantity_kind_cast(width(999 * m)), width(0 * km))); static_assert(same(quantity_kind_cast(width(1000 * m)), width(1 * km))); static_assert(same(quantity_kind_cast>(width(1 * m)), height(1 * m))); static_assert(same(quantity_kind_cast>(width(1 * m)), height(1.0 * m))); static_assert(same(quantity_kind_cast>(width(999 * m)), height(0 * km))); static_assert(same(quantity_kind_cast>(width(1000 * m)), height(1 * km))); static_assert(same(quantity_kind_cast>(width(999 * m)), height(0.999 * km))); static_assert(same(quantity_kind_cast(width(1 * m)), height(1 * m))); static_assert(same(quantity_kind_cast(width(1 * m)), height(1 * m))); static_assert(same(quantity_kind_cast(width(999 * m)), height(0 * km))); static_assert(same(quantity_kind_cast(width(1000 * m)), height(1 * km))); static_assert(same(quantity_kind_cast>(width(1 * cm)), cgs_width(1 * cgs_cm))); static_assert(same(quantity_kind_cast(width(1 * cm)), cgs_width(1 * cgs_cm))); static_assert(same(quantity_kind_cast(width(1 * cm)), cgs_width(1 * cgs_cm))); static_assert(same(quantity_kind_cast(width(1 * m)), cgs_width(1 * m))); static_assert(same(quantity_kind_cast(width(1 * m)), cgs_width(1 * m))); static_assert(same(quantity_kind_cast(width(1 * cm)), width(1 * cgs_cm))); static_assert(same(quantity_kind_cast>(width(1 * m)), width(0 * km))); static_assert(same(quantity_kind_cast>(width(1 * m)), width(100 * cm))); static_assert(same(quantity_kind_cast>(width(0.01 * m)), width(1 * cm))); static_assert(same(quantity_kind_cast>(width(1 * cgs_cm)), width(1 * cgs_cm))); // clang-format on template concept invalid_cast = requires { requires !requires { quantity_kind_cast>(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast>(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast>(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast>(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast>(quantity_kind(1 * m)); }; requires !requires { quantity_kind_cast>(quantity_kind(1 * m)); }; }; static_assert(invalid_cast); ///////////////////////// // extensible interface ///////////////////////// namespace mylib { struct radius_kind : kind {}; struct cylinder_size {}; template Radius, units::QuantityKindOf Height> cylinder_size operator+(Radius, Height); } // namespace mylib namespace yourapp { static_assert(is_same_v(1. * m) + height(1. * m))>); } // namespace yourapp } // namespace