From 1a59d4116c58c1b769283a7c1033d0e94086754b Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Thu, 22 Oct 2026 22:10:36 -0600 Subject: [PATCH] :art: Improvements to naming Problem: - Naming utilities do not deal with shrunken names as well as they might. Solution: - Deal with a shrunken name in `with_name` and friends. - Minimize type name length of the lambda representing a shrunk thing: move it to the stdx namespace. - Add tests to ensure idempotence of shrinking. --- include/stdx/ct_string.hpp | 22 ++++++++++-------- include/stdx/type_traits.hpp | 45 +++++++++++++++++++++++++----------- test/ct_string.cpp | 13 +++++++++++ test/type_traits.cpp | 22 ++++++++++++++++++ 4 files changed, 79 insertions(+), 23 deletions(-) diff --git a/include/stdx/ct_string.hpp b/include/stdx/ct_string.hpp index 8bb42bc..03e6e5d 100644 --- a/include/stdx/ct_string.hpp +++ b/include/stdx/ct_string.hpp @@ -184,16 +184,25 @@ template consteval auto operator""_ctst() { return cts_t{}; } } // namespace ct_string_literals } // namespace literals -template struct with_name { - constexpr static auto name = Name; +template consteval auto shrink() -> decltype(shrink_impl()) { + return {}; +} + +namespace detail { +template struct with_name { + constexpr static auto name = expand(Name); friend constexpr auto operator==(with_name, with_name) -> bool = default; }; -namespace detail { template concept is_ct_string = stdx::is_value_specialization_of_v, ct_string>; -} +} // namespace detail + +template using with_name = detail::with_name()>; +template + requires detail::is_shrunk_value +using with_shrunk_name = detail::with_name; template concept named = requires { @@ -242,10 +251,5 @@ template // NOLINTNEXTLINE(*-avoid-c-arrays) map_value_helper(char const (&)[N]) -> map_value_helper>; } // namespace detail - -template -consteval auto shrink() -> decltype(detail::shrink()) { - return {}; -} } // namespace v1 } // namespace stdx diff --git a/include/stdx/type_traits.hpp b/include/stdx/type_traits.hpp index 99ed0fc..967add8 100644 --- a/include/stdx/type_traits.hpp +++ b/include/stdx/type_traits.hpp @@ -234,18 +234,6 @@ template concept is_shrinkwrapped_value = requires { typename T::is_shrinkwrapped_value; }; -template auto (*)() -> shrinkwrap { return nullptr; }> -consteval auto shrink() { - return F; -} - -template auto (*)() -> shrinkwrap_value { return nullptr; }> -consteval auto shrink() { - return F; -} - template concept is_shrunk = requires(T const &t) { { t()() } -> is_shrinkwrapped; @@ -256,6 +244,35 @@ concept is_shrunk_value = requires(T const &t) { { t()() } -> is_shrinkwrapped_value; }; +} // namespace detail +} // namespace v1 +} // namespace stdx + +namespace stdx { +template auto (*)() -> detail::shrinkwrap { + return nullptr; + }> +consteval auto shrink_impl() { + if constexpr (detail::is_shrunk or detail::is_shrunk_value) { + return T{}; + } else { + return F; + } +} + +template auto (*)() -> detail::shrinkwrap_value { + return nullptr; + }> +consteval auto shrink_impl() { + return F; +} +} // namespace stdx + +namespace stdx { +inline namespace v1 { +namespace detail { template consteval auto maybe_expand() -> T; template consteval auto maybe_expand() -> typename decltype(T{}()())::type; @@ -282,13 +299,13 @@ template constexpr auto maybe_expand(T &&) { } } // namespace detail -template consteval auto shrink() -> decltype(detail::shrink()) { +template consteval auto shrink() -> decltype(shrink_impl()) { static_assert( always_false_v, "shrink() should not be called outside an unevaluated context"); } template -consteval auto shrink(T const &) -> decltype(detail::shrink()) { +consteval auto shrink(T const &) -> decltype(shrink_impl()) { return {}; } diff --git a/test/ct_string.cpp b/test/ct_string.cpp index 079a6b7..92c76b5 100644 --- a/test/ct_string.cpp +++ b/test/ct_string.cpp @@ -213,6 +213,19 @@ TEST_CASE("named object", "[ct_string]") { STATIC_CHECK(not stdx::same_name_q::template fn::value); } +TEST_CASE("named object with shrunk name", "[ct_string]") { + using namespace stdx::ct_string_literals; + constexpr auto n = stdx::shrink<"test">(); + using T = stdx::with_shrunk_name; + STATIC_CHECK(stdx::named); + STATIC_CHECK(stdx::name_of_v == "test"_cts); + STATIC_CHECK(stdx::constant_name_of_v == "test"_ctst); + + STATIC_CHECK(stdx::matching_name); + STATIC_CHECK(stdx::matching_name_t::value); + STATIC_CHECK(stdx::matching_name_q<"test">::template fn::value); +} + TEST_CASE("name or type fallback", "[ct_string]") { using namespace stdx::ct_string_literals; using T = stdx::with_name<"test">; diff --git a/test/type_traits.cpp b/test/type_traits.cpp index ab6ee4e..b51c6fd 100644 --- a/test/type_traits.cpp +++ b/test/type_traits.cpp @@ -245,6 +245,12 @@ TEST_CASE("type shrinkage (by type)", "[type_traits]") { STATIC_CHECK(std::is_same_v, C>); } +TEST_CASE("type shrinkage is idempotent", "[type_traits]") { + using X = stdx::shrink_t; + using Y = stdx::shrink_t; + STATIC_CHECK(std::is_same_v); +} + TEST_CASE("two shrunk types are considered the same", "[type_traits]") { using X = stdx::shrink_t; using Y = stdx::shrink_t; @@ -260,6 +266,14 @@ TEST_CASE("type shrinkage (by value)", "[type_traits]") { STATIC_CHECK(std::is_same_v); } +TEST_CASE("type shrinkage (by value) is idempotent", "[type_traits]") { + auto c = C{}; + auto x = stdx::shrink(c); + auto y = stdx::shrink(x); + STATIC_CHECK(std::is_same_v); + STATIC_CHECK(x == y); +} + TEST_CASE("value shrinkage for ct_strings", "[type_traits]") { using namespace stdx::literals; constexpr auto x = stdx::shrink<"hello"_cts>(); @@ -272,6 +286,14 @@ TEST_CASE("two shrunk ct_strings are considered the same type", constexpr auto x = stdx::shrink<"hello">(); constexpr auto y = stdx::shrink<"hello">(); STATIC_CHECK(std::is_same_v); + STATIC_CHECK(x == y); +} + +TEST_CASE("ct_string shrinkage is idempotent", "[type_traits]") { + constexpr auto x = stdx::shrink<"hello">(); + constexpr auto y = stdx::shrink(x); + STATIC_CHECK(std::is_same_v); + STATIC_CHECK(x == y); } namespace {