From c52ed4ce46c121a2151ffbf50a7175b08a3e57d0 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Fri, 11 Jul 2025 10:24:06 -0600 Subject: [PATCH] :art: Make `cts_t` and `format_result` easier to format Problem: - `cts_t` and `format_result` are non-constexpr values that generally may contain values that are constexpr-usable. `CX_WRAP` doesn't recognize this and wraps them in a capturing lambda as if they are runtime values. Solution: - Expose `cx_value_t` members and call operators (like `std::integral_constant` has) to make `cts_t` and `format_result` constexpr usable. Note: - `format_result` is compile-time usable iff it has no runtime format arguments and its string is also compile-time usable. --- include/stdx/ct_format.hpp | 18 ++++++++++++++++++ include/stdx/ct_string.hpp | 2 ++ include/stdx/type_traits.hpp | 1 - test/ct_format.cpp | 12 +++++++++++- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/include/stdx/ct_format.hpp b/include/stdx/ct_format.hpp index f67ab7c..6fdd687 100644 --- a/include/stdx/ct_format.hpp +++ b/include/stdx/ct_format.hpp @@ -51,6 +51,24 @@ template struct format_result { format_result const &) -> bool = default; }; +template + requires(Args::size() == 0 and is_cx_value_v) +struct format_result { + CONSTEVAL static auto ct_string_convertible() -> std::true_type; + + [[no_unique_address]] Str str; + [[no_unique_address]] Args args{}; + + friend constexpr auto operator+(format_result const &fr) { return +fr.str; } + + constexpr auto operator()() const noexcept { return +(*this); } + using cx_value_t [[maybe_unused]] = void; + + private: + friend constexpr auto operator==(format_result const &, + format_result const &) -> bool = default; +}; + template format_result(Str, Args) -> format_result; template format_result(Str) -> format_result>; diff --git a/include/stdx/ct_string.hpp b/include/stdx/ct_string.hpp index f3afcac..fe7cb4d 100644 --- a/include/stdx/ct_string.hpp +++ b/include/stdx/ct_string.hpp @@ -145,6 +145,8 @@ template struct cts_t { CONSTEVAL static auto ct_string_convertible() -> std::true_type; friend constexpr auto operator+(cts_t const &) { return value; } + constexpr auto operator()() const noexcept { return value; } + using cx_value_t [[maybe_unused]] = void; }; template diff --git a/include/stdx/type_traits.hpp b/include/stdx/type_traits.hpp index 56150d5..caa6dcf 100644 --- a/include/stdx/type_traits.hpp +++ b/include/stdx/type_traits.hpp @@ -274,6 +274,5 @@ STDX_PRAGMA(diagnostic pop) template constexpr auto is_complete_v = false; template constexpr auto is_complete_v> = true; - } // namespace v1 } // namespace stdx diff --git a/test/ct_format.cpp b/test/ct_format.cpp index f00485a..b8d028b 100644 --- a/test/ct_format.cpp +++ b/test/ct_format.cpp @@ -263,11 +263,21 @@ TEST_CASE("FORMAT a type argument", "[ct_format]") { STATIC_REQUIRE(STDX_CT_FORMAT("Hello {}", int) == "Hello int"_fmt_res); } -TEST_CASE("FORMAT a constexpr string argument", "[ct_format]") { +TEST_CASE("FORMAT a constexpr ct_string argument", "[ct_format]") { constexpr static auto S = "world"_cts; STATIC_REQUIRE(STDX_CT_FORMAT("Hello {}", S) == "Hello world"_fmt_res); } +TEST_CASE("FORMAT a cts_t argument", "[ct_format]") { + auto S = "world"_ctst; + STATIC_REQUIRE(STDX_CT_FORMAT("Hello {}", S) == "Hello world"_fmt_res); +} + +TEST_CASE("FORMAT a format_result argument", "[ct_format]") { + auto S = "world"_fmt_res; + STATIC_REQUIRE(STDX_CT_FORMAT("Hello {}", S) == "Hello world"_fmt_res); +} + TEST_CASE("FORMAT a constexpr int argument", "[ct_format]") { constexpr static auto I = 17; STATIC_REQUIRE(STDX_CT_FORMAT("Hello {}", I) == "Hello 17"_fmt_res);