Universal_formatter example
Closed this issue · 2 comments
Hi,
I figure out that universal_formatter
from the proposal does not work and here is the closest I could get with the current implementation:
#include <experimental/meta>
#include <print>
template <auto... Xs, typename F> constexpr void for_values(F &&f) {
(f.template operator()<Xs>(), ...);
}
template <auto B, auto E, typename F> constexpr void for_range(F &&f) {
using t = std::common_type_t<decltype(B), decltype(E)>;
[&f]<auto... Xs>(std::integer_sequence<t, Xs...>) {
for_values<(B + Xs)...>(f);
}(std::make_integer_sequence<t, E - B>{});
}
template <typename T> consteval auto base_info(int n) {
return bases_of(^T)[n];
}
template <typename T> consteval auto member_info(int n) {
return nonstatic_data_members_of(^T)[n];
}
struct universal_formatter {
constexpr auto parse(auto &ctx) { return ctx.begin(); }
template <typename T> auto format(T const &t, auto &ctx) const {
std::format_to(ctx.out(), "{}{{", name_of(^T));
auto delim = [&, first = true]() mutable {
if (!first) {
std::format_to(ctx.out(), ", ");
}
first = false;
};
for_range<0, bases_of(^T).size()>([&]<auto I>() {
constexpr auto base = base_info<T>(I);
if constexpr (is_accessible(base)) {
delim();
std::format_to(ctx.out(), "{}",
static_cast<[:type_of(base):] const &>(t));
}
});
for_range<0, nonstatic_data_members_of(^T).size()>([&]<auto I>() {
constexpr auto mem = member_info<T>(I);
delim();
std::format_to(ctx.out(), ".{}={}", name_of(mem), t.[:mem:]);
});
std::format_to(ctx.out(), "}}");
return ctx.out();
}
};
struct X {
int m1 = 1;
};
struct Y {
int m2 = 2;
};
class Z : public X, private Y {
int m3 = 3;
int m4 = 4;
};
template <> struct std::formatter<X> : universal_formatter {};
template <> struct std::formatter<Z> : universal_formatter {};
int main() {
std::println("{}", X(0)); //X{.m1=0}
std::println("{}", Z()); // Z{X{.m1 = 1}, .m3 = 3, .m4 = 4}
std::println("{}", []() {
auto z = Z();
z.m1 = -1;
return z;
}()); // Z{X{.m1 = -1}, .m3 = 3, .m4 = 4}
}
https://godbolt.org/z/K4cGYf8qj
One important note, is that Y
is not accessible from the instance of Z
so formatter can not show it.
It can be beneficial to have it as a part of tests/examples, so if you are open i can open a PR for this addition.
Hey, @Yaraslaut ! You're probably looking at P2996R2, but note that we presented a D2996R3 for the Tokyo meeting, which includes this link to a working example from Clang/P2996. We didn't implement your use of is_accessible
to check for private base classes though, which is a nice trick.
Since that time, Robert Leahy brought to our attention that C-style casts allow one to circumvent access-checking on base classes, so the original Universal Formatter can in fact be saved. This example will be published in the next revision, complete with support for private base classes: https://godbolt.org/z/rbs6K78WG
That said, I did forget to add the example as a test case! If you'd like to open a PR adding the example linked above as a test case, I'd be happy to merge it.
Hey, @Yaraslaut ! You're probably looking at P2996R2, but note that we presented a D2996R3 for the Tokyo meeting, which includes this link to a working example from Clang/P2996. We didn't implement your use of
is_accessible
to check for private base classes though, which is a nice trick.Since that time, Robert Leahy brought to our attention that C-style casts allow one to circumvent access-checking on base classes, so the original Universal Formatter can in fact be saved. This example will be published in the next revision, complete with support for private base classes: https://godbolt.org/z/rbs6K78WG
That said, I did forget to add the example as a test case! If you'd like to open a PR adding the example linked above as a test case, I'd be happy to merge it.
Thanks for the detailed reply, I will make a PR later.