mirror of
https://github.com/mpusz/mp-units.git
synced 2026-07-07 09:00:49 +02:00
fix: silence MSVC C4146/C4459/C4702/C4245 warnings exposed by build:all=True
CI builds the MSVC matrix with `user.mp-units.build:all=False` so neither tests nor examples are compiled there. Building with `all=True` on MSVC reveals a backlog of warnings that get promoted to errors by `-WX`: * `unit_magnitude.h(793)` C4245 — `find_first_factor` takes `uintmax_t`; cast `intmax_t N` to it explicitly before calling. * `framework/unit_magnitude.h::mag_ratio` (MSVC ICE workaround) — `prime_factorization` only accepts positive arguments, but the workaround was passing the original `D` directly, so `mag_ratio<3, -4>` instantiated `prime_factorization<-4>`. Normalize both halves to be positive and reconstruct the sign from the parity of `(N < 0) != (D < 0)`. * `safe_int.h::div_overflows` / `neg_overflows` / `int_in_range` C4702 — MSVC's reachability analysis does not see the `if constexpr (is_signed_v<T>) return …;` branch as unconditional for signed instantiations, so the trailing fallback return looks unreachable. Restructure as `if constexpr (…) … else …;` so both branches share an explicit `else`. * `safe_int.h::operator-` and `constrained.h::operator-` C4146 — unary minus on an unsigned operand is well-defined in C++ but MSVC flags it. Suppress locally with the new `MP_UNITS_DIAGNOSTIC_IGNORE_UNARY_MINUS_UNSIGNED` macro added to `bits/hacks.h` (no-op on GCC/Clang, `#pragma warning(disable : 4146)` on MSVC). * `framework/quantity.h` C4459 — the erased-handler trampolines used `void* p`, which shadows `si::unit_symbols::p` (pico). Renamed to `void* self`, which is also the more accurate name for a `this`-trampoline. * `bits/format.h` / `bits/text_tools.h` C4459 — short loop variables in trivial character-copy loops shadow various single-letter unit-symbol aliases brought into scope by `using namespace si::unit_symbols`. These are genuinely benign; wrap each loop in `MP_UNITS_DIAGNOSTIC_IGNORE_SHADOW` rather than chasing the next colliding short name. * `test/static/bounded_quantity_point_test.cpp` C4459 — local `auto t = time_of_day(...)` shadows `non_si::unit_symbols::t` (tonne). Renamed to `auto tod = …` — the longer name reads better in any case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -86,7 +86,13 @@ constexpr Out write_padded(Out out, std::basic_string_view<Char> s, int width, f
|
||||
for (std::size_t j = 0; j < fill.size(); ++j) *out++ = fill[j];
|
||||
};
|
||||
write_fill(lpad);
|
||||
MP_UNITS_DIAGNOSTIC_PUSH
|
||||
// Loop variable shadows a `si::unit_symbols` short name when `using namespace si::unit_symbols`
|
||||
// is in scope at the call site (MSVC C4459); the local variable here is the documented
|
||||
// canonical name for this trivial loop.
|
||||
MP_UNITS_DIAGNOSTIC_IGNORE_SHADOW
|
||||
for (Char c : s) *out++ = c;
|
||||
MP_UNITS_DIAGNOSTIC_POP
|
||||
write_fill(rpad);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#define MP_UNITS_DIAGNOSTIC_IGNORE_MISSING_BRACES MP_UNITS_DIAGNOSTIC_IGNORE("-Wmissing-braces")
|
||||
#define MP_UNITS_DIAGNOSTIC_IGNORE_NON_TEMPLATE_FRIEND MP_UNITS_DIAGNOSTIC_IGNORE("-Wnon-template-friend")
|
||||
#define MP_UNITS_DIAGNOSTIC_IGNORE_SHADOW MP_UNITS_DIAGNOSTIC_IGNORE("-Wshadow")
|
||||
#define MP_UNITS_DIAGNOSTIC_IGNORE_UNARY_MINUS_UNSIGNED
|
||||
#define MP_UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE
|
||||
#define MP_UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT \
|
||||
MP_UNITS_DIAGNOSTIC_IGNORE("-Wzero-as-nullpointer-constant")
|
||||
@@ -79,6 +80,7 @@
|
||||
#define MP_UNITS_DIAGNOSTIC_IGNORE_MISSING_BRACES
|
||||
#define MP_UNITS_DIAGNOSTIC_IGNORE_NON_TEMPLATE_FRIEND
|
||||
#define MP_UNITS_DIAGNOSTIC_IGNORE_SHADOW MP_UNITS_DIAGNOSTIC_IGNORE(4459)
|
||||
#define MP_UNITS_DIAGNOSTIC_IGNORE_UNARY_MINUS_UNSIGNED MP_UNITS_DIAGNOSTIC_IGNORE(4146)
|
||||
#define MP_UNITS_DIAGNOSTIC_IGNORE_UNREACHABLE MP_UNITS_DIAGNOSTIC_IGNORE(4702)
|
||||
#define MP_UNITS_DIAGNOSTIC_IGNORE_ZERO_AS_NULLPOINTER_CONSTANT
|
||||
#define MP_UNITS_DIAGNOSTIC_IGNORE_DEPRECATED
|
||||
|
||||
@@ -110,7 +110,12 @@ template<typename CharT, std::size_t N, std::size_t M, std::output_iterator<Char
|
||||
// fallback to portable mode
|
||||
return ::mp_units::detail::copy(txt.portable().begin(), txt.portable().end(), out);
|
||||
#endif
|
||||
MP_UNITS_DIAGNOSTIC_PUSH
|
||||
// Short names like `ch` / `byte` shadow unit symbols (`imperial::ch`, `iec::byte`) when
|
||||
// `using namespace …::unit_symbols` is in scope at the call site (MSVC C4459).
|
||||
MP_UNITS_DIAGNOSTIC_IGNORE_SHADOW
|
||||
for (const char8_t ch : txt.utf8()) *out++ = static_cast<char>(ch);
|
||||
MP_UNITS_DIAGNOSTIC_POP
|
||||
return out;
|
||||
} else
|
||||
MP_UNITS_THROW(std::invalid_argument("UTF-8 text can't be copied to CharT output"));
|
||||
|
||||
@@ -790,7 +790,7 @@ template<std::intmax_t N>
|
||||
struct prime_factorization {
|
||||
[[nodiscard]] static consteval std::intmax_t get_or_compute_first_factor()
|
||||
{
|
||||
return static_cast<std::intmax_t>(find_first_factor(N));
|
||||
return static_cast<std::intmax_t>(find_first_factor(static_cast<std::uintmax_t>(N)));
|
||||
}
|
||||
|
||||
static constexpr std::intmax_t first_base = get_or_compute_first_factor();
|
||||
|
||||
@@ -232,11 +232,17 @@ public:
|
||||
return +x.value_;
|
||||
}
|
||||
|
||||
MP_UNITS_DIAGNOSTIC_PUSH
|
||||
// Generic operator- is intentionally instantiated with unsigned T as well (where unary minus
|
||||
// returns the same unsigned type by C++ rules; MSVC C4146 flags this even though it is the
|
||||
// documented behavior).
|
||||
MP_UNITS_DIAGNOSTIC_IGNORE_UNARY_MINUS_UNSIGNED
|
||||
[[nodiscard]] friend constexpr auto operator-(const constrained& x)
|
||||
-> constrained<decltype(-std::declval<T>()), ErrorPolicy>
|
||||
{
|
||||
return -x.value_;
|
||||
}
|
||||
MP_UNITS_DIAGNOSTIC_POP
|
||||
|
||||
// -- Increment / decrement --
|
||||
constexpr constrained& operator++()
|
||||
|
||||
@@ -990,13 +990,14 @@ class MP_UNITS_STD_FMT::formatter<mp_units::quantity<Reference, Rep>, Char> {
|
||||
void on_text(const Char* begin, const Char* end) { mp_units::detail::copy(begin, end, out); }
|
||||
|
||||
// Static trampolines for erased_handler — tiny, do not drag in parse_quantity_specs.
|
||||
static void on_number_erased(void* p) { static_cast<quantity_formatter*>(p)->on_number(); }
|
||||
static void on_maybe_space_erased(void* p) { static_cast<quantity_formatter*>(p)->on_maybe_space(); }
|
||||
static void on_unit_erased(void* p) { static_cast<quantity_formatter*>(p)->on_unit(); }
|
||||
static void on_dimension_erased(void* p) { static_cast<quantity_formatter*>(p)->on_dimension(); }
|
||||
static void on_text_erased(void* p, const Char* b, const Char* e)
|
||||
// (Param renamed from `p` so it doesn't shadow `si::unit_symbols::p` (pico) on MSVC C4459.)
|
||||
static void on_number_erased(void* self) { static_cast<quantity_formatter*>(self)->on_number(); }
|
||||
static void on_maybe_space_erased(void* self) { static_cast<quantity_formatter*>(self)->on_maybe_space(); }
|
||||
static void on_unit_erased(void* self) { static_cast<quantity_formatter*>(self)->on_unit(); }
|
||||
static void on_dimension_erased(void* self) { static_cast<quantity_formatter*>(self)->on_dimension(); }
|
||||
static void on_text_erased(void* self, const Char* b, const Char* e)
|
||||
{
|
||||
static_cast<quantity_formatter*>(p)->on_text(b, e);
|
||||
static_cast<quantity_formatter*>(self)->on_text(b, e);
|
||||
}
|
||||
|
||||
erased_handler erase()
|
||||
|
||||
@@ -78,8 +78,11 @@ template<std::intmax_t N, std::intmax_t D>
|
||||
requires(N != 0)
|
||||
constexpr UnitMagnitude auto mag_ratio = []() consteval {
|
||||
constexpr auto abs_n = N < 0 ? -N : N;
|
||||
constexpr auto abs_mag = detail::prime_factorization_v<abs_n> / detail::prime_factorization_v<D>;
|
||||
if constexpr (N < 0)
|
||||
constexpr auto abs_d = D < 0 ? -D : D;
|
||||
// prime_factorization is only defined for positive N, so normalize both halves to be
|
||||
// positive and reconstruct the overall sign from N and D independently.
|
||||
constexpr auto abs_mag = detail::prime_factorization_v<abs_n> / detail::prime_factorization_v<abs_d>;
|
||||
if constexpr ((N < 0) != (D < 0))
|
||||
return detail::unit_magnitude<detail::negative_tag{}>{} * abs_mag;
|
||||
else
|
||||
return abs_mag;
|
||||
|
||||
@@ -171,16 +171,20 @@ template<integral T>
|
||||
[[nodiscard]] constexpr bool div_overflows(T lhs, T rhs) noexcept
|
||||
{
|
||||
if (rhs == 0) return true;
|
||||
if constexpr (is_signed_v<T>) return lhs == std::numeric_limits<T>::min() && rhs == T{-1};
|
||||
return false;
|
||||
if constexpr (is_signed_v<T>)
|
||||
return lhs == std::numeric_limits<T>::min() && rhs == T{-1};
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true if -lhs overflows (only INT_MIN for signed).
|
||||
template<integral T>
|
||||
[[nodiscard]] constexpr bool neg_overflows(T v) noexcept
|
||||
{
|
||||
if constexpr (is_signed_v<T>) return v == std::numeric_limits<T>::min();
|
||||
return v != T{0}; // negation of any non-zero unsigned overflows
|
||||
if constexpr (is_signed_v<T>)
|
||||
return v == std::numeric_limits<T>::min();
|
||||
else
|
||||
return v != T{0}; // negation of any non-zero unsigned overflows
|
||||
}
|
||||
|
||||
// Extracts the underlying integral type from an arithmetic wrapper:
|
||||
@@ -233,8 +237,10 @@ template<integral To, integral From>
|
||||
return v <= static_cast<From>(std::numeric_limits<To>::max());
|
||||
} else {
|
||||
if (v < From{0}) {
|
||||
if constexpr (!is_signed_v<To>) return false; // negative -> unsigned: never
|
||||
return v >= static_cast<From>(std::numeric_limits<To>::min());
|
||||
if constexpr (!is_signed_v<To>)
|
||||
return false; // negative -> unsigned: never
|
||||
else
|
||||
return v >= static_cast<From>(std::numeric_limits<To>::min());
|
||||
}
|
||||
return v <= static_cast<From>(std::numeric_limits<To>::max());
|
||||
}
|
||||
@@ -391,11 +397,17 @@ public:
|
||||
|
||||
// -- Unary arithmetic --
|
||||
[[nodiscard]] constexpr auto operator+() const -> safe_int<decltype(+value_), ErrorPolicy> { return +value_; }
|
||||
MP_UNITS_DIAGNOSTIC_PUSH
|
||||
// Generic operator- is intentionally instantiated with unsigned T as well (where unary minus
|
||||
// returns the same unsigned type by C++ rules; MSVC C4146 flags this even though it is the
|
||||
// documented behavior).
|
||||
MP_UNITS_DIAGNOSTIC_IGNORE_UNARY_MINUS_UNSIGNED
|
||||
[[nodiscard]] constexpr auto operator-() const -> safe_int<decltype(-value_), ErrorPolicy>
|
||||
{
|
||||
if (detail::neg_overflows(+value_)) handle_overflow("safe_int: negation overflow");
|
||||
return -value_;
|
||||
}
|
||||
MP_UNITS_DIAGNOSTIC_POP
|
||||
|
||||
// -- Increment / decrement (use add/sub overflow check when T is integral) --
|
||||
constexpr safe_int& operator++()
|
||||
|
||||
@@ -837,9 +837,9 @@ static_assert(time_of_day(-3600.0 * s, midnight).quantity_from(midnight) == 8280
|
||||
// Arithmetic: adding a duration that crosses midnight wraps correctly.
|
||||
consteval bool time_of_day_wrap_arithmetic()
|
||||
{
|
||||
auto t = time_of_day(82800.0 * s, midnight); // 23:00
|
||||
t += 7200.0 * s; // +2 h -> 01:00 next day
|
||||
return t.quantity_from(midnight) == 3600.0 * s;
|
||||
auto tod = time_of_day(82800.0 * s, midnight); // 23:00
|
||||
tod += 7200.0 * s; // +2 h -> 01:00 next day
|
||||
return tod.quantity_from(midnight) == 3600.0 * s;
|
||||
}
|
||||
static_assert(time_of_day_wrap_arithmetic());
|
||||
|
||||
@@ -894,17 +894,17 @@ static_assert(time_of_day(-3'600'000.0 * time_of_day_qs[ms], midnight).quantity_
|
||||
|
||||
consteval bool time_of_day_hour_increment()
|
||||
{
|
||||
auto t = time_of_day(23.0 * time_of_day_qs[h], midnight); // 23:00 = 82800 s
|
||||
t += 2.0 * time_of_day_qs[h]; // +2 h → 25:00 = 90000 s → wraps to 3600 s
|
||||
return t.quantity_from(midnight) == 3600.0 * s;
|
||||
auto tod = time_of_day(23.0 * time_of_day_qs[h], midnight); // 23:00 = 82800 s
|
||||
tod += 2.0 * time_of_day_qs[h]; // +2 h → 25:00 = 90000 s → wraps to 3600 s
|
||||
return tod.quantity_from(midnight) == 3600.0 * s;
|
||||
}
|
||||
static_assert(time_of_day_hour_increment());
|
||||
|
||||
consteval bool time_of_day_minute_increment()
|
||||
{
|
||||
auto t = time_of_day(23.0 * time_of_day_qs[h] + 30.0 * time_of_day_qs[min], midnight); // 23:30 = 84600 s
|
||||
t += 45.0 * time_of_day_qs[min]; // +45 min → 24:15 = 87300 s → wraps to 900 s
|
||||
return t.quantity_from(midnight) == 900.0 * s;
|
||||
auto tod = time_of_day(23.0 * time_of_day_qs[h] + 30.0 * time_of_day_qs[min], midnight); // 23:30 = 84600 s
|
||||
tod += 45.0 * time_of_day_qs[min]; // +45 min → 24:15 = 87300 s → wraps to 900 s
|
||||
return tod.quantity_from(midnight) == 900.0 * s;
|
||||
}
|
||||
static_assert(time_of_day_minute_increment());
|
||||
|
||||
@@ -1079,9 +1079,9 @@ static_assert(time_of_day(-3 * 86400.0 * s + 82800.0 * s, midnight).quantity_fro
|
||||
// operator+=: start at 23:00 (82800 s), add 3 days + 2 hours (= 266400 s) → lands at 01:00 (3600 s).
|
||||
consteval bool time_of_day_multiday_assign()
|
||||
{
|
||||
auto t = time_of_day(82800.0 * s, midnight); // 23:00
|
||||
t += 3 * 86400.0 * s + 2 * 3600.0 * s; // +3 days 2 hours
|
||||
return t.quantity_from(midnight) == 3600.0 * s;
|
||||
auto tod = time_of_day(82800.0 * s, midnight); // 23:00
|
||||
tod += 3 * 86400.0 * s + 2 * 3600.0 * s; // +3 days 2 hours
|
||||
return tod.quantity_from(midnight) == 3600.0 * s;
|
||||
}
|
||||
static_assert(time_of_day_multiday_assign());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user