mirror of
https://github.com/mpusz/mp-units.git
synced 2025-08-06 13:44:27 +02:00
Remove .value
; provide free function only
This is a cleaner interface. I also checked all of the commented-out test cases.
This commit is contained in:
@@ -139,6 +139,14 @@ constexpr T int_power(T base, std::integral auto exp){
|
||||
// "parameter-compatible static_asserts", and should not result in exceptions at runtime.
|
||||
if (exp < 0) { throw std::invalid_argument{"int_power only supports positive integer powers"}; }
|
||||
|
||||
constexpr auto checked_multiply = [] (auto a, auto b) {
|
||||
const auto result = a * b;
|
||||
if (result / a != b) { throw std::overflow_error{"Wraparound detected"}; }
|
||||
return result;
|
||||
};
|
||||
|
||||
constexpr auto checked_square = [checked_multiply] (auto a) { return checked_multiply(a, a); };
|
||||
|
||||
// TODO(chogg): Unify this implementation with the one in pow.h. That one takes its exponent as a
|
||||
// template parameter, rather than a function parameter.
|
||||
|
||||
@@ -147,17 +155,10 @@ constexpr T int_power(T base, std::integral auto exp){
|
||||
}
|
||||
|
||||
if (exp % 2 == 1) {
|
||||
return base * int_power(base, exp - 1);
|
||||
return checked_multiply(base, int_power(base, exp - 1));
|
||||
}
|
||||
|
||||
const auto square_root = int_power(base, exp / 2);
|
||||
const auto result = square_root * square_root;
|
||||
|
||||
if constexpr(std::is_unsigned_v<T>) {
|
||||
if (result / square_root != square_root) { throw std::overflow_error{"Unsigned wraparound"}; }
|
||||
}
|
||||
|
||||
return result;
|
||||
return checked_square(int_power(base, exp / 2));
|
||||
}
|
||||
|
||||
|
||||
@@ -331,14 +332,6 @@ struct magnitude {
|
||||
|
||||
// Whether this magnitude represents a rational number.
|
||||
friend constexpr bool is_rational(const magnitude&) { return (detail::is_rational(BPs) && ...); }
|
||||
|
||||
// The value of this magnitude, expressed in a given type.
|
||||
template<typename T>
|
||||
requires (
|
||||
std::is_floating_point_v<T>
|
||||
|| (std::is_integral_v<T> && (detail::is_integral(BPs) && ...)))
|
||||
static constexpr T value = detail::checked_static_cast<T>(
|
||||
(detail::compute_base_power<T>(BPs) * ...));
|
||||
};
|
||||
|
||||
// Implementation for Magnitude concept (below).
|
||||
@@ -356,12 +349,16 @@ template<typename T>
|
||||
concept Magnitude = detail::is_magnitude<T>;
|
||||
|
||||
/**
|
||||
* @brief Free-function access to the value of a Magnitude in a desired type.
|
||||
*
|
||||
* Can avoid the need for an unsightly `.template` keyword.
|
||||
* @brief The value of a Magnitude in a desired type T.
|
||||
*/
|
||||
template<typename T>
|
||||
constexpr T get_value(Magnitude auto m) { return decltype(m)::template value<T>; }
|
||||
template<typename T, BasePower auto... BPs>
|
||||
requires (std::is_floating_point_v<T> || (std::is_integral_v<T> && is_integral(magnitude<BPs...>{})))
|
||||
constexpr T get_value(const magnitude<BPs...> &) {
|
||||
// Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow.
|
||||
constexpr auto result = detail::checked_static_cast<T>((detail::compute_base_power<T>(BPs) * ...));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A base to represent pi.
|
||||
|
@@ -170,7 +170,7 @@ TEST_CASE("magnitude converts to numerical value")
|
||||
constexpr auto via_float = cube(std::numbers::pi_v<float>);
|
||||
constexpr auto via_long_double = static_cast<float>(cube(std::numbers::pi_v<long double>));
|
||||
|
||||
constexpr auto pi_cubed_value = pi_cubed.value<float>;
|
||||
constexpr auto pi_cubed_value = get_value<float>(pi_cubed);
|
||||
REQUIRE(pi_cubed_value != via_float);
|
||||
CHECK(pi_cubed_value == via_long_double);
|
||||
}
|
||||
@@ -181,22 +181,22 @@ TEST_CASE("magnitude converts to numerical value")
|
||||
// Naturally, we cannot actually write a test to verify a compiler error. But any of these can
|
||||
// be uncommented if desired to verify that it breaks the build.
|
||||
|
||||
// (void)as_magnitude<412>().value<int8_t>;
|
||||
// get_value<int8_t>(as_magnitude<412>());
|
||||
|
||||
// Would work for pow<62>:
|
||||
// (void)pow<63>(as_magnitude<2>()).value<int64_t>;
|
||||
// get_value<int64_t>(pow<63>(as_magnitude<2>()));
|
||||
|
||||
// Would work for pow<63>:
|
||||
// (void)pow<64>(as_magnitude<2>()).value<uint64_t>;
|
||||
// get_value<uint64_t>(pow<64>(as_magnitude<2>()));
|
||||
|
||||
(void)pow<308>(as_magnitude<10>()).value<double>; // Compiles, correctly.
|
||||
// (void)pow<309>(as_magnitude<10>()).value<double>;
|
||||
// (void)pow<3099>(as_magnitude<10>()).value<double>;
|
||||
// (void)pow<3099999>(as_magnitude<10>()).value<double>;
|
||||
get_value<double>(pow<308>(as_magnitude<10>())); // Compiles, correctly.
|
||||
// get_value<double>(pow<309>(as_magnitude<10>()));
|
||||
// get_value<double>(pow<3099>(as_magnitude<10>()));
|
||||
// get_value<double>(pow<3099999>(as_magnitude<10>()));
|
||||
|
||||
auto sqrt_2 = pow<ratio{1, 2}>(as_magnitude<2>());
|
||||
CHECK(!is_integral(sqrt_2));
|
||||
// (void)sqrt_2.value<int>;
|
||||
// get_value<int>(sqrt_2);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user