Skip to content

Commit 85f6ecc

Browse files
authored
Add format_as support for std::variant and std::expected formatters (#4575)
Signed-off-by: Vladislav Shchapov <[email protected]>
1 parent 378a5ab commit 85f6ecc

File tree

4 files changed

+66
-24
lines changed

4 files changed

+66
-24
lines changed

include/fmt/format.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,14 @@ template <typename T> struct allocator : private std::decay<void> {
763763
}
764764
};
765765

766+
template <typename Formatter>
767+
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
768+
-> decltype(f.set_debug_format(set)) {
769+
f.set_debug_format(set);
770+
}
771+
template <typename Formatter>
772+
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
773+
766774
} // namespace detail
767775

768776
FMT_BEGIN_EXPORT

include/fmt/ranges.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,6 @@ using range_reference_type =
241241
template <typename Range>
242242
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
243243

244-
template <typename Formatter>
245-
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
246-
-> decltype(f.set_debug_format(set)) {
247-
f.set_debug_format(set);
248-
}
249-
template <typename Formatter>
250-
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
251-
252244
template <typename T>
253245
struct range_format_kind_
254246
: std::integral_constant<range_format,

include/fmt/std.h

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,17 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
111111
#endif // FMT_CPP_LIB_FILESYSTEM
112112

113113
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
114-
template <typename Char, typename OutputIt, typename T>
115-
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
114+
115+
template <typename Char, typename OutputIt, typename T, typename FormatContext>
116+
auto write_escaped_alternative(OutputIt out, const T& v, FormatContext& ctx)
117+
-> OutputIt {
116118
if constexpr (has_to_string_view<T>::value)
117119
return write_escaped_string<Char>(out, detail::to_string_view(v));
118120
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
119-
return write<Char>(out, v);
121+
122+
formatter<std::remove_cv_t<T>, Char> underlying;
123+
maybe_set_debug_format(underlying, true);
124+
return underlying.format(v, ctx);
120125
}
121126
#endif
122127

@@ -382,18 +387,9 @@ struct formatter<std::optional<T>, Char,
382387
static constexpr basic_string_view<Char> none =
383388
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
384389

385-
template <class U>
386-
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
387-
-> decltype(u.set_debug_format(set)) {
388-
u.set_debug_format(set);
389-
}
390-
391-
template <class U>
392-
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
393-
394390
public:
395391
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
396-
maybe_set_debug_format(underlying_, true);
392+
detail::maybe_set_debug_format(underlying_, true);
397393
return underlying_.parse(ctx);
398394
}
399395

@@ -429,10 +425,10 @@ struct formatter<std::expected<T, E>, Char,
429425
if (value.has_value()) {
430426
out = detail::write<Char>(out, "expected(");
431427
if constexpr (!std::is_void<T>::value)
432-
out = detail::write_escaped_alternative<Char>(out, *value);
428+
out = detail::write_escaped_alternative<Char>(out, *value, ctx);
433429
} else {
434430
out = detail::write<Char>(out, "unexpected(");
435-
out = detail::write_escaped_alternative<Char>(out, value.error());
431+
out = detail::write_escaped_alternative<Char>(out, value.error(), ctx);
436432
}
437433
*out++ = ')';
438434
return out;
@@ -496,7 +492,7 @@ struct formatter<Variant, Char,
496492
FMT_TRY {
497493
std::visit(
498494
[&](const auto& v) {
499-
out = detail::write_escaped_alternative<Char>(out, v);
495+
out = detail::write_escaped_alternative<Char>(out, v, ctx);
500496
},
501497
value);
502498
}

test/std-test.cc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,33 @@ class my_class {
197197
return fmt::to_string(elm.av);
198198
}
199199
};
200+
201+
class my_class_int {
202+
public:
203+
int av;
204+
205+
private:
206+
friend auto format_as(const my_class_int& elm) -> int { return elm.av; }
207+
};
200208
} // namespace my_nso
209+
210+
TEST(std_test, expected_format_as) {
211+
#ifdef __cpp_lib_expected
212+
EXPECT_EQ(
213+
fmt::format(
214+
"{}", std::expected<my_nso::my_number, int>{my_nso::my_number::one}),
215+
"expected(\"first\")");
216+
EXPECT_EQ(
217+
fmt::format("{}",
218+
std::expected<my_nso::my_class, int>{my_nso::my_class{7}}),
219+
"expected(\"7\")");
220+
EXPECT_EQ(fmt::format("{}",
221+
std::expected<my_nso::my_class_int, int>{
222+
my_nso::my_class_int{8}}),
223+
"expected(8)");
224+
#endif
225+
}
226+
201227
TEST(std_test, optional_format_as) {
202228
#ifdef __cpp_lib_optional
203229
EXPECT_EQ(fmt::format("{}", std::optional<my_nso::my_number>{}), "none");
@@ -206,6 +232,8 @@ TEST(std_test, optional_format_as) {
206232
EXPECT_EQ(fmt::format("{}", std::optional<my_nso::my_class>{}), "none");
207233
EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_class{7}}),
208234
"optional(\"7\")");
235+
EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_class_int{8}}),
236+
"optional(8)");
209237
#endif
210238
}
211239

@@ -275,6 +303,24 @@ TEST(std_test, variant) {
275303
#endif
276304
}
277305

306+
TEST(std_test, variant_format_as) {
307+
#ifdef __cpp_lib_variant
308+
309+
EXPECT_EQ(fmt::format("{}", std::variant<my_nso::my_number>{}),
310+
"variant(\"first\")");
311+
EXPECT_EQ(fmt::format(
312+
"{}", std::variant<my_nso::my_number>{my_nso::my_number::one}),
313+
"variant(\"first\")");
314+
EXPECT_EQ(
315+
fmt::format("{}", std::variant<my_nso::my_class>{my_nso::my_class{7}}),
316+
"variant(\"7\")");
317+
EXPECT_EQ(
318+
fmt::format("{}",
319+
std::variant<my_nso::my_class_int>{my_nso::my_class_int{8}}),
320+
"variant(8)");
321+
#endif
322+
}
323+
278324
TEST(std_test, error_code) {
279325
auto& generic = std::generic_category();
280326
EXPECT_EQ(fmt::format("{}", std::error_code(42, generic)), "generic:42");

0 commit comments

Comments
 (0)