diff --git a/CHANGELOG.md b/CHANGELOG.md index ff3d5525913..11cd233788d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,6 +85,7 @@ - Dev: Refactored `MessageBuilder` to be a single class. (#5548) - Dev: Recent changes are now shown in the nightly release description. (#5553, #5554) - Dev: The timer for `StreamerMode` is now destroyed on the correct thread. (#5571) +- Dev: Cleanup some parts of the `magic_enum` adaptation for Qt. (#5587) ## 2.5.1 diff --git a/src/util/QMagicEnum.hpp b/src/util/QMagicEnum.hpp index 0325102e3b8..2e1d7a73063 100644 --- a/src/util/QMagicEnum.hpp +++ b/src/util/QMagicEnum.hpp @@ -4,23 +4,30 @@ #include #include +#include + namespace chatterino::qmagicenum::detail { -template -struct EnableIfEnum { -}; +template +concept DecaysTo = std::same_as, U>; -template -struct EnableIfEnum { - using type = R; -}; +template +concept IsEnum = std::is_enum_v; + +template +consteval bool isDefaultPredicate() noexcept +{ + return std::is_same_v, + std::equal_to> || + std::is_same_v, std::equal_to<>>; +} -template , - typename D = std::decay_t> -using enable_if_t = typename EnableIfEnum< - std::is_enum_v && - std::is_invocable_r_v, - R>::type; +template +consteval bool isNothrowInvocable() +{ + return isDefaultPredicate() || + std::is_nothrow_invocable_r_v; +} template consteval QStringView fromArray(const std::array &arr) @@ -43,12 +50,12 @@ consteval bool isLatin1(std::string_view maybe) } template -inline constexpr bool eq( +constexpr bool eq( QStringView a, QStringView b, [[maybe_unused]] BinaryPredicate && p) noexcept(magic_enum::detail::is_nothrow_invocable()) { - // Note: operator== isn't constexpr + // Note: QStringView::operator== isn't constexpr if (a.size() != b.size()) { return false; @@ -82,24 +89,22 @@ consteval auto enumNameStorage() return storage; } +/// This contains a std::array for each enum value (V) with the +/// corresponding name. template inline constexpr auto ENUM_NAME_STORAGE = enumNameStorage(); -template +template consteval auto namesStorage(std::index_sequence /*unused*/) { return std::array{{detail::fromArray( - ENUM_NAME_STORAGE()[I]>)...}}; + ENUM_NAME_STORAGE()[I]>)...}}; } -template > -inline constexpr auto NAMES_STORAGE = namesStorage( - std::make_index_sequence()>{}); - -template > -using NamesStorage = decltype((NAMES_STORAGE)); +/// This contains a std::array for each enum (E). +template +inline constexpr auto NAMES_STORAGE = + namesStorage(std::make_index_sequence()>{}); template > class CaseInsensitive @@ -112,16 +117,34 @@ class CaseInsensitive } public: - template - constexpr std::enable_if_t, QChar> && - std::is_same_v, QChar>, - bool> - operator()(L lhs, R rhs) const noexcept + template L, DecaysTo R> + constexpr bool operator()(L lhs, R rhs) const noexcept { return Op{}(toLower(lhs), toLower(rhs)); } }; +/// @brief Gets a static QString from @a view. +/// +/// @pre @a view must be a static string view (i.e. it must be valid throughout +/// the entire duration of the program). +/// +/// @param view The view to turn into a static string +/// @returns Qt6: A static string (never gets freed), Qt5: regular string +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +[[nodiscard]] inline QString staticString(QStringView view) noexcept +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + return QString(QStringPrivate(nullptr, const_cast(view.utf16()), + view.size())); +} +#else +[[nodiscard]] inline QString staticString(QStringView view) +{ + return view.toString(); +} +#endif + } // namespace chatterino::qmagicenum::detail namespace chatterino::qmagicenum { @@ -132,9 +155,8 @@ namespace chatterino::qmagicenum { /// /// @tparam V The enum value /// @returns The name as a string view -template -[[nodiscard]] consteval detail::enable_if_t - enumName() noexcept +template +[[nodiscard]] consteval QStringView enumName() noexcept { return QStringView{ detail::fromArray(detail::ENUM_NAME_STORAGE)}; @@ -145,41 +167,18 @@ template /// @param value The enum value /// @returns The name as a string view. If @a value does not have name or the /// value is out of range an empty string is returned. -template > -[[nodiscard]] constexpr detail::enable_if_t enumName( - E value) noexcept +template +[[nodiscard]] constexpr QStringView enumName(E value) noexcept { using D = std::decay_t; - if (const auto i = magic_enum::enum_index(value)) + if (const auto i = magic_enum::enum_index(value)) { - return detail::NAMES_STORAGE[*i]; + return detail::NAMES_STORAGE[*i]; } return {}; } -/// @brief Gets a static QString from @a view. -/// -/// @pre @a view must be a static string view (i.e. it must be valid throughout -/// the entire duration of the program). -/// -/// @param view The view to turn into a static string -/// @returns Qt6: A static string (never gets freed), Qt5: regular string -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) -[[nodiscard]] inline QString staticString(QStringView view) noexcept -{ - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) - return QString(QStringPrivate(nullptr, const_cast(view.utf16()), - view.size())); -} -#else -[[nodiscard]] inline QString staticString(QStringView view) -{ - return view.toString(); -} -#endif - /// @brief Get the name of an enum value /// /// This version is much lighter on the compile times and is not restricted to @@ -187,11 +186,10 @@ template -[[nodiscard]] inline detail::enable_if_t - enumNameString() noexcept +template +[[nodiscard]] inline QString enumNameString() noexcept { - return staticString(enumName()); + return detail::staticString(enumName()); } /// @brief Get the name of an enum value @@ -203,14 +201,12 @@ template /// @returns The name as a string. If @a value does not have name or the /// value is out of range an empty string is returned. /// The returned string is static. -template > -[[nodiscard]] inline detail::enable_if_t enumNameString( - E value) noexcept +template +[[nodiscard]] inline QString enumNameString(E value) noexcept { using D = std::decay_t; - return staticString(enumName(value)); + return detail::staticString(enumName(value)); } /// @brief Gets the enum value from a name @@ -221,29 +217,25 @@ template , - typename BinaryPredicate = std::equal_to<>> -[[nodiscard]] constexpr detail::enable_if_t>, - BinaryPredicate> - enumCast(QStringView name, - [[maybe_unused]] BinaryPredicate p = - {}) noexcept(magic_enum::detail:: - is_nothrow_invocable()) +template > +[[nodiscard]] constexpr std::optional> + enumCast(QStringView name, BinaryPredicate p = {}) noexcept( + detail::isNothrowInvocable()) + requires std::is_invocable_r_v { using D = std::decay_t; - if constexpr (magic_enum::enum_count() == 0) + if constexpr (magic_enum::enum_count() == 0) { static_cast(name); return std::nullopt; // Empty enum. } - for (std::size_t i = 0; i < magic_enum::enum_count(); i++) + for (std::size_t i = 0; i < magic_enum::enum_count(); i++) { - if (detail::eq(name, detail::NAMES_STORAGE[i], p)) + if (detail::eq(name, detail::NAMES_STORAGE[i], p)) { - return magic_enum::enum_value(i); + return magic_enum::enum_value(i); } } return std::nullopt; // Invalid value or out of range. @@ -256,22 +248,23 @@ template -[[nodiscard]] inline detail::enable_if_t enumFlagsName( - E flags, char16_t sep = u'|') +template +[[nodiscard]] inline QString enumFlagsName(E flags, char16_t sep = u'|') { using D = std::decay_t; using U = std::underlying_type_t; - constexpr auto S = magic_enum::detail::enum_subtype::flags; // NOLINT + static_assert(magic_enum::detail::subtype_v == + magic_enum::detail::enum_subtype::flags, + "enumFlagsName used for non-flags enum"); QString name; auto checkValue = U{0}; - for (std::size_t i = 0; i < magic_enum::enum_count(); ++i) + for (std::size_t i = 0; i < magic_enum::enum_count(); ++i) { - const auto v = static_cast(magic_enum::enum_value(i)); + const auto v = static_cast(magic_enum::enum_value(i)); if ((static_cast(flags) & v) != 0) { - const auto n = detail::NAMES_STORAGE[i]; + const auto n = detail::NAMES_STORAGE[i]; if (!n.empty()) { checkValue |= v; @@ -299,12 +292,10 @@ template /// /// @tparam E The enum type /// @returns A `std::array` of all names (`QStringView`s) -template > +template [[nodiscard]] constexpr auto enumNames() noexcept - -> detail::enable_if_t> { - return detail::NAMES_STORAGE, S>; + return detail::NAMES_STORAGE>; } /// Allows you to write qmagicenum::enumCast("bar", qmagicenum::CASE_INSENSITIVE) diff --git a/tests/src/QMagicEnum.cpp b/tests/src/QMagicEnum.cpp index 6778427fe43..17acc98635c 100644 --- a/tests/src/QMagicEnum.cpp +++ b/tests/src/QMagicEnum.cpp @@ -27,6 +27,8 @@ enum class MyFlag { Two = 2, Four = 4, Eight = 8, + TwoPow9 = 512, + TwoPow10 = 1024, }; using MyFlags = chatterino::FlagsEnum; @@ -130,7 +132,8 @@ TEST(QMagicEnum, flags) static_assert(checkConst(MyFlag::Eight, u"Eight")); static_assert(checkConst(MyFlag::Eight, u"Eight")); static_assert(eq(enumName(static_cast(16)), u"")); - static_assert(checkValues({u"One", u"Two", u"Four", u"Eight"})); + static_assert(checkValues( + {u"One", u"Two", u"Four", u"Eight", u"TwoPow9", u"TwoPow10"})); } TEST(QMagicEnum, enumNameString)