From 2ec43969d87016703aa0acbaff06cc4d8018a815 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 20 Feb 2025 16:34:09 +0800 Subject: [PATCH 1/4] More reliable value validness detection towards CWG1766 (#398) CWG1766 made out-of-range cast to enumeration without fixed underlying type raise undefined behavior. Such UB arguably also applies to `bit_cast`, although it's not required that UB in `bit_cast` makes the expression non-constant. Currently, only Clang has implemented CWG1766, while Clang's underlying `__builtin_bit_cast` happens to be a workaround. However, it's more reliable to me to rely on the guarantee that core language UB causes constant evaluation failure. The approach in this patch effectively detects whether `std::integral_constant(V)>` is a valid type, which is equivalent to whether `static_cast(V)` is a constant expression. When the answer is `false`, value `V` can't be an enumerator of `E`. --- include/magic_enum/magic_enum.hpp | 41 ++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/include/magic_enum/magic_enum.hpp b/include/magic_enum/magic_enum.hpp index 158207e..7491fae 100644 --- a/include/magic_enum/magic_enum.hpp +++ b/include/magic_enum/magic_enum.hpp @@ -621,26 +621,37 @@ constexpr auto enum_name() noexcept { template inline constexpr auto enum_name_v = enum_name(); +// CWG1766: Values outside the range of the values of an enumeration +// https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 +#if defined(__clang__) && __clang_major__ >= 16 +template +inline constexpr bool is_enum_constexpr_static_cast_valid = false; +template +inline constexpr bool is_enum_constexpr_static_cast_valid(V)>>> = true; +#else +template +inline constexpr bool is_enum_constexpr_static_cast_valid = true; +#endif + template constexpr bool is_valid() noexcept { -#if defined(__clang__) && __clang_major__ >= 16 - // https://reviews.llvm.org/D130058, https://reviews.llvm.org/D131307 - constexpr E v = __builtin_bit_cast(E, V); -#else - constexpr E v = static_cast(V); -#endif - [[maybe_unused]] constexpr auto custom = customize::enum_name(v); - static_assert(std::is_same_v, customize::customize_t>, "magic_enum::customize requires customize_t type."); - if constexpr (custom.first == customize::detail::customize_tag::custom_tag) { - constexpr auto name = custom.second; - static_assert(!name.empty(), "magic_enum::customize requires not empty string."); - return name.size() != 0; - } else if constexpr (custom.first == customize::detail::customize_tag::default_tag) { + if constexpr (is_enum_constexpr_static_cast_valid) { + constexpr E v = static_cast(V); + [[maybe_unused]] constexpr auto custom = customize::enum_name(v); + static_assert(std::is_same_v, customize::customize_t>, "magic_enum::customize requires customize_t type."); + if constexpr (custom.first == customize::detail::customize_tag::custom_tag) { + constexpr auto name = custom.second; + static_assert(!name.empty(), "magic_enum::customize requires not empty string."); + return name.size() != 0; + } else if constexpr (custom.first == customize::detail::customize_tag::default_tag) { #if defined(MAGIC_ENUM_VS_2017_WORKAROUND) - return n().size_ != 0; + return n().size_ != 0; #else - return n().size_ != 0; + return n().size_ != 0; #endif + } else { + return false; + } } else { return false; } From 61a4f2058187f2f6bdea5dfcca27be1923a39da4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Feb 2025 12:36:05 +0400 Subject: [PATCH 2/4] Bump the github-actions group across 1 directory with 2 updates (#399) --- .github/workflows/scorecard.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 13d77a7..32f223d 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -47,7 +47,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: SARIF file path: results.sarif @@ -55,6 +55,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 + uses: github/codeql-action/upload-sarif@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8 with: sarif_file: results.sarif From ff6e5dd1c82a0c8cf8f774759933cc0a353fa5f9 Mon Sep 17 00:00:00 2001 From: Daniil Goncharov Date: Thu, 20 Feb 2025 12:37:47 +0400 Subject: [PATCH 3/4] fix 379 (#382) --- include/magic_enum/magic_enum_format.hpp | 76 +++++++----------------- test/test.cpp | 1 + 2 files changed, 24 insertions(+), 53 deletions(-) diff --git a/include/magic_enum/magic_enum_format.hpp b/include/magic_enum/magic_enum_format.hpp index 4997589..eadceec 100644 --- a/include/magic_enum/magic_enum_format.hpp +++ b/include/magic_enum/magic_enum_format.hpp @@ -35,44 +35,35 @@ #include "magic_enum.hpp" #include "magic_enum_flags.hpp" -#if !defined(MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT) -# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT 1 -# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE -#endif +namespace magic_enum::detail { -namespace magic_enum::customize { - // customize enum to enable/disable automatic std::format - template - constexpr bool enum_format_enabled() noexcept { - return MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT; +template >, int> = 0> +std::string format_as(E e) { + using D = std::decay_t; + static_assert(std::is_same_v, "magic_enum::formatter requires string_view::value_type type same as char."); + if constexpr (magic_enum::detail::supported::value) { + if constexpr (magic_enum::detail::subtype_v == magic_enum::detail::enum_subtype::flags) { + if (const auto name = magic_enum::enum_flags_name(e); !name.empty()) { + return {name.data(), name.size()}; + } + } else { + if (const auto name = magic_enum::enum_name(e); !name.empty()) { + return {name.data(), name.size()}; + } + } } -} // magic_enum::customize + return std::to_string(magic_enum::enum_integer(e)); +} + +} // namespace magic_enum::format #if defined(__cpp_lib_format) -#ifndef MAGIC_ENUM_USE_STD_MODULE -#include -#endif - template -struct std::formatter> && magic_enum::customize::enum_format_enabled(), char>> : std::formatter { +struct std::formatter>, char>> : std::formatter { template auto format(E e, FormatContext& ctx) const { - static_assert(std::is_same_v, "formatter requires string_view::value_type type same as char."); - using D = std::decay_t; - - if constexpr (magic_enum::detail::supported::value) { - if constexpr (magic_enum::detail::subtype_v == magic_enum::detail::enum_subtype::flags) { - if (const auto name = magic_enum::enum_flags_name(e); !name.empty()) { - return formatter::format(std::string_view{name.data(), name.size()}, ctx); - } - } else { - if (const auto name = magic_enum::enum_name(e); !name.empty()) { - return formatter::format(std::string_view{name.data(), name.size()}, ctx); - } - } - } - return formatter::format(std::to_string(magic_enum::enum_integer(e)), ctx); + return std::formatter::format(magic_enum::detail::format_as(e), ctx); } }; @@ -80,35 +71,14 @@ struct std::formatter> && mag #if defined(FMT_VERSION) -#include - template -struct fmt::formatter> && magic_enum::customize::enum_format_enabled(), char>> : fmt::formatter { +struct fmt::formatter>, char>> : fmt::formatter { template auto format(E e, FormatContext& ctx) const { - static_assert(std::is_same_v, "formatter requires string_view::value_type type same as char."); - using D = std::decay_t; - - if constexpr (magic_enum::detail::supported::value) { - if constexpr (magic_enum::detail::subtype_v == magic_enum::detail::enum_subtype::flags) { - if (const auto name = magic_enum::enum_flags_name(e); !name.empty()) { - return formatter::format(std::string_view{name.data(), name.size()}, ctx); - } - } else { - if (const auto name = magic_enum::enum_name(e); !name.empty()) { - return formatter::format(std::string_view{name.data(), name.size()}, ctx); - } - } - } - return formatter::format(std::to_string(magic_enum::enum_integer(e)), ctx); + return fmt::formatter::format(magic_enum::detail::format_as(e), ctx); } }; #endif -#if defined(MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE) -# undef MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT -# undef MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE -#endif - #endif // NEARGYE_MAGIC_ENUM_FORMAT_HPP diff --git a/test/test.cpp b/test/test.cpp index ef0459d..9d26eab 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1176,6 +1176,7 @@ TEST_CASE("multdimensional-switch-case") { #if defined(__cpp_lib_format) +#include #include TEST_CASE("format-base") { From a413fcc9c46a020a746907136a384c227f3cd095 Mon Sep 17 00:00:00 2001 From: Thomas Khyn Date: Thu, 20 Feb 2025 21:38:17 +1300 Subject: [PATCH 4/4] fix: use `inline` instead of `static` for constexpr in header file (#401) Using `static constexpr` in a header file does not seem to be correct - see [1] - and generates a bug when building the magic_enum module with GCC 15 [1] https://isocpp.org/blog/2018/05/quick-q-use-of-constexpr-in-header-file --- include/magic_enum/magic_enum_containers.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/magic_enum/magic_enum_containers.hpp b/include/magic_enum/magic_enum_containers.hpp index f817306..9a0208a 100644 --- a/include/magic_enum/magic_enum_containers.hpp +++ b/include/magic_enum/magic_enum_containers.hpp @@ -52,10 +52,10 @@ namespace magic_enum::containers { namespace detail { template -static constexpr bool is_transparent_v{}; +inline constexpr bool is_transparent_v{}; template -static constexpr bool is_transparent_v>{true}; +inline constexpr bool is_transparent_v>{true}; template , typename T1, typename T2> constexpr bool equal(T1&& t1, T2&& t2, Eq&& eq = {}) {