Info
-
Did you know that you can customize formatter for your classes with
std::format
?
Example
struct foo {
int value{};
};
template<>
struct fmt::formatter<foo> : fmt::formatter<int> {
template<class FormatContext>
auto format(foo f, FormatContext& ctx) {
return fmt::formatter<int>::format(f.value, ctx);
}
};
int main() {
std::cout << fmt::format("{}", foo{.value = 42}); // prints 42
}
Puzzle
- Can you implement a custom formatter for
foo
?
template<class...>
struct foo;
int main() {
using namespace boost::ut;
using namespace std::literals;
"custom formatter"_test = [] {
should("format empty with no spaces") = [] {
expect("foo()"sv == fmt::format("{}", foo{}));
};
should("format fields seperated by space") = [] {
expect("foo( 1 )"sv == fmt::format("{}", foo{1}));
expect("foo( 1 2 )"sv == fmt::format("{}", foo{1, 2}));
expect("foo( 1 2 3 )"sv == fmt::format("{}", foo{1, 2, 3}));
};
should("format fields seperated by space with different types") = [] {
expect("foo( Quant Lab )"sv == fmt::format("{}", foo{"Quant", "Lab"}));
expect("foo( Q 42 B )"sv == fmt::format("{}", foo{'Q', 42, "B"}));
};
};
}
Solutions
template <class... Ts>
struct foo {
std::tuple<Ts...> values;
foo(const Ts... args) : values(std::make_tuple(args...)){};
};
template <class... Ts>
struct fmt::formatter<foo<Ts...>> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.end();
}
template <class FormatContext>
auto format(foo<Ts...> f, FormatContext& ctx) {
return fmt::format_to(
ctx.out(),
std::tuple_size<decltype(f.values)>::value ? "foo( {} )" : "foo({})",
fmt::join(f.values, " "));
}
};
template<typename... Ts>
struct foo {
foo(Ts...t) : data{t...} {}
std::tuple<Ts...> data{};
};
template<typename ...Ts>
struct fmt::formatter<foo<Ts...>> {
constexpr auto parse(auto& ctx) {
return ctx.end();
}
auto format(foo<Ts...> f, auto& ctx) {
if constexpr (0 == sizeof...(Ts)) {
return fmt::format_to(ctx.out(), "foo()");
} else {
return fmt::format_to(ctx.out(), "foo( {} )", fmt::join(f.data, " "));
}
}
};
template <class... TArgs>
struct foo {
constexpr foo(TArgs... args) : val{args...} {}
std::tuple<TArgs...> val;
};
template <class... TArgs>
struct fmt::formatter<foo<TArgs...>> {
constexpr auto parse(format_parse_context& ctx) const { return ctx.end(); }
constexpr auto format(foo<TArgs...> f, auto& ctx) const {
if constexpr (sizeof...(TArgs)) {
return fmt::format_to(ctx.out(), "foo( {} )", fmt::join(f.val, " "));
}
return fmt::format_to(ctx.out(), "foo()");
}
};
template <class... TArgs>
struct foo : std::tuple<TArgs...> {
constexpr foo(TArgs... args) : std::tuple<TArgs...>{args...} {}
};
template <typename... Ts>
struct fmt::formatter<foo<Ts...>> : fmt::formatter<std::string_view> {
template <class FormatContext>
auto format(foo<Ts...> f, FormatContext& ctx) {
if (sizeof...(Ts) == 0) {
return fmt::format_to(ctx.out(), "foo()");
}
return fmt::format_to(ctx.out(), "foo( {} )", fmt::join(f, " "));
}
};
template <class... TArgs>
struct foo : std::tuple<TArgs...> {
using std::tuple<TArgs...>::tuple;
};
template <class... TArgs>
foo(TArgs...) -> foo<TArgs...>;
template <class... TArgs>
struct fmt::formatter<foo<TArgs...>> {
template <class TParseContext>
constexpr auto parse(TParseContext &ctx) {
return std::begin(ctx);
}
template <class FormatContext>
auto format(foo<TArgs...> f, FormatContext& ctx) {
if constexpr (sizeof...(TArgs) == 0) {
return fmt::format_to(ctx.out(), "foo()");
}
fmt::format_to(ctx.out(), "foo( ");
[&]<auto... Is>(std::index_sequence<Is...>) {
(fmt::format_to(ctx.out(), "{} ", std::get<Is>(f)), ...);
}(std::index_sequence_for<TArgs...>{});
return fmt::format_to(ctx.out(), ")");
}
};
template<class... Args>
struct foo : std::tuple<Args...> {
using std::tuple<Args...>::tuple;
};
template<class...Args> foo(Args...) -> foo<Args...>;
template<class...T>
struct fmt::formatter<foo<T...>> : fmt::formatter<int> {
template<class FormatContext>
auto format(foo<T...> f, FormatContext& ctx) {
std::string fmtString = sizeof...(T) > 0 ? "foo( " : "foo(";
for (int i=0; i<sizeof...(T); i++)
fmtString += "{} ";
fmtString += ")";
auto formatOut = [&](auto...args) { return fmt::format_to(ctx.out(), fmtString, args...); };
return std::apply(formatOut, static_cast<std::tuple<T...>>(f));
}
};
template<class... Ts>
struct foo : std::tuple<Ts...> {
constexpr explicit(true) foo(Ts... ts) : std::tuple<Ts...>(ts...) {}
};
template<class... Ts>
struct fmt::formatter<foo<Ts...>> {
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
template<class TCtx>
auto format(foo<Ts...> f, TCtx& ctx) {
if constexpr(sizeof...(Ts) == 0) {
return format_to(ctx.out(), "foo()");
} else {
return std::apply([&](auto... args) {
return format_to(
ctx.out(),
std::string{"foo( "} + ((args, std::string{"{} "}) + ...) + ")",
args...);
}, static_cast<const std::tuple<Ts...>&>(f));
}
}
};