chore: cleanup some parts of qmagicenum (#5587)

This commit is contained in:
nerix
2024-09-09 19:16:36 +02:00
committed by GitHub
parent 1240bd53b5
commit 2afa227139
3 changed files with 96 additions and 101 deletions
+1
View File
@@ -85,6 +85,7 @@
- Dev: Refactored `MessageBuilder` to be a single class. (#5548) - Dev: Refactored `MessageBuilder` to be a single class. (#5548)
- Dev: Recent changes are now shown in the nightly release description. (#5553, #5554) - 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: 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 ## 2.5.1
+91 -100
View File
@@ -4,23 +4,30 @@
#include <QString> #include <QString>
#include <QStringView> #include <QStringView>
#include <concepts>
namespace chatterino::qmagicenum::detail { namespace chatterino::qmagicenum::detail {
template <bool, typename R> template <typename T, typename U>
struct EnableIfEnum { concept DecaysTo = std::same_as<std::decay_t<T>, U>;
};
template <typename R> template <typename T>
struct EnableIfEnum<true, R> { concept IsEnum = std::is_enum_v<T>;
using type = R;
};
template <typename T, typename R, typename BinaryPredicate = std::equal_to<>, template <typename BinaryPredicate>
typename D = std::decay_t<T>> consteval bool isDefaultPredicate() noexcept
using enable_if_t = typename EnableIfEnum< {
std::is_enum_v<D> && return std::is_same_v<std::decay_t<BinaryPredicate>,
std::is_invocable_r_v<bool, BinaryPredicate, QChar, QChar>, std::equal_to<QChar>> ||
R>::type; std::is_same_v<std::decay_t<BinaryPredicate>, std::equal_to<>>;
}
template <typename BinaryPredicate>
consteval bool isNothrowInvocable()
{
return isDefaultPredicate<BinaryPredicate>() ||
std::is_nothrow_invocable_r_v<bool, BinaryPredicate, QChar, QChar>;
}
template <std::size_t N> template <std::size_t N>
consteval QStringView fromArray(const std::array<char16_t, N> &arr) consteval QStringView fromArray(const std::array<char16_t, N> &arr)
@@ -43,12 +50,12 @@ consteval bool isLatin1(std::string_view maybe)
} }
template <typename BinaryPredicate> template <typename BinaryPredicate>
inline constexpr bool eq( constexpr bool eq(
QStringView a, QStringView b, QStringView a, QStringView b,
[[maybe_unused]] BinaryPredicate && [[maybe_unused]] BinaryPredicate &&
p) noexcept(magic_enum::detail::is_nothrow_invocable<BinaryPredicate>()) p) noexcept(magic_enum::detail::is_nothrow_invocable<BinaryPredicate>())
{ {
// Note: operator== isn't constexpr // Note: QStringView::operator== isn't constexpr
if (a.size() != b.size()) if (a.size() != b.size())
{ {
return false; return false;
@@ -82,24 +89,22 @@ consteval auto enumNameStorage()
return storage; return storage;
} }
/// This contains a std::array<char16_t> for each enum value (V) with the
/// corresponding name.
template <typename E, E V> template <typename E, E V>
inline constexpr auto ENUM_NAME_STORAGE = enumNameStorage<char16_t, E, V>(); inline constexpr auto ENUM_NAME_STORAGE = enumNameStorage<char16_t, E, V>();
template <typename E, magic_enum::detail::enum_subtype S, std::size_t... I> template <typename E, std::size_t... I>
consteval auto namesStorage(std::index_sequence<I...> /*unused*/) consteval auto namesStorage(std::index_sequence<I...> /*unused*/)
{ {
return std::array<QStringView, sizeof...(I)>{{detail::fromArray( return std::array<QStringView, sizeof...(I)>{{detail::fromArray(
ENUM_NAME_STORAGE<E, magic_enum::enum_values<E, S>()[I]>)...}}; ENUM_NAME_STORAGE<E, magic_enum::enum_values<E>()[I]>)...}};
} }
template <typename E, /// This contains a std::array<QStringView> for each enum (E).
magic_enum::detail::enum_subtype S = magic_enum::detail::subtype_v<E>> template <typename E>
inline constexpr auto NAMES_STORAGE = namesStorage<E, S>( inline constexpr auto NAMES_STORAGE =
std::make_index_sequence<magic_enum::enum_count<E, S>()>{}); namesStorage<E>(std::make_index_sequence<magic_enum::enum_count<E>()>{});
template <typename E, magic_enum::detail::enum_subtype S,
typename D = std::decay_t<E>>
using NamesStorage = decltype((NAMES_STORAGE<D, S>));
template <typename Op = std::equal_to<>> template <typename Op = std::equal_to<>>
class CaseInsensitive class CaseInsensitive
@@ -112,53 +117,13 @@ class CaseInsensitive
} }
public: public:
template <typename L, typename R> template <DecaysTo<QChar> L, DecaysTo<QChar> R>
constexpr std::enable_if_t<std::is_same_v<std::decay_t<L>, QChar> && constexpr bool operator()(L lhs, R rhs) const noexcept
std::is_same_v<std::decay_t<R>, QChar>,
bool>
operator()(L lhs, R rhs) const noexcept
{ {
return Op{}(toLower(lhs), toLower(rhs)); return Op{}(toLower(lhs), toLower(rhs));
} }
}; };
} // namespace chatterino::qmagicenum::detail
namespace chatterino::qmagicenum {
/// @brief Get the name of an enum value
///
/// This version is much lighter on the compile times and is not restricted to the enum_range limitation.
///
/// @tparam V The enum value
/// @returns The name as a string view
template <auto V>
[[nodiscard]] consteval detail::enable_if_t<decltype(V), QStringView>
enumName() noexcept
{
return QStringView{
detail::fromArray(detail::ENUM_NAME_STORAGE<decltype(V), V>)};
}
/// @brief Get the name of an enum value
///
/// @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 <typename E,
magic_enum::detail::enum_subtype S = magic_enum::detail::subtype_v<E>>
[[nodiscard]] constexpr detail::enable_if_t<E, QStringView> enumName(
E value) noexcept
{
using D = std::decay_t<E>;
if (const auto i = magic_enum::enum_index<D, S>(value))
{
return detail::NAMES_STORAGE<D, S>[*i];
}
return {};
}
/// @brief Gets a static QString from @a view. /// @brief Gets a static QString from @a view.
/// ///
/// @pre @a view must be a static string view (i.e. it must be valid throughout /// @pre @a view must be a static string view (i.e. it must be valid throughout
@@ -180,6 +145,40 @@ template <typename E,
} }
#endif #endif
} // namespace chatterino::qmagicenum::detail
namespace chatterino::qmagicenum {
/// @brief Get the name of an enum value
///
/// This version is much lighter on the compile times and is not restricted to the enum_range limitation.
///
/// @tparam V The enum value
/// @returns The name as a string view
template <detail::IsEnum auto V>
[[nodiscard]] consteval QStringView enumName() noexcept
{
return QStringView{
detail::fromArray(detail::ENUM_NAME_STORAGE<decltype(V), V>)};
}
/// @brief Get the name of an enum value
///
/// @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 <detail::IsEnum E>
[[nodiscard]] constexpr QStringView enumName(E value) noexcept
{
using D = std::decay_t<E>;
if (const auto i = magic_enum::enum_index<D>(value))
{
return detail::NAMES_STORAGE<D>[*i];
}
return {};
}
/// @brief Get the name of an enum value /// @brief Get the name of an enum value
/// ///
/// This version is much lighter on the compile times and is not restricted to /// This version is much lighter on the compile times and is not restricted to
@@ -187,11 +186,10 @@ template <typename E,
/// ///
/// @tparam V The enum value /// @tparam V The enum value
/// @returns The name as a string. The returned string is static. /// @returns The name as a string. The returned string is static.
template <auto V> template <detail::IsEnum auto V>
[[nodiscard]] inline detail::enable_if_t<decltype(V), QString> [[nodiscard]] inline QString enumNameString() noexcept
enumNameString() noexcept
{ {
return staticString(enumName<V>()); return detail::staticString(enumName<V>());
} }
/// @brief Get the name of an enum value /// @brief Get the name of an enum value
@@ -203,14 +201,12 @@ template <auto V>
/// @returns The name as a string. If @a value does not have name or the /// @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. /// value is out of range an empty string is returned.
/// The returned string is static. /// The returned string is static.
template <typename E, template <detail::IsEnum E>
magic_enum::detail::enum_subtype S = magic_enum::detail::subtype_v<E>> [[nodiscard]] inline QString enumNameString(E value) noexcept
[[nodiscard]] inline detail::enable_if_t<E, QString> enumNameString(
E value) noexcept
{ {
using D = std::decay_t<E>; using D = std::decay_t<E>;
return staticString(enumName<D, S>(value)); return detail::staticString(enumName<D>(value));
} }
/// @brief Gets the enum value from a name /// @brief Gets the enum value from a name
@@ -221,29 +217,25 @@ template <typename E,
/// (defaults to std::equal_to) /// (defaults to std::equal_to)
/// @returns A `std::optional` of the parsed value. If no value was parsed, /// @returns A `std::optional` of the parsed value. If no value was parsed,
/// `std::nullopt` is returned. /// `std::nullopt` is returned.
template <typename E, template <detail::IsEnum E, typename BinaryPredicate = std::equal_to<>>
magic_enum::detail::enum_subtype S = magic_enum::detail::subtype_v<E>, [[nodiscard]] constexpr std::optional<std::decay_t<E>>
typename BinaryPredicate = std::equal_to<>> enumCast(QStringView name, BinaryPredicate p = {}) noexcept(
[[nodiscard]] constexpr detail::enable_if_t<E, std::optional<std::decay_t<E>>, detail::isNothrowInvocable<BinaryPredicate>())
BinaryPredicate> requires std::is_invocable_r_v<bool, BinaryPredicate, QChar, QChar>
enumCast(QStringView name,
[[maybe_unused]] BinaryPredicate p =
{}) noexcept(magic_enum::detail::
is_nothrow_invocable<BinaryPredicate>())
{ {
using D = std::decay_t<E>; using D = std::decay_t<E>;
if constexpr (magic_enum::enum_count<D, S>() == 0) if constexpr (magic_enum::enum_count<D>() == 0)
{ {
static_cast<void>(name); static_cast<void>(name);
return std::nullopt; // Empty enum. return std::nullopt; // Empty enum.
} }
for (std::size_t i = 0; i < magic_enum::enum_count<D, S>(); i++) for (std::size_t i = 0; i < magic_enum::enum_count<D>(); i++)
{ {
if (detail::eq(name, detail::NAMES_STORAGE<D, S>[i], p)) if (detail::eq(name, detail::NAMES_STORAGE<D>[i], p))
{ {
return magic_enum::enum_value<D, S>(i); return magic_enum::enum_value<D>(i);
} }
} }
return std::nullopt; // Invalid value or out of range. return std::nullopt; // Invalid value or out of range.
@@ -256,22 +248,23 @@ template <typename E,
/// @returns A string containing all names separated by @a sep. If any flag in /// @returns A string containing all names separated by @a sep. If any flag in
/// @a flags is out of rage or does not have a name, an empty string /// @a flags is out of rage or does not have a name, an empty string
/// is returned. /// is returned.
template <typename E> template <detail::IsEnum E>
[[nodiscard]] inline detail::enable_if_t<E, QString> enumFlagsName( [[nodiscard]] inline QString enumFlagsName(E flags, char16_t sep = u'|')
E flags, char16_t sep = u'|')
{ {
using D = std::decay_t<E>; using D = std::decay_t<E>;
using U = std::underlying_type_t<D>; using U = std::underlying_type_t<D>;
constexpr auto S = magic_enum::detail::enum_subtype::flags; // NOLINT static_assert(magic_enum::detail::subtype_v<E> ==
magic_enum::detail::enum_subtype::flags,
"enumFlagsName used for non-flags enum");
QString name; QString name;
auto checkValue = U{0}; auto checkValue = U{0};
for (std::size_t i = 0; i < magic_enum::enum_count<D, S>(); ++i) for (std::size_t i = 0; i < magic_enum::enum_count<D>(); ++i)
{ {
const auto v = static_cast<U>(magic_enum::enum_value<D, S>(i)); const auto v = static_cast<U>(magic_enum::enum_value<D>(i));
if ((static_cast<U>(flags) & v) != 0) if ((static_cast<U>(flags) & v) != 0)
{ {
const auto n = detail::NAMES_STORAGE<D, S>[i]; const auto n = detail::NAMES_STORAGE<D>[i];
if (!n.empty()) if (!n.empty())
{ {
checkValue |= v; checkValue |= v;
@@ -299,12 +292,10 @@ template <typename E>
/// ///
/// @tparam E The enum type /// @tparam E The enum type
/// @returns A `std::array` of all names (`QStringView`s) /// @returns A `std::array` of all names (`QStringView`s)
template <typename E, template <detail::IsEnum E>
magic_enum::detail::enum_subtype S = magic_enum::detail::subtype_v<E>>
[[nodiscard]] constexpr auto enumNames() noexcept [[nodiscard]] constexpr auto enumNames() noexcept
-> detail::enable_if_t<E, detail::NamesStorage<E, S>>
{ {
return detail::NAMES_STORAGE<std::decay_t<E>, S>; return detail::NAMES_STORAGE<std::decay_t<E>>;
} }
/// Allows you to write qmagicenum::enumCast<foo>("bar", qmagicenum::CASE_INSENSITIVE) /// Allows you to write qmagicenum::enumCast<foo>("bar", qmagicenum::CASE_INSENSITIVE)
+4 -1
View File
@@ -27,6 +27,8 @@ enum class MyFlag {
Two = 2, Two = 2,
Four = 4, Four = 4,
Eight = 8, Eight = 8,
TwoPow9 = 512,
TwoPow10 = 1024,
}; };
using MyFlags = chatterino::FlagsEnum<MyFlag>; using MyFlags = chatterino::FlagsEnum<MyFlag>;
@@ -130,7 +132,8 @@ TEST(QMagicEnum, flags)
static_assert(checkConst(MyFlag::Eight, u"Eight")); static_assert(checkConst(MyFlag::Eight, u"Eight"));
static_assert(checkConst(MyFlag::Eight, u"Eight")); static_assert(checkConst(MyFlag::Eight, u"Eight"));
static_assert(eq(enumName(static_cast<MyFlag>(16)), u"")); static_assert(eq(enumName(static_cast<MyFlag>(16)), u""));
static_assert(checkValues<MyFlag>({u"One", u"Two", u"Four", u"Eight"})); static_assert(checkValues<MyFlag>(
{u"One", u"Two", u"Four", u"Eight", u"TwoPow9", u"TwoPow10"}));
} }
TEST(QMagicEnum, enumNameString) TEST(QMagicEnum, enumNameString)