Skip to content

Commit 428c43f

Browse files
committed
🐛 Fix formatting of move-only types
Problem: - Runtime formatting of move-only types does not work. Solution: - Move the values through the formatting machinery appropriately.
1 parent 8ace16b commit 428c43f

2 files changed

Lines changed: 35 additions & 16 deletions

File tree

include/stdx/ct_format.hpp

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ struct format_result<Str, Args, Spans> {
109109

110110
template <typename Spans = type_list<>, typename Str, typename Args = tuple<>>
111111
constexpr auto make_format_result(Str s, Args args = {}) {
112-
return format_result<Str, Args, Spans>{s, args};
112+
return format_result<Str, Args, Spans>{s, std::move(args)};
113113
}
114114

115115
inline namespace literals {
@@ -228,13 +228,13 @@ CONSTEVAL auto arg_type(fmt_cx_value auto a) {
228228

229229
template <typename Str, typename Args, typename Spans, typename S>
230230
constexpr auto operator+(format_result<Str, Args, Spans> r, S s) {
231-
return make_format_result<Spans>(r.str + s, r.args);
231+
return make_format_result<Spans>(r.str + s, std::move(r.args));
232232
}
233233

234234
template <typename S, typename Str, typename Args, typename Spans>
235235
constexpr auto operator+(S s, format_result<Str, Args, Spans> r) {
236-
return make_format_result<detail::apply_offset<s.size(), Spans>>(s + r.str,
237-
r.args);
236+
return make_format_result<detail::apply_offset<s.size(), Spans>>(
237+
s + r.str, std::move(r.args));
238238
}
239239

240240
template <typename Str1, typename Args1, typename Spans1, typename Str2,
@@ -243,7 +243,7 @@ constexpr auto operator+(format_result<Str1, Args1, Spans1> r1,
243243
format_result<Str2, Args2, Spans2> r2) {
244244
return make_format_result<boost::mp11::mp_append<
245245
Spans1, detail::apply_offset<r1.str.size(), Spans2>>>(
246-
r1.str + r2.str, tuple_cat(r1.args, r2.args));
246+
r1.str + r2.str, tuple_cat(std::move(r1.args), std::move(r2.args)));
247247
}
248248

249249
template <typename T, T...> struct null_output;
@@ -302,10 +302,10 @@ constexpr auto format1(Arg arg) {
302302
auto const sub_result = format1<Fmt, Start>(arg.str);
303303
using Spans = typename Arg::spans_t;
304304
return make_format_result<detail::apply_offset<Start, Spans>>(
305-
sub_result.str, arg.args);
305+
sub_result.str, std::move(arg).args);
306306
} else {
307307
using Spans = type_list<format_span<Start, Fmt.size()>>;
308-
return make_format_result<Spans>(cts_t<Fmt>{}, tuple{arg});
308+
return make_format_result<Spans>(cts_t<Fmt>{}, tuple{std::move(arg)});
309309
}
310310
}
311311

@@ -324,10 +324,20 @@ template <ct_string Fmt> struct fmt_data {
324324
to_ct_string<splits[N].view.size()>(splits[N].view);
325325
};
326326

327-
template <typename T>
328-
constexpr auto ct_format_as(T const &t) -> decltype(auto) {
329-
return (t);
330-
}
327+
[[maybe_unused]] constexpr inline struct format_as_t {
328+
template <typename T>
329+
requires true
330+
constexpr auto operator()(T &&t) const
331+
noexcept(noexcept(ct_format_as(std::forward<T>(t))))
332+
-> decltype(ct_format_as(std::forward<T>(t))) {
333+
return ct_format_as(std::forward<T>(t));
334+
}
335+
336+
template <typename T>
337+
constexpr auto operator()(T &&t) const -> decltype(auto) {
338+
return T(std::forward<T>(t));
339+
}
340+
} format_as;
331341
} // namespace detail
332342

333343
template <ct_string Fmt,
@@ -349,14 +359,13 @@ constexpr auto ct_format = [](auto &&...args) {
349359
return detail::format1<cts, data::splits[I].start>(FWD(arg));
350360
};
351361

352-
auto const result = [&]<std::size_t... Is>(std::index_sequence<Is...>) {
353-
using detail::ct_format_as;
354-
return (format1.template operator()<Is>(ct_format_as(FWD(args))) + ... +
355-
make_format_result(cts_t<data::last_cts>{}));
362+
auto result = [&]<std::size_t... Is>(std::index_sequence<Is...>) {
363+
return (format1.template operator()<Is>(detail::format_as(FWD(args))) +
364+
... + make_format_result(cts_t<data::last_cts>{}));
356365
}(std::make_index_sequence<data::N>{});
357366
constexpr auto str = detail::convert_output<result.str.value, Output>();
358367
using Spans = typename std::remove_cvref_t<decltype(result)>::spans_t;
359-
return make_format_result<Spans>(str, result.args);
368+
return make_format_result<Spans>(str, std::move(result).args);
360369
};
361370

362371
template <ct_string Fmt>

test/ct_format.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#include "detail/tuple_types.hpp"
2+
13
#include <stdx/ct_format.hpp>
24
#include <stdx/utility.hpp>
35

@@ -193,6 +195,14 @@ TEST_CASE("format a runtime argument", "[ct_format]") {
193195
STATIC_CHECK(stdx::ct_format<"Hello {}">(x) == expected);
194196
}
195197

198+
TEST_CASE("format a move-only runtime argument", "[ct_format]") {
199+
using expected_spans_t = stdx::type_list<stdx::format_span<6, 8>>;
200+
constexpr auto expected = stdx::make_format_result<expected_spans_t>(
201+
"Hello {}"_ctst, stdx::tuple{move_only{17}});
202+
CHECK(stdx::ct_format<"Hello {}">(move_only{17}) == expected);
203+
STATIC_CHECK(stdx::ct_format<"Hello {}">(move_only{17}) == expected);
204+
}
205+
196206
TEST_CASE("format a compile-time and a runtime argument (1)", "[ct_format]") {
197207
constexpr auto x = 17;
198208
using expected_spans_t =

0 commit comments

Comments
 (0)