From adbdabd67dd1a3e293113860814b7203700dab1f Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Wed, 7 Oct 2026 15:26:39 -0600 Subject: [PATCH] :art: Allow ct_strings in type_map Problem: - `type_map` does not easily allow pairs containing `ct_string`. This doesn't work: ```cpp using M = type_map, vt_pair<"B", Y>>; ``` - `type_map` returns `void` or `0` on a miss, either of which might be valid entries. Solution: - Allow `ct_string`s to be used as keys, values and defaults in `type_map`. - On a miss, return a `missing_t` type (or value of that type) by default. Note: - This is a minor breaking change: downstream functions that check for `void` on a map lookup miss need to update. e.g. https://github.com/intel/compile-time-init-build/blob/main/include/interrupt/manager.hpp#L68 --- include/stdx/ct_string.hpp | 6 +++ include/stdx/utility.hpp | 79 +++++++++++++++++----------- test/type_map.cpp | 104 ++++++++++++++++++++++++++++++++++--- 3 files changed, 152 insertions(+), 37 deletions(-) diff --git a/include/stdx/ct_string.hpp b/include/stdx/ct_string.hpp index 9dff838..ce9bff3 100644 --- a/include/stdx/ct_string.hpp +++ b/include/stdx/ct_string.hpp @@ -236,5 +236,11 @@ template consteval auto name_for() { template consteval auto constant_name_for() { return cts_t()>{}; } + +namespace detail { +template +// NOLINTNEXTLINE(*-avoid-c-arrays) +map_value_helper(char const (&)[N]) -> map_value_helper>; +} // namespace detail } // namespace v1 } // namespace stdx diff --git a/include/stdx/utility.hpp b/include/stdx/utility.hpp index 19636af..d2f9ad5 100644 --- a/include/stdx/utility.hpp +++ b/include/stdx/utility.hpp @@ -24,7 +24,14 @@ template overload(Fs...) -> overload; [[noreturn]] inline auto unreachable() -> void { __builtin_unreachable(); } namespace detail { -template struct value_t { +template struct map_value_helper { + consteval explicit(false) map_value_helper(auto t) : value(t) {} + + T value; +}; +template map_value_helper(T) -> map_value_helper; + +template struct map_value_t { constexpr static inline auto value = V; }; @@ -38,10 +45,13 @@ template struct type_pair { using value_type = V; }; template using tt_pair = type_pair; -template using vt_pair = tt_pair, V>; -template using tv_pair = tt_pair>; -template -using vv_pair = tt_pair, detail::value_t>; +template +using vt_pair = tt_pair, V>; +template +using tv_pair = tt_pair>; +template +using vv_pair = + tt_pair, detail::map_value_t>; template using type_map = shrink_t>; @@ -57,50 +67,61 @@ template constexpr static auto reverse_lookup(type_pair) -> K; } // namespace detail -template +struct missing_t { + friend constexpr auto operator==(missing_t const &, missing_t const &) + -> bool = default; +}; +constexpr inline auto missing = missing_t{}; + +template using type_lookup_t = decltype(detail::lookup(expand())); -template +template using reverse_type_lookup_t = decltype(detail::reverse_lookup(expand())); -template +template using value_lookup_t = - decltype(detail::lookup, Default>(expand())); + decltype(detail::lookup, Default>( + expand())); -template +template using reverse_value_lookup_t = - decltype(detail::reverse_lookup, Default>(expand())); + decltype(detail::reverse_lookup, Default>( + expand())); namespace detail { template -using is_not_void = std::bool_constant>; +using is_present = std::bool_constant>; } -template +template constexpr static auto type_lookup_v = - type_or_t(expand())), - detail::value_t>::value; + type_or_t(expand())), + detail::map_value_t>::value; -template +template constexpr static auto reverse_type_lookup_v = - type_or_t(expand())), - detail::value_t>::value; + type_or_t(expand())), + detail::map_value_t>::value; -template +template constexpr static auto value_lookup_v = - type_or_t, void>(expand())), - detail::value_t>::value; + type_or_t, missing_t>( + expand())), + detail::map_value_t>::value; -template +template constexpr static auto reverse_value_lookup_v = - type_or_t, void>( - expand())), - detail::value_t>::value; + type_or_t, + missing_t>(expand())), + detail::map_value_t>::value; template [[nodiscard]] constexpr auto forward_like(U &&u) noexcept -> decltype(auto) { diff --git a/test/type_map.cpp b/test/type_map.cpp index abe36f1..a4773df 100644 --- a/test/type_map.cpp +++ b/test/type_map.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -18,7 +19,7 @@ TEST_CASE("look up type in map", "[type map]") { TEST_CASE("look up type not in map", "[type map]") { using M = stdx::type_map, stdx::type_pair>; - STATIC_REQUIRE(std::is_same_v, void>); + STATIC_REQUIRE(std::is_same_v, stdx::missing_t>); STATIC_REQUIRE(std::is_same_v, int>); } @@ -30,10 +31,23 @@ TEST_CASE("look up type in map (by value)", "[type map]") { TEST_CASE("look up type not in map (by value)", "[type map]") { using M = stdx::type_map, stdx::vt_pair<1, Y>>; - STATIC_REQUIRE(std::is_same_v, void>); + STATIC_REQUIRE(std::is_same_v, stdx::missing_t>); STATIC_REQUIRE(std::is_same_v, int>); } +TEST_CASE("look up type in map (by string value)", "[type map]") { + using M = stdx::type_map, stdx::vt_pair<"B", Y>>; + STATIC_REQUIRE(std::is_same_v, X>); + STATIC_REQUIRE(std::is_same_v, Y>); +} + +TEST_CASE("look up type not in map (by string value)", "[type map]") { + using M = stdx::type_map, stdx::vt_pair<"B", Y>>; + STATIC_REQUIRE( + std::is_same_v, stdx::missing_t>); + STATIC_REQUIRE(std::is_same_v, int>); +} + TEST_CASE("look up value in map (by type)", "[type map]") { using M = stdx::type_map, stdx::tv_pair>; STATIC_REQUIRE(stdx::type_lookup_v == 0); @@ -42,10 +56,24 @@ TEST_CASE("look up value in map (by type)", "[type map]") { TEST_CASE("look up value not in map (by type)", "[type map]") { using M = stdx::type_map, stdx::tv_pair>; - STATIC_REQUIRE(stdx::type_lookup_v == 0); + STATIC_REQUIRE(stdx::type_lookup_v == stdx::missing); STATIC_REQUIRE(stdx::type_lookup_v == 2); } +TEST_CASE("look up string value in map (by type)", "[type map]") { + using namespace stdx::literals; + using M = stdx::type_map, stdx::tv_pair>; + STATIC_REQUIRE(stdx::type_lookup_v == "X"_cts); + STATIC_REQUIRE(stdx::type_lookup_v == "Y"_cts); +} + +TEST_CASE("look up string value not in map (by type)", "[type map]") { + using namespace stdx::literals; + using M = stdx::type_map, stdx::tv_pair>; + STATIC_REQUIRE(stdx::type_lookup_v == stdx::missing); + STATIC_REQUIRE(stdx::type_lookup_v == "Z"_cts); +} + TEST_CASE("look up value in map (by value)", "[type map]") { using M = stdx::type_map, stdx::vv_pair<1, 11>>; STATIC_REQUIRE(stdx::value_lookup_v == 10); @@ -54,10 +82,24 @@ TEST_CASE("look up value in map (by value)", "[type map]") { TEST_CASE("look up value not in map (by value)", "[type map]") { using M = stdx::type_map, stdx::vv_pair<1, 11>>; - STATIC_REQUIRE(stdx::value_lookup_v == 0); + STATIC_REQUIRE(stdx::value_lookup_v == stdx::missing); STATIC_REQUIRE(stdx::value_lookup_v == 3); } +TEST_CASE("look up string value in map (by string value)", "[type map]") { + using namespace stdx::literals; + using M = stdx::type_map, stdx::vv_pair<"B", "Y">>; + STATIC_REQUIRE(stdx::value_lookup_v == "X"_cts); + STATIC_REQUIRE(stdx::value_lookup_v == "Y"_cts); +} + +TEST_CASE("look up string value not in map (by string value)", "[type map]") { + using namespace stdx::literals; + using M = stdx::type_map, stdx::vv_pair<"B", "Y">>; + STATIC_REQUIRE(stdx::value_lookup_v == stdx::missing); + STATIC_REQUIRE(stdx::value_lookup_v == "Z"_cts); +} + TEST_CASE("reverse look up type in map", "[type map]") { using M = stdx::type_map, stdx::type_pair>; STATIC_REQUIRE(std::is_same_v, A>); @@ -66,7 +108,8 @@ TEST_CASE("reverse look up type in map", "[type map]") { TEST_CASE("reverse look up type not in map", "[type map]") { using M = stdx::type_map, stdx::type_pair>; - STATIC_REQUIRE(std::is_same_v, void>); + STATIC_REQUIRE( + std::is_same_v, stdx::missing_t>); STATIC_REQUIRE(std::is_same_v, int>); } @@ -78,11 +121,26 @@ TEST_CASE("reverse look up type in map (by value)", "[type map]") { TEST_CASE("reverse look up type not in map (by value)", "[type map]") { using M = stdx::type_map, stdx::tv_pair>; - STATIC_REQUIRE(std::is_same_v, void>); + STATIC_REQUIRE( + std::is_same_v, stdx::missing_t>); STATIC_REQUIRE( std::is_same_v, int>); } +TEST_CASE("reverse look up type in map (by string value)", "[type map]") { + using M = stdx::type_map, stdx::tv_pair>; + STATIC_REQUIRE(std::is_same_v, X>); + STATIC_REQUIRE(std::is_same_v, Y>); +} + +TEST_CASE("reverse look up type not in map (by string value)", "[type map]") { + using M = stdx::type_map, stdx::tv_pair>; + STATIC_REQUIRE( + std::is_same_v, stdx::missing_t>); + STATIC_REQUIRE( + std::is_same_v, int>); +} + TEST_CASE("reverse look up value in map (by type)", "[type map]") { using M = stdx::type_map, stdx::vt_pair<1, B>>; STATIC_REQUIRE(stdx::reverse_type_lookup_v == 0); @@ -91,10 +149,24 @@ TEST_CASE("reverse look up value in map (by type)", "[type map]") { TEST_CASE("reverse look up value not in map (by type)", "[type map]") { using M = stdx::type_map, stdx::vt_pair<1, B>>; - STATIC_REQUIRE(stdx::reverse_type_lookup_v == 0); + STATIC_REQUIRE(stdx::reverse_type_lookup_v == stdx::missing); STATIC_REQUIRE(stdx::reverse_type_lookup_v == 2); } +TEST_CASE("reverse look up string value in map (by type)", "[type map]") { + using namespace stdx::literals; + using M = stdx::type_map, stdx::vt_pair<"B", B>>; + STATIC_REQUIRE(stdx::reverse_type_lookup_v == "A"_cts); + STATIC_REQUIRE(stdx::reverse_type_lookup_v == "B"_cts); +} + +TEST_CASE("reverse look up string value not in map (by type)", "[type map]") { + using namespace stdx::literals; + using M = stdx::type_map, stdx::vt_pair<"B", B>>; + STATIC_REQUIRE(stdx::reverse_type_lookup_v == stdx::missing); + STATIC_REQUIRE(stdx::reverse_type_lookup_v == "C"_cts); +} + TEST_CASE("reverse look up value in map (by value)", "[type map]") { using M = stdx::type_map, stdx::vv_pair<1, 11>>; STATIC_REQUIRE(stdx::reverse_value_lookup_v == 0); @@ -103,6 +175,22 @@ TEST_CASE("reverse look up value in map (by value)", "[type map]") { TEST_CASE("reverse look up value not in map (by value)", "[type map]") { using M = stdx::type_map, stdx::vv_pair<1, 11>>; - STATIC_REQUIRE(stdx::reverse_value_lookup_v == 0); + STATIC_REQUIRE(stdx::reverse_value_lookup_v == stdx::missing); STATIC_REQUIRE(stdx::reverse_value_lookup_v == 3); } + +TEST_CASE("reverse look up string value in map (by string value)", + "[type map]") { + using namespace stdx::literals; + using M = stdx::type_map, stdx::vv_pair<"Y", "B">>; + STATIC_REQUIRE(stdx::reverse_value_lookup_v == "X"_cts); + STATIC_REQUIRE(stdx::reverse_value_lookup_v == "Y"_cts); +} + +TEST_CASE("reverse look up string value not in map (by string value)", + "[type map]") { + using namespace stdx::literals; + using M = stdx::type_map, stdx::vv_pair<"Y", "B">>; + STATIC_REQUIRE(stdx::reverse_value_lookup_v == stdx::missing); + STATIC_REQUIRE(stdx::reverse_value_lookup_v == "Z"_cts); +}