Skip to content

Commit

Permalink
improve pretty print for aggregate struct
Browse files Browse the repository at this point in the history
  • Loading branch information
jyf111 committed Nov 11, 2023
1 parent 12cded6 commit 36cc4f2
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 33 deletions.
3 changes: 2 additions & 1 deletion examples/demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ struct data {
};
struct Bad {
int x, y;
std::initializer_list<int> z = { 5, 9, 10 };
std::initializer_list<int> z;
};
DBG_REGISTER(Bad, x, y, z);
struct MyStruct {
struct gps {
double latitude;
Expand Down
76 changes: 63 additions & 13 deletions include/dbg.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,45 +126,39 @@ concept is_enum = std::is_enum_v<T>;
template <typename T>
concept is_union = std::is_union_v<T>;
template <typename T>
concept has_begin = requires(T t) {
concept is_iteratable = requires(const T &t) {
std::begin(t);
};
template <typename T>
concept has_end = requires(T t) {
std::end(t);
};
template <typename T>
concept has_size = requires(T t) {
std::size(t);
};
template <typename T>
concept has_ostream_operator = requires(std::ostream &os, T t) {
concept has_ostream_operator = requires(std::ostream &os, const T &t) {
os << t;
};
template <typename T>
concept is_container = has_begin<T> && has_end<T> && has_size<T> && !std::same_as<std::decay_t<T>, std::string>;
concept is_container = is_iteratable<T> && !std::same_as<std::decay_t<T>, std::string>;

// Ignore explicitly specified template arguments
template <int &...ExplicitArgumentBarrier, typename T>
requires(!std::is_enum_v<T> && !std::is_union_v<T>) std::string type_name(std::type_identity<T>) {
std::string_view pretty_name(std::source_location::current().function_name());
const auto L = pretty_name.find("T = ") + 4;
const auto R = pretty_name.find_last_of(';');
const auto R = pretty_name.find_last_of(']');
return std::string(pretty_name.substr(L, R - L));
}
template <is_enum Enum>
std::string type_name(std::type_identity<Enum>) {
std::string_view pretty_name(std::source_location::current().function_name());
const auto L = pretty_name.find("Enum = ") + 7;
const auto R = pretty_name.find_last_of(';');
const auto R = pretty_name.find_last_of(']');
return "enum " + std::string(pretty_name.substr(L, R - L)) + " : " +
type_name(std::type_identity<std::underlying_type_t<Enum>>{});
}
template <is_union Union>
std::string type_name(std::type_identity<Union>) {
std::string_view pretty_name(std::source_location::current().function_name());
const auto L = pretty_name.find("Union = ") + 8;
const auto R = pretty_name.find_last_of(';');
const auto R = pretty_name.find_last_of(']');
return "union " + std::string(pretty_name.substr(L, R - L));
}

Expand Down Expand Up @@ -524,9 +518,65 @@ void print(std::ostream &os, const Container &value) {
os << "]";
}

#if __has_builtin(__builtin_dump_struct) && __clang_major__ >= 15
template <is_aggregate Aggregate>
struct ReflectField {
static constexpr size_t n_field = flatten::num_aggregate_fields_v<Aggregate>;
inline static std::string fields[n_field];
inline static size_t i_field = 0;
static void constexpr_printf(const char *format, auto... args) {
if constexpr (sizeof...(args) > 1) {
if (i_field < n_field) {
auto tuple = std::tuple{ args... };
if (strlen(std::get<0>(tuple)) == 2) {
fields[i_field++] = std::get<2>(tuple);
}
}
}
}

inline static bool initialized = false;
static void initialize(const Aggregate &value) {
if (!initialized) {
__builtin_dump_struct(&value, constexpr_printf);
initialized = true;
}
}
};

template <is_aggregate Aggregate, size_t idx>
struct print_named_tuple {
template <typename... Ts>
void operator()(std::ostream &os, const std::tuple<Ts...> &tuple) {
print_named_tuple<Aggregate, idx - 1>()(os, tuple);
os << ", " << ReflectField<Aggregate>::fields[idx] + ": ";
print(os, std::get<idx>(tuple));
}
};
template <is_aggregate Aggregate>
struct print_named_tuple<Aggregate, 0> {
template <typename... Ts>
void operator()(std::ostream &os, const std::tuple<Ts...> &tuple) {
os << ReflectField<Aggregate>::fields[0] + ": ";
print(os, std::get<0>(tuple));
}
};
#endif

template <is_aggregate Aggregate>
requires(!is_container<Aggregate>) void print(std::ostream &os, const Aggregate &value) {
print(os, flatten::flatten_to_tuple(value));
const auto &tuple = flatten::flatten_to_tuple(value);
#if __has_builtin(__builtin_dump_struct) && __clang_major__ >= 15
ReflectField<Aggregate>::initialize(value);
os << "{";
constexpr size_t tuple_size = std::tuple_size_v<std::remove_cvref_t<decltype(tuple)>>;
if constexpr (tuple_size > 0) {
print_named_tuple<Aggregate, tuple_size - 1>()(os, tuple);
}
os << "}";
#else
print(os, tuple);
#endif
}

template <size_t idx>
Expand Down
49 changes: 30 additions & 19 deletions tests/unit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ TEST_CASE("primitive type", "[type]") {
DBG(std::move(rvalue));
}
SECTION("unvisible char") {
int8_t byte = 202;
int8_t byte = 22;
char invisible = '\x00'; // static_cast<char>(130);
char &lchar = invisible;
const char *invisible_str = "\x80\x81\x82\x83\x90";
Expand Down Expand Up @@ -242,12 +242,12 @@ TEST_CASE("variadic argument", "[variadic]") {
DBG(d);
DBG(e);
int tmparr[] = { 1, 2, 3 };
std::initializer_list tmplist = { 0, 9, -1, 2 };
std::initializer_list<int> tmplist = { 0, 9, -1, 2 };
DBG(tmparr);
DBG(tmplist);
// DBG({1, 2, 3}, {0, 9, -1, 2}); // ! NOTICE
DBG((std::initializer_list{ 1, 2, 3 }));
DBG((std::initializer_list{ 0, 9, -1, 2 }));
DBG((std::initializer_list<int>{ 1, 2, 3 }));
DBG((std::initializer_list<int>{ 0, 9, -1, 2 }));
DBG("first:");
DBG(a);
DBG("second:");
Expand All @@ -259,20 +259,20 @@ TEST_CASE("variadic argument", "[variadic]") {
TEST_CASE("base output", "[base]") {
char value = 110;
short neg = -12;
SECTION("Hex") {
DBG(dbg::Hex(value));
DBG(dbg::Hex(neg));
DBG(dbg::Hex(-neg));
}
SECTION("Bin") {
DBG(dbg::Bin(value));
DBG(dbg::Bin(neg));
DBG(dbg::Bin(-neg));
}
SECTION("Oct") {
DBG(dbg::Oct(value));
DBG(dbg::Oct(neg));
}
// SECTION("Hex") {
// DBG(dbg::Hex(value));
// DBG(dbg::Hex(neg));
// DBG(dbg::Hex(-neg));
// }
// SECTION("Bin") {
// DBG(dbg::Bin(value));
// DBG(dbg::Bin(neg));
// DBG(dbg::Bin(-neg));
// }
// SECTION("Oct") {
// DBG(dbg::Oct(value));
// DBG(dbg::Oct(neg));
// }
}

TEST_CASE("container", "[container]") {
Expand Down Expand Up @@ -306,7 +306,7 @@ TEST_CASE("container", "[container]") {
DBG(dummy_list);
}
SECTION("initializer_list") {
DBG((std::initializer_list{ "10", "20", "66" }));
DBG((std::initializer_list<std::string>{ "10", "20", "66" }));
std::string a = "12", b = "22";
DBG((std::initializer_list<std::string>{ a, b, "32" }));
}
Expand Down Expand Up @@ -491,4 +491,15 @@ TEST_CASE("miscellaneous") {
};
outer o = { { 1, 2 }, 3 };
DBG(o);
struct O {
struct i {
int a, b;
};
int x;
};
O oo = { 1 };
DBG(oo);
struct Empty {};
Empty E;
DBG(E);
}

0 comments on commit 36cc4f2

Please sign in to comment.