diff --git a/src/include/units/bits/pow.h b/src/include/units/bits/pow.h new file mode 100644 index 00000000..c6693103 --- /dev/null +++ b/src/include/units/bits/pow.h @@ -0,0 +1,61 @@ +// 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. + +#pragma once + +#include +#include + +namespace units::detail { + +constexpr std::intmax_t ipow10(std::intmax_t exp) +{ + assert(exp >= 0); + if (exp == 0) return 1; + std::intmax_t result = 1; + while (exp > 0) { + result *= 10; + --exp; + } + return result; +} + +template +constexpr Rep fpow10(std::intmax_t exp) +{ + if (exp == 0) return Rep(1.0); + Rep result = Rep(1.0); + if (exp < 0) { + while (exp < 0) { + result = result / Rep(10.0); + ++exp; + } + } else { + while (exp > 0) { + result = result * Rep(10.0); + --exp; + } + } + return result; +} + +} // namespace units::detail diff --git a/src/include/units/prefix.h b/src/include/units/prefix.h index fbf45665..129be0b3 100644 --- a/src/include/units/prefix.h +++ b/src/include/units/prefix.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include #include diff --git a/src/include/units/quantity.h b/src/include/units/quantity.h index 06c46c5c..eb38df01 100644 --- a/src/include/units/quantity.h +++ b/src/include/units/quantity.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -342,9 +343,9 @@ template) { - return lhs.count() * rhs.count() * static_cast(r.num * fpow10(r.exp)) / static_cast(r.den); + return lhs.count() * rhs.count() * static_cast(r.num * detail::fpow10(r.exp)) / static_cast(r.den); } else { - return lhs.count() * rhs.count() * static_cast(r.num * ipow10(r.exp)) / static_cast(r.den); + return lhs.count() * rhs.count() * static_cast(r.num * detail::ipow10(r.exp)) / static_cast(r.den); } } diff --git a/src/include/units/quantity_cast.h b/src/include/units/quantity_cast.h index 14bfbd5b..79509c50 100644 --- a/src/include/units/quantity_cast.h +++ b/src/include/units/quantity_cast.h @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #include #ifdef _MSC_VER @@ -35,38 +38,6 @@ namespace units { -constexpr std::intmax_t ipow10(std::intmax_t exp) -{ - assert(exp >= 0); - if (exp == 0) return 1; - std::intmax_t result = 1; - while (exp > 0) { - result *= 10; - --exp; - } - return result; -} - -template -constexpr Rep fpow10(std::intmax_t exp) -{ - if (exp == 0) return Rep(1.0); - Rep result = Rep(1.0); - if (exp < 0) { - while (exp < 0) { - result = result / Rep(10.0); - ++exp; - } - } else { - while (exp > 0) { - result = result * Rep(10.0); - --exp; - } - } - return result; -} - - // QuantityOf template concept QuantityOf = Quantity && Dimension && equivalent_dim; @@ -92,13 +63,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * static_cast(fpow10(CRatio.exp)))); + return To(static_cast(static_cast(q.count()) * static_cast(detail::fpow10(CRatio.exp)))); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(static_cast(q.count()) * static_cast(ipow10(CRatio.exp)))); + return To(static_cast(static_cast(q.count()) * static_cast(detail::ipow10(CRatio.exp)))); } else { - return To(static_cast(static_cast(q.count()) / static_cast(ipow10(-CRatio.exp)))); + return To(static_cast(static_cast(q.count()) / static_cast(detail::ipow10(-CRatio.exp)))); } } } @@ -122,21 +93,21 @@ struct quantity_cast_impl { { if constexpr (treat_as_floating_point) { return To(static_cast(static_cast(q.count()) * - static_cast(fpow10(CRatio.exp)) * + static_cast(detail::fpow10(CRatio.exp)) * (static_cast(CRatio.num) / static_cast(CRatio.den)))); } else { if constexpr (CRatio.exp > 0) { return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * - static_cast(ipow10(CRatio.exp)) / + static_cast(detail::ipow10(CRatio.exp)) / static_cast(CRatio.den))); } else { return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) / (static_cast(CRatio.den) * - static_cast(ipow10(-CRatio.exp))))); + static_cast(detail::ipow10(-CRatio.exp))))); } } } @@ -157,13 +128,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * static_cast(fpow10(CRatio.exp)) * (CRep{1} / static_cast(CRatio.den)))); + return To(static_cast(static_cast(q.count()) * static_cast(detail::fpow10(CRatio.exp)) * (CRep{1} / static_cast(CRatio.den)))); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(static_cast(q.count()) * static_cast(ipow10(CRatio.exp)) / static_cast(CRatio.den))); + return To(static_cast(static_cast(q.count()) * static_cast(detail::ipow10(CRatio.exp)) / static_cast(CRatio.den))); } else { - return To(static_cast(static_cast(q.count()) / (static_cast(ipow10(-CRatio.exp)) * static_cast(CRatio.den)))); + return To(static_cast(static_cast(q.count()) / (static_cast(detail::ipow10(-CRatio.exp)) * static_cast(CRatio.den)))); } } } @@ -184,13 +155,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * static_cast(fpow10(CRatio.exp)))); + return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * static_cast(detail::fpow10(CRatio.exp)))); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * static_cast(ipow10(CRatio.exp)))); + return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) * static_cast(detail::ipow10(CRatio.exp)))); } else { - return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) / static_cast(ipow10(-CRatio.exp)))); + return To(static_cast(static_cast(q.count()) * static_cast(CRatio.num) / static_cast(detail::ipow10(-CRatio.exp)))); } } } @@ -202,13 +173,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(q.count() * fpow10(CRatio.exp))); + return To(static_cast(q.count() * detail::fpow10(CRatio.exp))); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(q.count() * ipow10(CRatio.exp))); + return To(static_cast(q.count() * detail::ipow10(CRatio.exp))); } else { - return To(static_cast(q.count() / ipow10(-CRatio.exp))); + return To(static_cast(q.count() / detail::ipow10(-CRatio.exp))); } } } @@ -229,13 +200,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(q.count() * fpow10(CRatio.exp) * (CRatio.num / CRatio.den))); + return To(static_cast(q.count() * detail::fpow10(CRatio.exp) * (CRatio.num / CRatio.den))); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(q.count() * CRatio.num * ipow10(CRatio.exp) / CRatio.den)); + return To(static_cast(q.count() * CRatio.num * detail::ipow10(CRatio.exp) / CRatio.den)); } else { - return To(static_cast(q.count()) * CRatio.num / (CRatio.den * ipow10(-CRatio.exp))); + return To(static_cast(q.count()) * CRatio.num / (CRatio.den * detail::ipow10(-CRatio.exp))); } } } @@ -256,13 +227,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(q.count() * fpow10(CRatio.exp) / CRatio.den)); + return To(static_cast(q.count() * detail::fpow10(CRatio.exp) / CRatio.den)); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(q.count() * ipow10(CRatio.exp) / CRatio.den)); + return To(static_cast(q.count() * detail::ipow10(CRatio.exp) / CRatio.den)); } else { - return To(static_cast(q.count() / (ipow10(-CRatio.exp) * CRatio.den))); + return To(static_cast(q.count() / (detail::ipow10(-CRatio.exp) * CRatio.den))); } } } @@ -283,13 +254,13 @@ struct quantity_cast_impl { static constexpr To cast(const Q& q) { if constexpr (treat_as_floating_point) { - return To(static_cast(q.count() * CRatio.num * fpow10(CRatio.exp))); + return To(static_cast(q.count() * CRatio.num * detail::fpow10(CRatio.exp))); } else { if constexpr (CRatio.exp > 0) { - return To(static_cast(q.count() * CRatio.num * ipow10(CRatio.exp))); + return To(static_cast(q.count() * CRatio.num * detail::ipow10(CRatio.exp))); } else { - return To(static_cast(q.count() * CRatio.num / ipow10(-CRatio.exp))); + return To(static_cast(q.count() * CRatio.num / detail::ipow10(-CRatio.exp))); } } } diff --git a/test/unit_test/static/custom_rep_min_req_test.cpp b/test/unit_test/static/custom_rep_min_req_test.cpp index d9a12ce5..2f50c244 100644 --- a/test/unit_test/static/custom_rep_min_req_test.cpp +++ b/test/unit_test/static/custom_rep_min_req_test.cpp @@ -25,6 +25,7 @@ #include "units/physical/si/frequency.h" #include "units/physical/si/speed.h" #include +#include #include using namespace units; @@ -65,7 +66,7 @@ template struct expl_constructible : scalar_ops> { T value_{}; expl_constructible() = default; - constexpr expl_constructible(T v) : value_(std::move(v)) {} + constexpr explicit expl_constructible(T v) : value_(std::move(v)) {} // no conversion to fundamental arithmetic types }; @@ -172,60 +173,60 @@ using namespace units::physical::si; // Quantity from Scalar // int <- int static_assert(length(expl_impl(1)).count() == 1); -// static_assert(length(impl_expl(1)).count() == 1); // should not compile (not convertible) +static_assert(!std::is_constructible_v, impl_expl>); static_assert(length(int(impl_expl(1))).count() == 1); -// static_assert(length>(1).count() == expl_impl{1}); // should not compile (not convertible) +static_assert(!std::is_constructible_v>, int>); static_assert(length>(expl_impl(1)).count() == expl_impl{1}); static_assert(length>(1).count() == impl_expl{1}); // double <- double static_assert(length(expl_impl(1.0)).count() == 1.0); -// static_assert(length(impl_expl(1.0)).count() == 1.0); // should not compile (not convertible) +static_assert(!std::is_constructible_v, impl_expl>); static_assert(length(double(impl_expl(1.0))).count() == 1.0); -// static_assert(length>(1.0).count() == expl_impl{1.0}); // should not compile (not convertible) +static_assert(!std::is_constructible_v>, double>); static_assert(length>(expl_impl(1.0)).count() == expl_impl{1.0}); static_assert(length>(1.0).count() == impl_expl{1.0}); // double <- int static_assert(length(expl_impl(1)).count() == 1.0); -// static_assert(length(impl_expl(1)).count() == 1.0); // should not compile (not convertible) +static_assert(!std::is_constructible_v, impl_expl>); static_assert(length(int(impl_expl(1))).count() == 1.0); -// static_assert(length>(1).count() == expl_impl{1}); // should not compile (not convertible) +static_assert(!std::is_constructible_v>, int>); static_assert(length>(expl_impl(1)).count() == expl_impl{1}); static_assert(length>(1).count() == impl_expl{1.0}); // int <- double -// static_assert(length(expl_impl(1.0)).count() == 1); // should not compile (truncating conversion) -// static_assert(length>(1.0).count() == impl_expl{1}); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v, expl_impl>); +static_assert(!std::is_constructible_v>, double>); // Quantity from other Quantity with different Rep // int <- int static_assert(length(length>(expl_impl(1))).count() == 1); -// static_assert(length(length>(1)).count() == 1); // should not compile (not convertible) +static_assert(!std::is_constructible_v, length>>); static_assert(length(quantity_cast(length>(1))).count() == 1); -// static_assert(length>(length(1)).count() == expl_impl{1}); // should not compile (not convertible) +static_assert(!std::is_constructible_v>, length>); static_assert(length>(quantity_cast>(length(1))).count() == expl_impl{1}); static_assert(length>(length(1)).count() == impl_expl{1}); // double <- double static_assert(length(length>(expl_impl(1.0))).count() == 1.0); -// static_assert(length(length>(1.0)).count() == 1.0); // should not compile (not convertible) +static_assert(!std::is_constructible_v, length>>); static_assert(length(quantity_cast(length>(1.0))).count() == 1.0); -// static_assert(length>(length(1.0).count() == expl_impl{1.0}); // should not compile (not convertible) +static_assert(!std::is_constructible_v>, length>); static_assert(length>(quantity_cast>(length(1.0))).count() == expl_impl{1.0}); static_assert(length>(length(1.0)).count() == impl_expl{1.0}); // double <- int static_assert(length(length>(expl_impl(1))).count() == 1.0); -// static_assert(length(length>(1)).count() == 1.0); // should not compile (not convertible) +static_assert(!std::is_constructible_v, length>>); static_assert(length(quantity_cast(length>(1))).count() == 1.0); -// static_assert(length>(length(1)).count() == expl_impl{1}); // should not compile (not convertible) +static_assert(!std::is_constructible_v>, length>); static_assert(length>(quantity_cast>(length(1))).count() == expl_impl{1}); static_assert(length>(length(1)).count() == impl_expl{1.0}); // int <- double -// static_assert(length(length>(1.0)).count() == 1); // should not compile (truncating conversion) -// static_assert(length>(length(1.0)).count() == impl_expl{1}); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v, length>>); +static_assert(!std::is_constructible_v>, length>); // unit conversions @@ -236,43 +237,43 @@ static_assert(length>(length>(1) static_assert(length>(length>(expl_impl(1))).count() == expl_impl(1000)); static_assert(length>(length>(expl_expl(1))).count() == expl_expl(1000)); -// static_assert(length>(length>(2000)).count() == impl(2)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, length>>); static_assert(length>(quantity_cast(length>(2000))).count() == impl(2)); -// static_assert(length>(length>(expl(2000))).count() == expl(2)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, length>>); static_assert(length>(quantity_cast(length>(expl(2000)))).count() == expl(2)); -// static_assert(length>(length>(2000)).count() == impl_impl(2)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, length>>); static_assert(length>(quantity_cast(length>(2000))).count() == impl_impl(2)); -// static_assert(length>(length>(2000)).count() == impl_expl(2)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, length>>); static_assert(length>(quantity_cast(length>(2000))).count() == impl_expl(2)); -// static_assert(length>(length>(expl_impl(2000))).count() == expl_impl(2)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, length>>); static_assert(length>(quantity_cast(length>(expl_impl(2000)))).count() == expl_impl(2)); -// static_assert(length>(length>(expl_expl(2000))).count() == expl_expl(2)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, length>>); static_assert(length>(quantity_cast(length>(expl_expl(2000)))).count() == expl_expl(2)); -// static_assert(speed>(speed>(72)).count() == impl(20)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(72))).count() == impl(20)); -// static_assert(speed>(speed>(expl(72))).count() == expl(20)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(expl(72)))).count() == expl(20)); -// static_assert(speed>(speed>(72)).count() == impl_impl(20)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(72))).count() == impl_impl(20)); -// static_assert(speed>(speed>(72)).count() == impl_expl(20)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(72))).count() == impl_expl(20)); -// static_assert(speed>(speed>(expl_impl(72))).count() == expl_impl(20)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(expl_impl(72)))).count() == expl_impl(20)); -// static_assert(speed>(speed>(expl_expl(72))).count() == expl_expl(20)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(expl_expl(72)))).count() == expl_expl(20)); -// static_assert(speed>(speed>(20)).count() == impl(72)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(20))).count() == impl(72)); -// static_assert(speed>(speed>(expl(20))).count() == expl(72)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(expl(20)))).count() == expl(72)); -// static_assert(speed>(speed>(20)).count() == impl_impl(72)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(20))).count() == impl_impl(72)); -// static_assert(speed>(speed>(20)).count() == impl_expl(72)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(20))).count() == impl_expl(72)); -// static_assert(speed>(speed>(expl_impl(20))).count() == expl_impl(72)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(expl_impl(20)))).count() == expl_impl(72)); -// static_assert(speed>(speed>(expl_expl(20))).count() == expl_expl(72)); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v>, speed>>); static_assert(speed>(quantity_cast(speed>(expl_expl(20)))).count() == expl_expl(72)); } // namespace diff --git a/test/unit_test/static/quantity_point_test.cpp b/test/unit_test/static/quantity_point_test.cpp index 424d785f..48267bfc 100644 --- a/test/unit_test/static/quantity_point_test.cpp +++ b/test/unit_test/static/quantity_point_test.cpp @@ -47,7 +47,7 @@ concept invalid_types = requires !requires { typename quantity; }; // reordered arguments }; -static_assert(invalid_types); +static_assert(invalid_types); // member types @@ -226,12 +226,16 @@ static_assert(quantity_point_cast(quantity_point(1.23q_m)).relative().count // time -#if COMP_MSVC || COMP_GCC >= 10 -static_assert(!std::equality_comparable_with, - quantity_point>); // different dimensions -#endif static_assert(quantity_point{1q_h} == quantity_point{3600q_s}); +template +constexpr bool no_crossdimensional_equality = !requires +{ + quantity_point(1q_s) == quantity_point(length(1)); +}; + +static_assert(no_crossdimensional_equality); + // length static_assert(quantity_point(1q_km) == quantity_point(1000q_m)); diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index 3e4b20f4..dcc9d7bf 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -36,10 +36,15 @@ using namespace units::physical::si; // class invariants -// constexpr quantity error(0); // should not compile (unit of a different dimension) -// constexpr quantity> error(0); // should not compile (quantity used as Rep) -// constexpr quantity error(0); // should not compile (reordered arguments) -// constexpr quantity, int> error(0); // should not compile (negative unit ratio) +template +concept invalid_types = requires +{ + !requires { typename quantity; }; // unit of a different dimension + !requires { typename quantity>; }; // quantity used as Rep + !requires { typename quantity; }; // reordered arguments +}; + +static_assert(invalid_types); // member types @@ -56,18 +61,21 @@ static_assert(km.count() == 1000); static_assert(length(km).count() == km.count()); static_assert(length(1).count() == 1); -// static_assert(length(1.0).count() == 1); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v, double>); // truncating conversion static_assert(length(1.0).count() == 1.0); static_assert(length(1).count() == 1.0); static_assert(length(3.14).count() == 3.14); static_assert(length(km).count() == 1000); -// static_assert(length(length(3.14)).count() == 3); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v, + length>); // truncating conversion static_assert(length(1000.0q_m).count() == 1000.0); static_assert(length(km).count() == 1000.0); static_assert(length(1q_km).count() == 1000); -// static_assert(length(1q_s).count() == 1); // should not compile (different dimensions) -//static_assert(length(1010q_m).count() == 1); // should not compile (truncating conversion) +static_assert(!std::is_constructible_v, + physical::si::time>); // different dimensions +static_assert(!std::is_constructible_v, + length>); // truncating conversion // assignment operator @@ -89,24 +97,22 @@ static_assert((-km).count() == -1000); static_assert((+(-km)).count() == -1000); static_assert((-(-km)).count() == 1000); -// binary member operators - static_assert([](auto v) { auto vv = v++; - return std::make_pair(v, vv); -}(km) == std::make_pair(length(1001), length(1000))); + return std::pair(v, vv); +}(km) == std::pair(length(1001), length(1000))); static_assert([](auto v) { auto vv = ++v; - return std::make_pair(v, vv); -}(km) == std::make_pair(length(1001), length(1001))); + return std::pair(v, vv); +}(km) == std::pair(length(1001), length(1001))); static_assert([](auto v) { auto vv = v--; - return std::make_pair(v, vv); -}(km) == std::make_pair(length(999), length(1000))); + return std::pair(v, vv); +}(km) == std::pair(length(999), length(1000))); static_assert([](auto v) { auto vv = --v; - return std::make_pair(v, vv); -}(km) == std::make_pair(length(999), length(999))); + return std::pair(v, vv); +}(km) == std::pair(length(999), length(999))); // compound assignment @@ -116,22 +122,29 @@ static_assert((1q_m *= 2).count() == 2); static_assert((2q_m /= 2).count() == 1); static_assert((7q_m %= 2).count() == 1); static_assert((7q_m %= 2q_m).count() == 1); -// static_assert((7.m %= 2.).count() == 1); // should not compile (operation not allowed for floating-point types) -// static_assert((7.m %= 2).count() == 1); // should not compile (operation not allowed for floating-point types) -// static_assert((7q_m %= 2.).count() == 1); // should not compile (operation not allowed for floating-point types) -static_assert((7q_m %= 2q_m).count() == 1); -// static_assert((7.m %= 2.m).count() == 1); // should not compile (operation not allowed for floating-point types) -// static_assert((7.m %= 2q_m).count() == 1); // should not compile (operation not allowed for floating-point types) -// static_assert((7q_m %= 2.m).count() == 1); // should not compile (operation not allowed for floating-point types) -// static_assert(2q_m += 3.5q_m); // should not compile static_assert((2.5q_m += 3q_m).count() == 5.5); static_assert((2.5q_m += 3.5q_m).count() == 6); -// static_assert(2q_m *= 3.5); // should not compile static_assert((2.5q_m *= 3).count() == 7.5); static_assert((2.5q_m *= 3.5).count() == 8.75); +// operations not allowed for the respective quantities +template +concept invalid_compound_assignments = requires() +{ + !requires(length l) { l %= 2.; }; + !requires(length l) { l %= 2; }; + !requires(length l) { l %= 2.; }; + !requires(length l) { l %= 2.q_m; }; + !requires(length l) { l %= 2q_m; }; + !requires(length l) { l %= 2.q_m; }; + !requires(length l) { l += 3.5q_m; }; + !requires(length l) { l *= 3.5q_m; }; +}; + +static_assert(invalid_compound_assignments); + // non-member arithmetic operators static_assert(is_same_v() + length()), length>); @@ -253,9 +266,13 @@ static_assert(quantity_cast(1.23q_m).count() == 1); // time -// static_assert(1q_s == 1q_m); // should not compile (different dimensions) static_assert(1q_h == 3600q_s); +template +constexpr bool no_crossdimensional_equality = !requires { 1q_s == length(1); }; + +static_assert(no_crossdimensional_equality); + // length static_assert(1q_km == 1000q_m); diff --git a/test/unit_test/static/ratio_test.cpp b/test/unit_test/static/ratio_test.cpp index 2c05828b..4b4d044f 100644 --- a/test/unit_test/static/ratio_test.cpp +++ b/test/unit_test/static/ratio_test.cpp @@ -29,7 +29,6 @@ using namespace units; static_assert(ratio(2, 4) == ratio(1, 2)); // basic exponents tests -// note use of ::type is required because template params are changed while stamping out template static_assert(ratio(2, 40, 1) == ratio(1, 20, 1)); static_assert(ratio(20, 4, -1) == ratio(10, 2, -1)); static_assert(ratio(200, 5) == ratio(20'000, 50, -1)); @@ -89,4 +88,10 @@ static_assert(common_ratio(ratio(100, 1), ratio(1, 10)) == ratio(1, 10)); static_assert(common_ratio(ratio(1), ratio(1, 1, 3)) == ratio(1)); static_assert(common_ratio(ratio(10, 1, -1), ratio(1, 1, -3)) == ratio(1, 1, -3)); +// nonzero denominator +template struct require_constant; // [range.split.view] +template concept constant = requires { typename require_constant; }; + +static_assert(!constant<[] { ratio(1, 0); }>); + } // namespace diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index a48a1e86..f13d0ac6 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -40,7 +40,9 @@ struct hour : named_scaled_unit { struct dim_time : base_dimension<"time", second> {}; struct kelvin : named_unit {}; -// struct kilokelvin : prefixed_unit {}; // should not compile (prefix not allowed for this reference unit) +#if COMP_MSVC || COMP_GCC >= 10 +static_assert([](P) { return !requires { typename prefixed_unit; }; }(si::kilo{})); // negative unit ratio +#endif struct metre_per_second : unit {}; struct dim_speed : derived_dimension, units::exp> {}; @@ -51,6 +53,9 @@ static_assert(is_same_v>, centimetr static_assert(is_same_v>, yard>); static_assert(is_same_v>, foot>); static_assert(is_same_v>, kilometre_per_hour>); +#if COMP_MSVC || COMP_GCC >= 10 +static_assert([]() { return !requires { typename scaled_unit; }; }.template operator()()); // negative unit ratio +#endif static_assert(centimetre::symbol == "cm"); static_assert(kilometre::symbol == "km");