Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,19 @@ int main() {


### <rsl/tuple>
`rsl::tuple` is a reflective reimplementation of `std::tuple`.
`rsl::tuple` is a reflective reimplementation of `std::tuple`.

### <rsl/format>
`rsl::format` is an implementation of a formatter for the arbitrary aggregate types

``` cpp
struct S
{
int i;
int j;
};

rsl::format(S{1,2}); // S{.i=1, .j=2}
```
If you define `RSL_INJECT_GLOBAL_FORMATTER`, this will automatically generate formatting of aggregate types for `std::formatter` to use.
You can also use it directly inlude this functionality in the godbolt using `#include <https://raw.githubusercontent.com/rsl-org/util/refs/heads/master/include/rsl/format>`
66 changes: 66 additions & 0 deletions include/rsl/format
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#pragma once
#include <meta>
#include <string>

namespace rsl {

template <typename T>
auto format(T const& t) {
std::string result;
std::string_view type_label = "(unnamed-type)";
if constexpr (has_identifier(^^T))
type_label = identifier_of(^^T);
auto out = std::format_to(std::back_inserter(result), "{}{{", type_label);

auto delim = [first = true, &out]() mutable {
if (!first) {
*out++ = ',';
*out++ = ' ';
}
first = false;
};

constexpr auto access_ctx = std::meta::access_context::unchecked();

template for (constexpr auto base : define_static_array(bases_of(^^T, access_ctx))) {
delim();
out = std::format_to(out, "{}", (typename[:type_of(base):] const&)(t));
}

template for (constexpr auto mem :
define_static_array(nonstatic_data_members_of(^^T, access_ctx))) {
delim();

std::string_view mem_label = "unnamed-member";
if constexpr (has_identifier(mem))
mem_label = identifier_of(mem);

if constexpr (is_bit_field(mem) && !has_identifier(mem))
out = std::format_to(out, "(unnamed-bitfield)");
if constexpr (std::formattable<typename[:type_of(mem):], char>)
out = std::format_to(out, ".{}={}", mem_label, t.[:mem:]);
else
out = std::format_to(out, ".{}={}", mem_label, format(t.[:mem:]));
}

*out++ = '}';
return result;
}

} // namespace rsl

#if defined(RSL_INJECT_GLOBAL_FORMATTER)

# include <format>

template<typename T>
requires std::is_aggregate_v<T>
struct std::formatter<T>
{
constexpr auto parse(auto& ctx) { return ctx.begin(); }
auto format(T const& t, auto& ctx) const
{
return std::format_to(ctx.out(), "{}", rsl::format(t));
}
};
#endif
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ add_subdirectory(expect)
add_subdirectory(serializer)
add_subdirectory(string_view)
add_subdirectory(span)
add_subdirectory(format)
3 changes: 3 additions & 0 deletions test/format/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
target_sources(rsl_util_test PRIVATE
format.cpp
)
73 changes: 73 additions & 0 deletions test/format/format.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include <gtest/gtest.h>
#define RSL_INJECT_GLOBAL_FORMATTER
#include <rsl/format>

struct S {
int a;
int b;
int c;
int d;
int e;
int f;
int g;
};

struct B {
S i;
S j;
};

struct BB {
S i;
S j;
};


struct I: BB, B, S {
S Member;
};

template <>
struct std::formatter<B> : std::formatter<std::string_view> {
auto format(B const& t, auto& ctx) const {
return std::formatter<std::string_view>::format("B TYPE", ctx);
}
};

TEST(Format, Format) {
S s{1, 2, 3, 4, 5, 6, 7};
auto formatted = rsl::format(s);
ASSERT_EQ(formatted, "S{.a=1, .b=2, .c=3, .d=4, .e=5, .f=6, .g=7}");

B b{
{1, 2, 3, 4, 5, 6, 7},
{8, 9, 10, 11, 12, 13, 14}
};
auto formattedB = rsl::format(b);
ASSERT_EQ(formattedB,
"B{.i=S{.a=1, .b=2, .c=3, .d=4, .e=5, .f=6, .g=7}, .j=S{.a=8, .b=9, .c=10, .d=11, .e=12, .f=13, .g=14}}");

BB bb{
{1, 2, 3, 4, 5, 6, 7},
{8, 9, 10, 11, 12, 13, 14}
};
auto formattedBB = rsl::format(bb);
ASSERT_EQ(formattedBB,
"BB{.i=S{.a=1, .b=2, .c=3, .d=4, .e=5, .f=6, .g=7}, .j=S{.a=8, .b=9, .c=10, .d=11, .e=12, .f=13, .g=14}}");

auto formattedI = rsl::format(I{bb, b, s, S{-1, -2}});
ASSERT_EQ(formattedI,
"I{BB{.i=S{.a=1, .b=2, .c=3, .d=4, .e=5, .f=6, .g=7}, .j=S{.a=8, .b=9, .c=10, .d=11, .e=12, .f=13, .g=14}}, B TYPE, S{.a=1, .b=2, .c=3, .d=4, .e=5, .f=6, .g=7}, .Member=S{.a=-1, .b=-2, .c=0, .d=0, .e=0, .f=0, .g=0}}");

}

TEST(StringView, formatter_injection){
B b{{1, 2, 3, 4, 5, 6, 7}, {8, 9, 10, 11, 12, 13, 14}};
auto formattedB = std::format("{}", b);
ASSERT_EQ(formattedB,"B TYPE");

BB bb{{1, 2, 3, 4, 5, 6, 7}, {8, 9, 10, 11, 12, 13, 14}};
auto formattedBB = std::format("{}", bb);
ASSERT_EQ(formattedBB,
"BB{.i=S{.a=1, .b=2, .c=3, .d=4, .e=5, .f=6, .g=7}, .j=S{.a=8, .b=9, .c=10, .d=11, .e=12, .f=13, .g=14}}");
}