mirror of
https://github.com/Neargye/magic_enum.git
synced 2026-01-10 23:44:29 +00:00
Introduce mixed_sign_less()/min()/max() (#18)
Previously, there was mixed sign comparison: ``` reflected_min_v = -120 > 0U ? -120 : 0 ``` which returns -120 because the condition is true, since the compiler casts the signed value to unsigned, leading to a huge value. This caused the following values as result: ``` reflected_min_v == -120 min_v = -1 static_cast<U>(min_v) = 18446744073709551615 ``` The last one is used in magic_enum::enum_index().
This commit is contained in:
parent
1f3ea64407
commit
0f36cd5b0f
2 changed files with 114 additions and 6 deletions
|
|
@ -141,6 +141,34 @@ constexpr std::string_view pretty_name(std::string_view name) noexcept {
|
||||||
return {}; // Invalid name.
|
return {}; // Invalid name.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename L, typename R>
|
||||||
|
constexpr bool mixed_sign_less(L left, R right) noexcept {
|
||||||
|
static_assert(std::is_integral_v<L>, "L must be an integral value");
|
||||||
|
static_assert(std::is_integral_v<R>, "R must be an integral value");
|
||||||
|
if constexpr(std::is_signed_v<L> == std::is_signed_v<R>) {
|
||||||
|
// If same signedness (both signed or both unsigned)
|
||||||
|
return left < right;
|
||||||
|
}
|
||||||
|
else if constexpr(std::is_signed_v<L>) {
|
||||||
|
// if 'left' is negative, then result is 'true', otherwise cast & compare
|
||||||
|
return left < 0 || static_cast<std::make_unsigned_t<L>>(left) < right;
|
||||||
|
}
|
||||||
|
else { // std::is_signed_v<R>
|
||||||
|
// if 'right' is negative, then result is 'false', otherwise cast & compare
|
||||||
|
return right >= 0 && left < static_cast<std::make_unsigned_t<R>>(right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename L, typename R>
|
||||||
|
constexpr int mixed_sign_min_as_int(L left, R right) noexcept {
|
||||||
|
return mixed_sign_less(left, right) ? static_cast<int>(left) : static_cast<int>(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename L, typename R>
|
||||||
|
constexpr int mixed_sign_max_as_int(L left, R right) noexcept {
|
||||||
|
return mixed_sign_less(left, right) ? static_cast<int>(right) : static_cast<int>(left);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
constexpr auto n() noexcept {
|
constexpr auto n() noexcept {
|
||||||
static_assert(is_enum_v<E>, "magic_enum::detail::n requires enum type.");
|
static_assert(is_enum_v<E>, "magic_enum::detail::n requires enum type.");
|
||||||
|
|
@ -182,14 +210,10 @@ template <typename E, E V>
|
||||||
inline constexpr auto name_v = n<E, V>();
|
inline constexpr auto name_v = n<E, V>();
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
inline constexpr int reflected_min_v = static_cast<int>(enum_range<E>::min > (std::numeric_limits<std::underlying_type_t<E>>::min)()
|
inline constexpr int reflected_min_v = mixed_sign_max_as_int(enum_range<E>::min, std::numeric_limits<std::underlying_type_t<E>>::min());
|
||||||
? enum_range<E>::min
|
|
||||||
: (std::numeric_limits<std::underlying_type_t<E>>::min)());
|
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
inline constexpr int reflected_max_v = static_cast<int>(enum_range<E>::max < (std::numeric_limits<std::underlying_type_t<E>>::max)()
|
inline constexpr int reflected_max_v = mixed_sign_min_as_int(enum_range<E>::max, std::numeric_limits<std::underlying_type_t<E>>::max());
|
||||||
? enum_range<E>::max
|
|
||||||
: (std::numeric_limits<std::underlying_type_t<E>>::max)());
|
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
constexpr std::size_t reflected_size() {
|
constexpr std::size_t reflected_size() {
|
||||||
|
|
|
||||||
|
|
@ -469,3 +469,87 @@ TEST_CASE("enum_traits") {
|
||||||
REQUIRE(enum_traits<number>::type_name == "number");
|
REQUIRE(enum_traits<number>::type_name == "number");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("extrema") {
|
||||||
|
enum class BadColor : uint64_t
|
||||||
|
{
|
||||||
|
RED,
|
||||||
|
GREEN,
|
||||||
|
YELLOW,
|
||||||
|
// The value NONE is ignored (out of range).
|
||||||
|
// However, it affects the value of min_v. When reflected_min_v was incorrect, the
|
||||||
|
// presence of NONE caused miv_v to be equal to -1, which was then cast to unsigned,
|
||||||
|
// leading to a value of 18446744073709551615 (numeric_limit_max of uint64_t).
|
||||||
|
NONE = std::numeric_limits<uint64_t>::max(),
|
||||||
|
};
|
||||||
|
SECTION("min") {
|
||||||
|
REQUIRE(magic_enum::enum_range<BadColor>::min == MAGIC_ENUM_RANGE_MIN);
|
||||||
|
REQUIRE(magic_enum::detail::reflected_min_v<BadColor> == 0);
|
||||||
|
REQUIRE(magic_enum::detail::min_v<BadColor> == 0);
|
||||||
|
}
|
||||||
|
SECTION("max") {
|
||||||
|
REQUIRE(magic_enum::enum_range<BadColor>::max == MAGIC_ENUM_RANGE_MAX);
|
||||||
|
REQUIRE(magic_enum::detail::reflected_max_v<BadColor> == MAGIC_ENUM_RANGE_MAX);
|
||||||
|
REQUIRE(magic_enum::detail::max_v<BadColor> == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("mixed_sign_less") {
|
||||||
|
using magic_enum::detail::mixed_sign_less;
|
||||||
|
|
||||||
|
constexpr uint64_t uint64_t_min = std::numeric_limits<uint64_t>::min();
|
||||||
|
constexpr uint32_t uint32_t_min = std::numeric_limits<uint32_t>::min();
|
||||||
|
constexpr uint32_t uint32_t_max = std::numeric_limits<uint32_t>::max();
|
||||||
|
constexpr uint64_t uint64_t_max = std::numeric_limits<uint64_t>::max();
|
||||||
|
|
||||||
|
constexpr int64_t int64_t_min = std::numeric_limits<int64_t>::min();
|
||||||
|
constexpr int32_t int32_t_min = std::numeric_limits<int32_t>::min();
|
||||||
|
constexpr int32_t int32_t_max = std::numeric_limits<int32_t>::max();
|
||||||
|
constexpr int64_t int64_t_max = std::numeric_limits<int64_t>::max();
|
||||||
|
|
||||||
|
// Also testing with offset to avoid corner cases
|
||||||
|
// Two variables to avoid hidden casts
|
||||||
|
constexpr int64_t offset_int64_t = 17;
|
||||||
|
constexpr int32_t offset_int32_t = 17;
|
||||||
|
|
||||||
|
SECTION("same signedness") {
|
||||||
|
REQUIRE(mixed_sign_less(-5, -3));
|
||||||
|
REQUIRE(mixed_sign_less(27U, 49U));
|
||||||
|
}
|
||||||
|
SECTION("same signedness, different width") {
|
||||||
|
REQUIRE(mixed_sign_less(uint32_t_max, uint64_t_max));
|
||||||
|
REQUIRE(!mixed_sign_less(uint64_t_max, uint32_t_max));
|
||||||
|
REQUIRE(mixed_sign_less(int64_t_min, int32_t_min));
|
||||||
|
REQUIRE(!mixed_sign_less(int32_t_min, int64_t_min));
|
||||||
|
REQUIRE(mixed_sign_less(int64_t_min + offset_int64_t, int32_t_min + offset_int32_t));
|
||||||
|
REQUIRE(!mixed_sign_less(int32_t_min + offset_int32_t, int64_t_min + offset_int64_t));
|
||||||
|
}
|
||||||
|
SECTION("left signed, right unsigned") {
|
||||||
|
REQUIRE(mixed_sign_less(-5, 3U));
|
||||||
|
REQUIRE(mixed_sign_less(3, 5U));
|
||||||
|
}
|
||||||
|
SECTION("left signed, right unsigned, different width") {
|
||||||
|
REQUIRE(mixed_sign_less(int32_t_max, uint64_t_max));
|
||||||
|
REQUIRE(!mixed_sign_less(int64_t_max, uint32_t_max));
|
||||||
|
REQUIRE(mixed_sign_less(int32_t_min, uint64_t_min));
|
||||||
|
REQUIRE(mixed_sign_less(int64_t_min, uint32_t_min));
|
||||||
|
REQUIRE(mixed_sign_less(int32_t_max - offset_int32_t, uint64_t_max));
|
||||||
|
REQUIRE(!mixed_sign_less(int64_t_max - offset_int64_t, uint32_t_max));
|
||||||
|
REQUIRE(mixed_sign_less(int32_t_min + offset_int32_t, uint64_t_min));
|
||||||
|
REQUIRE(mixed_sign_less(int64_t_min + offset_int64_t, uint32_t_min));
|
||||||
|
}
|
||||||
|
SECTION("left unsigned, right signed") {
|
||||||
|
REQUIRE(!mixed_sign_less(3U, -5));
|
||||||
|
REQUIRE(mixed_sign_less(3U, 5));
|
||||||
|
}
|
||||||
|
SECTION("left unsigned, right signed, different width") {
|
||||||
|
REQUIRE(mixed_sign_less(uint32_t_max, int64_t_max));
|
||||||
|
REQUIRE(!mixed_sign_less(uint64_t_max, int32_t_max));
|
||||||
|
REQUIRE(!mixed_sign_less(uint32_t_min, int64_t_min));
|
||||||
|
REQUIRE(!mixed_sign_less(uint64_t_min, int32_t_min));
|
||||||
|
REQUIRE(mixed_sign_less(uint32_t_max, int64_t_max - offset_int32_t));
|
||||||
|
REQUIRE(!mixed_sign_less(uint64_t_max, int32_t_max - offset_int64_t));
|
||||||
|
REQUIRE(!mixed_sign_less(uint32_t_min, int64_t_min + offset_int32_t));
|
||||||
|
REQUIRE(!mixed_sign_less(uint64_t_min, int32_t_min + offset_int64_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue