From 20f8a49801f050edfc78a8dc3d01c0373f81d918 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Fri, 11 Sep 2026 16:48:03 -0600 Subject: [PATCH] :sparkles: Add functionality for named types Problem: - Named types are used everywhere, but in an ad-hoc manner. Solution: - Add `named` concept to detect a the `name` of a type as well as `name_of_v` and `constant_name_of_v` to extract the name either as a `ct_string` or a `cts_t`. - Add `with_name` as a convenient "named type" base class. --- include/stdx/ct_string.hpp | 20 ++++++++++++++++++++ test/ct_string.cpp | 8 ++++++++ test/tuple_algorithms.cpp | 26 +++++++++++--------------- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/include/stdx/ct_string.hpp b/include/stdx/ct_string.hpp index 0881b9e..7f7cf29 100644 --- a/include/stdx/ct_string.hpp +++ b/include/stdx/ct_string.hpp @@ -182,5 +182,25 @@ template consteval auto operator""_cts() { return S; } template consteval auto operator""_ctst() { return cts_t{}; } } // namespace ct_string_literals } // namespace literals + +template struct with_name { + constexpr static auto name = 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>; +} + +template +concept named = requires { + { T::name } -> detail::is_ct_string; +}; + +template constexpr auto name_of_v = T::name; +template using constant_name_of_t = cts_t; +template constexpr auto constant_name_of_v = constant_name_of_t{}; } // namespace v1 } // namespace stdx diff --git a/test/ct_string.cpp b/test/ct_string.cpp index f9b16a0..43f82c5 100644 --- a/test/ct_string.cpp +++ b/test/ct_string.cpp @@ -192,3 +192,11 @@ TEST_CASE("CT_WRAP", "[ct_string]") { STATIC_REQUIRE(CT_WRAP(X) == "hello"_ctst); }.template operator()<"hello">(); } + +TEST_CASE("named object", "[ct_string]") { + using namespace stdx::ct_string_literals; + using T = stdx::with_name<"test">; + STATIC_CHECK(stdx::named); + STATIC_CHECK(stdx::name_of_v == "test"_cts); + STATIC_CHECK(stdx::constant_name_of_v == "test"_ctst); +} diff --git a/test/tuple_algorithms.cpp b/test/tuple_algorithms.cpp index e3732f1..d0dc7c4 100644 --- a/test/tuple_algorithms.cpp +++ b/test/tuple_algorithms.cpp @@ -1,5 +1,6 @@ #include "detail/tuple_types.hpp" +#include #include #include #include @@ -979,31 +980,26 @@ TEST_CASE("gather with move only types", "[tuple_algorithms]") { } namespace { -template struct named_int { - using name_t = T; +template struct named_int : stdx::with_name { + constexpr named_int(int v) : value{v} {} int value; friend constexpr auto operator==(named_int, named_int) -> bool = default; }; - -template using name_of_t = typename T::name_t; } // namespace TEST_CASE("gather_by with projection", "[tuple_algorithms]") { - struct A; - struct B; - struct C; - constexpr auto t = stdx::tuple{named_int{3}, named_int{11}, - named_int{0}, named_int{12}}; - constexpr auto gathered = stdx::gather_by(t); + constexpr auto t = stdx::tuple{named_int<"C">{3}, named_int<"B">{11}, + named_int<"A">{0}, named_int<"B">{12}}; + constexpr auto gathered = stdx::gather_by(t); STATIC_REQUIRE( std::is_same_v>, - stdx::tuple, named_int>, - stdx::tuple>> const>); - CHECK(get<0>(gathered) == stdx::tuple{named_int{0}}); + stdx::tuple>, + stdx::tuple, named_int<"B">>, + stdx::tuple>> const>); + CHECK(get<0>(gathered) == stdx::tuple{named_int<"A">{0}}); CHECK(stdx::get<1>(gathered).fold_left( 0, [](auto x, auto y) { return x + y.value; }) == 23); - CHECK(get<2>(gathered) == stdx::tuple{named_int{3}}); + CHECK(get<2>(gathered) == stdx::tuple{named_int<"C">{3}}); } TEST_CASE("tuple_cons", "[tuple_algorithms]") {