mirror of
https://github.com/Neargye/magic_enum.git
synced 2026-01-14 00:14:19 +00:00
add comparison predicate for enum_cast
This commit is contained in:
parent
a76480629c
commit
306b1d0704
2 changed files with 74 additions and 47 deletions
|
|
@ -131,6 +131,12 @@ struct static_string<0> {
|
|||
constexpr operator std::string_view() const noexcept { return {}; }
|
||||
};
|
||||
|
||||
struct char_equal {
|
||||
constexpr bool operator()(char lhs, char rhs) const noexcept {
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr std::string_view pretty_name(std::string_view name) noexcept {
|
||||
for (std::size_t i = name.size(); i > 0; --i) {
|
||||
if (!((name[i - 1] >= '0' && name[i - 1] <= '9') ||
|
||||
|
|
@ -151,19 +157,33 @@ constexpr std::string_view pretty_name(std::string_view name) noexcept {
|
|||
return {}; // Invalid name.
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr bool mixed_sign_less(L lhs, R rhs) noexcept {
|
||||
static_assert(std::is_integral_v<L> && std::is_integral_v<R>, "magic_enum::detail::mixed_sign_less requires integral type.");
|
||||
template <typename BinaryPredicate>
|
||||
constexpr bool cmp_equal(std::string_view lhs, std::string_view rhs, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>) {
|
||||
if (lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
const auto size = lhs.size();
|
||||
for (std::size_t i = 0; i < size; ++i) {
|
||||
if (!p(lhs[i], rhs[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if constexpr (std::is_signed_v<L> && std::is_unsigned_v<R>) {
|
||||
// If 'left' is negative, then result is 'true', otherwise cast & compare.
|
||||
return lhs < 0 || static_cast<std::make_unsigned_t<L>>(lhs) < rhs;
|
||||
} else if constexpr (std::is_unsigned_v<L> && std::is_signed_v<R>) {
|
||||
// If 'right' is negative, then result is 'false', otherwise cast & compare.
|
||||
return rhs >= 0 && lhs < static_cast<std::make_unsigned_t<R>>(rhs);
|
||||
} else {
|
||||
template <typename L, typename R>
|
||||
constexpr bool cmp_less(L lhs, R rhs) noexcept {
|
||||
static_assert(std::is_integral_v<L> && std::is_integral_v<R>, "magic_enum::detail::cmp_less requires integral type.");
|
||||
|
||||
if constexpr (std::is_signed_v<L> == std::is_signed_v<R>) {
|
||||
// If same signedness (both signed or both unsigned).
|
||||
return lhs < rhs;
|
||||
} else if constexpr (std::is_signed_v<R>) {
|
||||
// If 'right' is negative, then result is 'false', otherwise cast & compare.
|
||||
return rhs > 0 && lhs < static_cast<std::make_unsigned_t<R>>(rhs);
|
||||
} else {
|
||||
// If 'left' is negative, then result is 'true', otherwise cast & compare.
|
||||
return lhs < 0 || static_cast<std::make_unsigned_t<L>>(lhs) < rhs;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -212,7 +232,7 @@ constexpr int reflected_min() noexcept {
|
|||
static_assert(lhs > (std::numeric_limits<std::int16_t>::min)(), "magic_enum::enum_range requires min must be greater than INT16_MIN.");
|
||||
constexpr auto rhs = (std::numeric_limits<std::underlying_type_t<E>>::min)();
|
||||
|
||||
return mixed_sign_less(lhs, rhs) ? rhs : lhs;
|
||||
return cmp_less(lhs, rhs) ? rhs : lhs;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
|
|
@ -222,7 +242,7 @@ constexpr int reflected_max() noexcept {
|
|||
static_assert(lhs < (std::numeric_limits<std::int16_t>::max)(), "magic_enum::enum_range requires max must be less than INT16_MAX.");
|
||||
constexpr auto rhs = (std::numeric_limits<std::underlying_type_t<E>>::max)();
|
||||
|
||||
return mixed_sign_less(lhs, rhs) ? lhs : rhs;
|
||||
return cmp_less(lhs, rhs) ? lhs : rhs;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
|
|
@ -443,19 +463,20 @@ using enum_traits = detail::enum_traits<std::decay_t<E>>;
|
|||
|
||||
// Obtains enum value from enum string name.
|
||||
// Returns std::optional with enum value.
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_t<E, std::optional<std::decay_t<E>>> {
|
||||
template <typename E, typename BinaryPredicate>
|
||||
[[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>) -> detail::enable_if_enum_t<E, std::optional<std::decay_t<E>>> {
|
||||
static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_cast requires bool(char, char) invocable predicate.");
|
||||
using D = std::decay_t<E>;
|
||||
|
||||
if constexpr (detail::range_size_v<D> > detail::count_v<D> * 2) {
|
||||
for (std::size_t i = 0; i < enum_traits<D>::count; ++i) {
|
||||
if (value == enum_traits<D>::names[i]) {
|
||||
if (detail::cmp_equal(value, enum_traits<D>::names[i], p)) {
|
||||
return enum_traits<D>::values[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto i = detail::min_v<D>; i <= detail::max_v<D>; ++i) {
|
||||
if (value == enum_traits<D>::name(static_cast<D>(i))) {
|
||||
if (detail::cmp_equal(value, enum_traits<D>::name(static_cast<D>(i)), p)) {
|
||||
return static_cast<D>(i);
|
||||
}
|
||||
}
|
||||
|
|
@ -464,6 +485,11 @@ template <typename E>
|
|||
return std::nullopt; // Invalid value or out of range.
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
[[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept -> detail::enable_if_enum_t<E, std::optional<std::decay_t<E>>> {
|
||||
return enum_cast<E>(value, detail::char_equal{});
|
||||
}
|
||||
|
||||
// Obtains enum value from integer value.
|
||||
// Returns std::optional with enum value.
|
||||
template <typename E>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <magic_enum.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <string_view>
|
||||
#include <sstream>
|
||||
|
||||
|
|
@ -56,7 +57,7 @@ TEST_CASE("enum_cast") {
|
|||
constexpr auto cr = enum_cast<Color>("RED");
|
||||
REQUIRE(cr.value() == Color::RED);
|
||||
REQUIRE(enum_cast<Color&>("GREEN").value() == Color::GREEN);
|
||||
REQUIRE(enum_cast<Color>("BLUE").value() == Color::BLUE);
|
||||
REQUIRE(enum_cast<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == Color::BLUE);
|
||||
REQUIRE_FALSE(enum_cast<Color>("None").has_value());
|
||||
|
||||
constexpr auto no = enum_cast<Numbers>("one");
|
||||
|
|
@ -626,8 +627,8 @@ TEST_CASE("extrema") {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("mixed_sign_less") {
|
||||
using magic_enum::detail::mixed_sign_less;
|
||||
TEST_CASE("cmp_less") {
|
||||
using magic_enum::detail::cmp_less;
|
||||
|
||||
constexpr std::uint64_t uint64_t_min = std::numeric_limits<std::uint64_t>::min();
|
||||
constexpr std::uint32_t uint32_t_min = std::numeric_limits<std::uint32_t>::min();
|
||||
|
|
@ -645,48 +646,48 @@ TEST_CASE("mixed_sign_less") {
|
|||
constexpr std::int32_t offset_int32_t = 17;
|
||||
|
||||
SECTION("same signedness") {
|
||||
REQUIRE(mixed_sign_less(-5, -3));
|
||||
REQUIRE(mixed_sign_less(27U, 49U));
|
||||
REQUIRE(cmp_less(-5, -3));
|
||||
REQUIRE(cmp_less(27U, 49U));
|
||||
}
|
||||
|
||||
SECTION("same signedness, different width") {
|
||||
REQUIRE(mixed_sign_less(uint32_t_max, uint64_t_max));
|
||||
REQUIRE_FALSE(mixed_sign_less(uint64_t_max, uint32_t_max));
|
||||
REQUIRE(mixed_sign_less(int64_t_min, int32_t_min));
|
||||
REQUIRE_FALSE(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_FALSE(mixed_sign_less(int32_t_min + offset_int32_t, int64_t_min + offset_int64_t));
|
||||
REQUIRE(cmp_less(uint32_t_max, uint64_t_max));
|
||||
REQUIRE_FALSE(cmp_less(uint64_t_max, uint32_t_max));
|
||||
REQUIRE(cmp_less(int64_t_min, int32_t_min));
|
||||
REQUIRE_FALSE(cmp_less(int32_t_min, int64_t_min));
|
||||
REQUIRE(cmp_less(int64_t_min + offset_int64_t, int32_t_min + offset_int32_t));
|
||||
REQUIRE_FALSE(cmp_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));
|
||||
REQUIRE(cmp_less(-5, 3U));
|
||||
REQUIRE(cmp_less(3, 5U));
|
||||
}
|
||||
|
||||
SECTION("left signed, right unsigned, different width") {
|
||||
REQUIRE(mixed_sign_less(int32_t_max, uint64_t_max));
|
||||
REQUIRE_FALSE(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_FALSE(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));
|
||||
REQUIRE(cmp_less(int32_t_max, uint64_t_max));
|
||||
REQUIRE_FALSE(cmp_less(int64_t_max, uint32_t_max));
|
||||
REQUIRE(cmp_less(int32_t_min, uint64_t_min));
|
||||
REQUIRE(cmp_less(int64_t_min, uint32_t_min));
|
||||
REQUIRE(cmp_less(int32_t_max - offset_int32_t, uint64_t_max));
|
||||
REQUIRE_FALSE(cmp_less(int64_t_max - offset_int64_t, uint32_t_max));
|
||||
REQUIRE(cmp_less(int32_t_min + offset_int32_t, uint64_t_min));
|
||||
REQUIRE(cmp_less(int64_t_min + offset_int64_t, uint32_t_min));
|
||||
}
|
||||
|
||||
SECTION("left unsigned, right signed") {
|
||||
REQUIRE_FALSE(mixed_sign_less(3U, -5));
|
||||
REQUIRE(mixed_sign_less(3U, 5));
|
||||
REQUIRE_FALSE(cmp_less(3U, -5));
|
||||
REQUIRE(cmp_less(3U, 5));
|
||||
}
|
||||
|
||||
SECTION("left unsigned, right signed, different width") {
|
||||
REQUIRE(mixed_sign_less(uint32_t_max, int64_t_max));
|
||||
REQUIRE_FALSE(mixed_sign_less(uint64_t_max, int32_t_max));
|
||||
REQUIRE_FALSE(mixed_sign_less(uint32_t_min, int64_t_min));
|
||||
REQUIRE_FALSE(mixed_sign_less(uint64_t_min, int32_t_min));
|
||||
REQUIRE(mixed_sign_less(uint32_t_max, int64_t_max - offset_int32_t));
|
||||
REQUIRE_FALSE(mixed_sign_less(uint64_t_max, int32_t_max - offset_int64_t));
|
||||
REQUIRE_FALSE(mixed_sign_less(uint32_t_min, int64_t_min + offset_int32_t));
|
||||
REQUIRE_FALSE(mixed_sign_less(uint64_t_min, int32_t_min + offset_int64_t));
|
||||
REQUIRE(cmp_less(uint32_t_max, int64_t_max));
|
||||
REQUIRE_FALSE(cmp_less(uint64_t_max, int32_t_max));
|
||||
REQUIRE_FALSE(cmp_less(uint32_t_min, int64_t_min));
|
||||
REQUIRE_FALSE(cmp_less(uint64_t_min, int32_t_min));
|
||||
REQUIRE(cmp_less(uint32_t_max, int64_t_max - offset_int32_t));
|
||||
REQUIRE_FALSE(cmp_less(uint64_t_max, int32_t_max - offset_int64_t));
|
||||
REQUIRE_FALSE(cmp_less(uint32_t_min, int64_t_min + offset_int32_t));
|
||||
REQUIRE_FALSE(cmp_less(uint64_t_min, int32_t_min + offset_int64_t));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue