Skip to content

Commit

Permalink
🐛 Back off the consteval fervour
Browse files Browse the repository at this point in the history
In order to call `ct_format` with runtime arguments, it can't be `consteval`!
  • Loading branch information
elbeno authored and lukevalenty committed May 23, 2024
1 parent 0bd6078 commit 6b44edd
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 14 deletions.
34 changes: 20 additions & 14 deletions include/stdx/ct_format.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,17 @@ CONSTEVAL auto arg_value(cx_value auto a) {
}

template <typename T, typename U, typename S>
CONSTEVAL auto operator+(format_result<T, U> r, S s) {
constexpr auto operator+(format_result<T, U> r, S s) {
return format_result{r.str + s, r.args};
}

template <typename S, typename T, typename U>
CONSTEVAL auto operator+(S s, format_result<T, U> r) {
constexpr auto operator+(S s, format_result<T, U> r) {
return format_result{s + r.str, r.args};
}

template <typename A, typename B, typename T, typename U>
CONSTEVAL auto operator+(format_result<A, B> r1, format_result<T, U> r2) {
constexpr auto operator+(format_result<A, B> r1, format_result<T, U> r2) {
return format_result{r1.str + r2.str, tuple_cat(r1.args, r2.args)};
}

Expand Down Expand Up @@ -148,7 +148,7 @@ CONSTEVAL auto convert_output() {
template <ct_string Fmt,
template <typename T, T...> typename Output = detail::null_output,
typename Arg>
CONSTEVAL auto format1(Arg arg) {
constexpr auto format1(Arg arg) {
if constexpr (cx_value<Arg>) {
constexpr auto result = [&] {
constexpr auto fmtstr = FMT_COMPILE(std::string_view{Fmt});
Expand Down Expand Up @@ -185,29 +185,35 @@ concept ct_format_compatible = requires {
Output<char, 'A'>{} + Output<char, 'B'>{}
} -> same_as<Output<char, 'A', 'B'>>;
};

template <ct_string Fmt> 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<N + 1>(fmt);
constexpr static auto last_cts =
detail::to_ct_string<splits[N].size()>(splits[N]);
};
} // namespace detail

template <ct_string Fmt,
template <typename T, T...> typename Output = detail::null_output>
constexpr auto ct_format = [](auto &&...args) CONSTEVAL {
constexpr auto ct_format = [](auto &&...args) {
if constexpr (not same_as<Output<char>, detail::null_output<char>>) {
static_assert(detail::ct_format_compatible<Output>);
}

constexpr auto fmt = std::string_view{Fmt};
constexpr auto N = detail::count_specifiers(fmt);
constexpr auto splits = detail::split_specifiers<N + 1>(fmt);
using data = detail::fmt_data<Fmt>;

auto const format1 = [&]<std::size_t I>(auto &&arg) CONSTEVAL {
constexpr auto cts = detail::to_ct_string<splits[I].size()>(splits[I]);
[[maybe_unused]] auto const format1 = [&]<std::size_t I>(auto &&arg) {
constexpr auto cts =
detail::to_ct_string<data::splits[I].size()>(data::splits[I]);
return detail::format1<cts, Output>(FWD(arg));
};

constexpr auto last_cts = detail::to_ct_string<splits[N].size()>(splits[N]);
return [&]<std::size_t... Is>(std::index_sequence<Is...>) CONSTEVAL {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return (format1.template operator()<Is>(FWD(args)) + ... +
detail::convert_output<last_cts, Output>());
}(std::make_index_sequence<N>{});
detail::convert_output<data::last_cts, Output>());
}(std::make_index_sequence<data::N>{});
};
} // namespace v1
} // namespace stdx
Expand Down
22 changes: 22 additions & 0 deletions test/ct_format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)});
Expand All @@ -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,
Expand Down Expand Up @@ -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<char, 'A'>{});

auto x = 42;
CHECK(stdx::ct_format<"{}", string_constant>(x) ==
stdx::format_result{string_constant<char, '{', '}'>{},
stdx::make_tuple(42)});
static_assert(stdx::ct_format<"{}", string_constant>(42) ==
stdx::format_result{string_constant<char, '{', '}'>{},
stdx::make_tuple(42)});
Expand Down Expand Up @@ -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{
Expand Down

0 comments on commit 6b44edd

Please sign in to comment.