From 6b44edddb5d5d1121b4e30d74a6892a8240d0d54 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Wed, 22 May 2024 18:20:00 -0600 Subject: [PATCH] :bug: Back off the consteval fervour In order to call `ct_format` with runtime arguments, it can't be `consteval`! --- include/stdx/ct_format.hpp | 34 ++++++++++++++++++++-------------- test/ct_format.cpp | 22 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/include/stdx/ct_format.hpp b/include/stdx/ct_format.hpp index b954d72..e6345e6 100644 --- a/include/stdx/ct_format.hpp +++ b/include/stdx/ct_format.hpp @@ -107,17 +107,17 @@ CONSTEVAL auto arg_value(cx_value auto a) { } template -CONSTEVAL auto operator+(format_result r, S s) { +constexpr auto operator+(format_result r, S s) { return format_result{r.str + s, r.args}; } template -CONSTEVAL auto operator+(S s, format_result r) { +constexpr auto operator+(S s, format_result r) { return format_result{s + r.str, r.args}; } template -CONSTEVAL auto operator+(format_result r1, format_result r2) { +constexpr auto operator+(format_result r1, format_result r2) { return format_result{r1.str + r2.str, tuple_cat(r1.args, r2.args)}; } @@ -148,7 +148,7 @@ CONSTEVAL auto convert_output() { template typename Output = detail::null_output, typename Arg> -CONSTEVAL auto format1(Arg arg) { +constexpr auto format1(Arg arg) { if constexpr (cx_value) { constexpr auto result = [&] { constexpr auto fmtstr = FMT_COMPILE(std::string_view{Fmt}); @@ -185,29 +185,35 @@ concept ct_format_compatible = requires { Output{} + Output{} } -> same_as>; }; + +template struct fmt_data { + constexpr static auto fmt = std::string_view{Fmt}; + constexpr static auto N = detail::count_specifiers(fmt); + constexpr static auto splits = detail::split_specifiers(fmt); + constexpr static auto last_cts = + detail::to_ct_string(splits[N]); +}; } // namespace detail template typename Output = detail::null_output> -constexpr auto ct_format = [](auto &&...args) CONSTEVAL { +constexpr auto ct_format = [](auto &&...args) { if constexpr (not same_as, detail::null_output>) { static_assert(detail::ct_format_compatible); } - constexpr auto fmt = std::string_view{Fmt}; - constexpr auto N = detail::count_specifiers(fmt); - constexpr auto splits = detail::split_specifiers(fmt); + using data = detail::fmt_data; - auto const format1 = [&](auto &&arg) CONSTEVAL { - constexpr auto cts = detail::to_ct_string(splits[I]); + [[maybe_unused]] auto const format1 = [&](auto &&arg) { + constexpr auto cts = + detail::to_ct_string(data::splits[I]); return detail::format1(FWD(arg)); }; - constexpr auto last_cts = detail::to_ct_string(splits[N]); - return [&](std::index_sequence) CONSTEVAL { + return [&](std::index_sequence) { return (format1.template operator()(FWD(args)) + ... + - detail::convert_output()); - }(std::make_index_sequence{}); + detail::convert_output()); + }(std::make_index_sequence{}); }; } // namespace v1 } // namespace stdx diff --git a/test/ct_format.cpp b/test/ct_format.cpp index 31111b8..af7a183 100644 --- a/test/ct_format.cpp +++ b/test/ct_format.cpp @@ -67,11 +67,17 @@ TEST_CASE("format a compile-time enum argument", "[ct_format]") { } TEST_CASE("format a runtime argument", "[ct_format]") { + auto x = 42; + CHECK(stdx::ct_format<"Hello {}">(x) == + stdx::format_result{"Hello {}"_cts, stdx::make_tuple(42)}); static_assert(stdx::ct_format<"Hello {}">(42) == stdx::format_result{"Hello {}"_cts, stdx::make_tuple(42)}); } TEST_CASE("format a compile-time and a runtime argument (1)", "[ct_format]") { + auto x = 42; + CHECK(stdx::ct_format<"Hello {} {}">(CX_VALUE(int), x) == + stdx::format_result{"Hello int {}"_cts, stdx::make_tuple(42)}); static_assert( stdx::ct_format<"Hello {} {}">(CX_VALUE(int), 42) == stdx::format_result{"Hello int {}"_cts, stdx::make_tuple(42)}); @@ -91,6 +97,11 @@ TEST_CASE("format multiple runtime arguments", "[ct_format]") { TEST_CASE("format multiple mixed arguments", "[ct_format]") { using namespace std::string_view_literals; + auto b = "B"sv; + CHECK(stdx::ct_format<"Hello {} {} {} {} world">(42, CX_VALUE("A"sv), b, + CX_VALUE(int)) == + stdx::format_result{"Hello {} A {} int world"_cts, + stdx::make_tuple(42, "B"sv)}); static_assert(stdx::ct_format<"Hello {} {} {} {} world">( 42, CX_VALUE("A"sv), "B"sv, CX_VALUE(int)) == stdx::format_result{"Hello {} A {} int world"_cts, @@ -130,6 +141,10 @@ TEST_CASE("format_to a different type", "[ct_format]") { static_assert(stdx::ct_format<"{}", string_constant>(CX_VALUE("A"sv)) == string_constant{}); + auto x = 42; + CHECK(stdx::ct_format<"{}", string_constant>(x) == + stdx::format_result{string_constant{}, + stdx::make_tuple(42)}); static_assert(stdx::ct_format<"{}", string_constant>(42) == stdx::format_result{string_constant{}, stdx::make_tuple(42)}); @@ -159,6 +174,13 @@ TEST_CASE("format a ct-formatted string with different type", "[ct_format]") { TEST_CASE("format multiple mixed arguments with different type", "[ct_format]") { using namespace std::string_view_literals; + auto b = "B"sv; + CHECK(stdx::ct_format<"Hello {} {} {} {} world", string_constant>( + 42, CX_VALUE("A"sv), b, CX_VALUE(int)) == + stdx::format_result{ + stdx::ct_string_to_type<"Hello {} A {} int world"_cts, + string_constant>(), + stdx::make_tuple(42, "B"sv)}); static_assert(stdx::ct_format<"Hello {} {} {} {} world", string_constant>( 42, CX_VALUE("A"sv), "B"sv, CX_VALUE(int)) == stdx::format_result{