// The MIT License (MIT) // // Copyright (c) 2018 Mateusz Pusz // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include #include #include #include #include #include #ifdef MP_UNITS_IMPORT_STD import std; #else #include #include #include #include #endif namespace { using namespace mp_units; using namespace mp_units::utility; using namespace mp_units::detail; // ============================================================================ // A test error policy // ============================================================================ struct test_policy { static constexpr void on_constraint_violation(std::string_view) noexcept {} }; // ============================================================================ // Type traits and value_type // ============================================================================ static_assert(std::is_same_v::value_type, int>); static_assert(std::is_same_v::value_type, double>); static_assert(std::is_same_v::value_type, long>); // ============================================================================ // error_policy alias // ============================================================================ static_assert(std::is_same_v::error_policy, test_policy>); // ============================================================================ // Representation concepts (int) // ============================================================================ static_assert(std::is_convertible_v, int>); static_assert(std::is_convertible_v>); static_assert(std::copyable>); static_assert(std::equality_comparable>); static_assert(std::totally_ordered>); static_assert(RepresentationOf, quantity_tensor_order::scalar>); // ============================================================================ // Representation concepts (double) // ============================================================================ static_assert(std::is_convertible_v, double>); static_assert(std::is_convertible_v>); static_assert(std::copyable>); static_assert(std::equality_comparable>); static_assert(std::totally_ordered>); static_assert(RepresentationOf, quantity_tensor_order::scalar>); // ============================================================================ // std::numeric_limits // ============================================================================ static_assert(std::numeric_limits>::is_specialized); static_assert(std::numeric_limits>::is_specialized); static_assert(std::numeric_limits>::min() == constrained{std::numeric_limits::min()}); static_assert(std::numeric_limits>::max() == constrained{std::numeric_limits::max()}); // ============================================================================ // Construction and conversion // ============================================================================ static_assert(constrained{42}.value() == 42); static_assert(static_cast(constrained{-7}) == -7); static_assert(constrained{0} == constrained{0}); static_assert(constrained{1} != constrained{2}); static_assert(constrained{3.14}.value() == 3.14); static_assert(static_cast(constrained{2.71828}) == 2.71828); // ============================================================================ // Comparison operators // ============================================================================ static_assert(constrained{1} < constrained{2}); static_assert(constrained{2} > constrained{1}); static_assert(constrained{1} <= constrained{1}); static_assert(constrained{1} >= constrained{1}); static_assert((constrained{1} <=> constrained{2}) < 0); static_assert(constrained{1.0} < constrained{2.0}); // ============================================================================ // Unary operators // ============================================================================ static_assert(+constrained{5} == constrained{5}); static_assert(-constrained{5} == constrained{-5}); static_assert(-constrained{-3} == constrained{3}); static_assert(-constrained{1.5} == constrained{-1.5}); // ============================================================================ // Arithmetic (int) — forwarded transparently // ============================================================================ static_assert(constrained{3} + constrained{4} == constrained{7}); static_assert(constrained{10} - constrained{3} == constrained{7}); static_assert(constrained{3} * constrained{4} == constrained{12}); static_assert(constrained{12} / constrained{4} == constrained{3}); static_assert(constrained{10} % constrained{3} == constrained{1}); // ============================================================================ // Arithmetic (double) // ============================================================================ static_assert(constrained{3.0} + constrained{4.0} == constrained{7.0}); static_assert(constrained{10.0} - constrained{3.0} == constrained{7.0}); static_assert(constrained{3.0} * constrained{4.0} == constrained{12.0}); static_assert(constrained{12.0} / constrained{4.0} == constrained{3.0}); // ============================================================================ // Increment / decrement // ============================================================================ static_assert([] { constrained x{5}; ++x; return x == constrained{6}; }()); static_assert([] { constrained x{5}; auto old = x++; return old == constrained{5} && x == constrained{6}; }()); static_assert([] { constrained x{5}; --x; return x == constrained{4}; }()); static_assert([] { constrained x{5}; auto old = x--; return old == constrained{5} && x == constrained{4}; }()); // Postfix ++ / -- for double — exercises the trailing-return-type // decltype(std::declval()++) path for floating-point T. static_assert([] { constrained x{1.5}; auto old = x++; return old == constrained{1.5} && x == constrained{2.5}; }()); static_assert([] { constrained x{2.5}; auto old = x--; return old == constrained{2.5} && x == constrained{1.5}; }()); // ============================================================================ // constraint_violation_handler specialization // ============================================================================ // constrained has a specialization static_assert(HasConstraintViolationHandler>); static_assert(HasConstraintViolationHandler>); // plain types do not static_assert(!HasConstraintViolationHandler); static_assert(!HasConstraintViolationHandler); // ============================================================================ // Mixed arithmetic with raw types // ============================================================================ // constrained + raw → should still be constrained (via implicit conversions) static_assert(std::is_same_v{3} + constrained{4}), constrained>); static_assert(std::is_same_v{3} * constrained{4}), constrained>); static_assert(std::is_same_v{5}), constrained>); // constrained * double and double * constrained must be unambiguous // (required by UsesFloatingPointScaling which is checked by RepresentationOf) static_assert(requires(constrained v, double f) { { v * f } -> std::common_with>; { f * v } -> std::common_with>; { v / f } -> std::common_with>; }); // ============================================================================ // UnitMagnitudeScalable / RepresentationOf — must hold for quantity_point use // ============================================================================ // These are the exact concept prerequisites checked when constrained is // used as the Rep in quantity<> or quantity_point<>. If any fails, the example // hello_units.cpp will fail to compile. static_assert(treat_as_floating_point>); static_assert(UsesFloatingPointScaling>); static_assert(UnitMagnitudeScalable>); static_assert(RealScalar>); static_assert(ScalarRepresentation>); static_assert(RepresentationOf, isq::angular_measure>); // ============================================================================ // representation_values compatibility // ============================================================================ static_assert(representation_values>::zero() == constrained{0}); static_assert(representation_values>::one() == constrained{1}); static_assert(representation_values>::min() == constrained{std::numeric_limits::lowest()}); static_assert(representation_values>::max() == constrained{std::numeric_limits::max()}); // ============================================================================ // Mixed-type arithmetic — same value_type (returns constrained) // ============================================================================ static_assert(std::is_same_v{} * 2), constrained>); static_assert(std::is_same_v{}), constrained>); static_assert(std::is_same_v{} / 2), constrained>); static_assert(constrained{6} * 2 == constrained{12}); static_assert(2 * constrained{6} == constrained{12}); static_assert(constrained{12} / 4 == constrained{3}); // ============================================================================ // Cross-type arithmetic (returns constrained) // Uses decltype of the actual underlying operation, not std::common_type_t. // ============================================================================ // constrained * double → constrained (decltype(int*double) == double) static_assert(std::is_same_v{} * 1.0), constrained>); static_assert(std::is_same_v{}), constrained>); static_assert(std::is_same_v{} / 1.0), constrained>); // constrained * float → constrained static_assert(std::is_same_v{} * 1.0f), constrained>); static_assert(std::is_same_v{}), constrained>); static_assert(std::is_same_v{} / 1.0f), constrained>); // constrained * long long → constrained (decltype(int*ll) == long long) static_assert(std::is_same_v{} * 1LL), constrained>); static_assert(std::is_same_v{}), constrained>); static_assert(std::is_same_v{} / 1LL), constrained>); // constrained * int → constrained (decltype(double*int) == double) static_assert(std::is_same_v{} * 1), constrained>); static_assert(std::is_same_v{} / 1), constrained>); // values static_assert(constrained{3} * 2.5 == constrained{7.5}); static_assert(2.5 * constrained{3} == constrained{7.5}); static_assert(constrained{10} / 4.0 == constrained{2.5}); // ============================================================================ // Heterogeneous wrapper×wrapper arithmetic (constrained op constrained, T≠U) // Result type is constrained — same EP, promoted value_type. // ============================================================================ // int op long → long (int widens to long on 64-bit platforms) static_assert(std::is_same_v{} + constrained{}), constrained>); static_assert(std::is_same_v{} - constrained{}), constrained>); static_assert(std::is_same_v{} * constrained{}), constrained>); static_assert(std::is_same_v{} / constrained{}), constrained>); static_assert(std::is_same_v{} % constrained{}), constrained>); // short op int → int (short promotes to int) static_assert(std::is_same_v{} + constrained{}), constrained>); static_assert(std::is_same_v{} + constrained{}), constrained>); // Value correctness static_assert(constrained{3} + constrained{4L} == constrained{7L}); static_assert(constrained{10} - constrained{4L} == constrained{6L}); static_assert(constrained{3} * constrained{4L} == constrained{12L}); static_assert(constrained{12} / constrained{4L} == constrained{3L}); static_assert(constrained{10} % constrained{3L} == constrained{1L}); // Heterogeneous comparison (operator== and operator<=>) static_assert(constrained{3} == constrained{3L}); static_assert(constrained{2} != constrained{3L}); static_assert((constrained{2} <=> constrained{3L}) < 0); static_assert((constrained{3} <=> constrained{3L}) == 0); static_assert((constrained{4} <=> constrained{3L}) > 0); // EP is preserved: result carries the same error policy as the operands static_assert(std::is_same_v{} + constrained{})::error_policy, test_policy>); // ============================================================================ // Integer and floating-point promotion rules // The wrapper uses decltype of the underlying operation, so it must follow // C++ usual-arithmetic-conversion and integer-promotion rules exactly. // ============================================================================ // Homogeneous short×short: both operands promote to int before the op; // result is constrained (== constrained on all // common platforms, but written portably here). static_assert(std::is_same_v{} + constrained{}), constrained>); static_assert(std::is_same_v{} * constrained{}), constrained>); // Unary + also triggers integer promotion: +short → int. static_assert( std::is_same_v{}), constrained>); // Scalar path inherits promotion: constrained * int-literal. static_assert( std::is_same_v{} * 2), constrained>); // Floating-point: float × float stays float (no promotion). static_assert(std::is_same_v{} + constrained{}), constrained>); // Usual arithmetic conversion: float op double → double. static_assert(std::is_same_v{} + constrained{}), constrained>); static_assert(std::is_same_v{} + constrained{}), constrained>); // FP scalar path: constrained * double-literal → constrained. static_assert(std::is_same_v{} * 1.0), constrained>); // ============================================================================ // common_type deduction // SFINAE-friendly helper (GCC emits hard errors from `requires { typename common_type_t<...>; }` // when the ternary inside common_type is ambiguous, so we use void_t instead). template struct has_common_type : std::false_type {}; template struct has_common_type>> : std::true_type {}; // ============================================================================ // common_type deduction // // constrained has no converting constructor from constrained. // When both wrappers have the same underlying type, common_type fails // (ambiguous: both constrained→T and T→constrained are one-step implicit). // When they have different underlying types, the wrapper drops off and // common_type resolves to the underlying common_type. // ============================================================================ // Same type: trivially exists static_assert(std::is_same_v, constrained>, constrained>); // Different underlying types: wrapper drops off, resolves to underlying common_type static_assert(std::is_same_v, constrained>, int>); static_assert(std::is_same_v, constrained>, long>); static_assert( std::is_same_v, constrained>, unsigned>); // constrained vs raw same type: ambiguous (both wrapper→T and T→wrapper are one-step implicit) static_assert(!has_common_type, int>::value); static_assert(!has_common_type>::value); static_assert(!has_common_type, double>::value); // Different policies: wrapper drops off, resolves to underlying type struct other_policy { static constexpr void on_constraint_violation(std::string_view) noexcept {} }; static_assert(std::is_same_v, constrained>, int>); } // namespace