Skip to content

Commit

Permalink
aoc_lib: rework pretty_print::repr
Browse files Browse the repository at this point in the history
Instead of passing an arbitrary bool for results formatting, use a
struct with named flags. Currently supports `.hex_float` (what the
previous `result` parameter controlled) and `.char_as_number` (like
`aoc::as_number`, but also applies to nested values).
  • Loading branch information
yut23 committed Jan 5, 2025
1 parent bdbf8a4 commit f893ad1
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 44 deletions.
4 changes: 2 additions & 2 deletions aoc_lib/src/ds/grid.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ Grid(std::vector<std::vector<T>> &&) -> Grid<T>;

template <class T>
std::ostream &print_repr(std::ostream &os, const aoc::ds::Grid<T> &grid,
const bool result) {
const pretty_print::repr_state state) {
// Get the current field width (from std::setw()) which we will use for the
// individual values. Also set it to 0, as it shouldn't apply to the
// initial '['.
Expand All @@ -449,7 +449,7 @@ std::ostream &print_repr(std::ostream &os, const aoc::ds::Grid<T> &grid,
if (j != 0) {
os << " ";
}
os << std::setw(field_width) << pretty_print::repr(value, result);
os << std::setw(field_width) << pretty_print::repr(value, state);
++j;
}
++i;
Expand Down
5 changes: 2 additions & 3 deletions aoc_lib/src/lib.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,9 +427,8 @@ class as_number {
util::concepts::same_as_any<std::remove_const_t<T>, char,
signed char, unsigned char>;
if constexpr (is_char) {
using int_type =
std::conditional_t<std::is_signed_v<T>, short, unsigned short>;
os << static_cast<int_type>(h.dest);
// all signed or unsigned 8-bit values will fit in a short
os << static_cast<short>(h.dest);
} else {
os << h.dest;
}
Expand Down
22 changes: 16 additions & 6 deletions aoc_lib/src/test_pretty_print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,22 @@ std::size_t test_repr() {
unit_test::TestSuite suite("pretty_print::repr");

const auto test = [&suite](auto &&value, const std::string &expected,
bool result = false,
const pretty_print::repr_state state = {},
const std::source_location loc =
std::source_location::current()) {
const std::string type_name =
util::demangle(typeid(std::remove_cvref_t<decltype(value)>).name());
suite.test(type_name, [&value, &expected, &result, &loc]() {
suite.test(type_name, [&value, &expected, &state, &loc]() {
std::ostringstream oss{};
oss << pretty_print::repr(value, result);
oss << pretty_print::repr(value, state);
unit_test::checks::check_equal(oss.str(), expected, "", loc);
});
};

using namespace std::string_literals;
// templated containers
test(std::vector<int>{1, 2, 3}, "{1, 2, 3}");
test(std::initializer_list<short>{-4, 15, 6}, "{-4, 15, 6}");
test(std::list<std::set<int>>{{1, 2, 3}, {2, 3, 4}, {3, 4, 5}},
"{{1, 2, 3}, {2, 3, 4}, {3, 4, 5}}");
test(std::array<float, 4>{1.5, 2.4, 3.2, -2.8}, "{1.5, 2.4, 3.2, -2.8}");
Expand All @@ -58,15 +59,24 @@ std::size_t test_repr() {
test(std::strong_ordering::equal, "equal");
test(std::strong_ordering::equivalent, "equal");

// bool
test(true, "true");
test(false, "false");

// floating point
test(1.4f, "1.4");
test(3.14, "3.14");
// includes hex format for results
test(1.4f, "1.4 (0x1.666666p+0)", true);
test(3.14, "3.14 (0x1.91eb851eb851fp+1)", true);
// include hex value as well
test(1.4f, "1.4 (0x1.666666p+0)", {.hex_float = true});
test(3.14, "3.14 (0x1.91eb851eb851fp+1)", {.hex_float = true});

// chars
test('a', R"('a')");
test('\x10', R"('\x10')");
test('\x10', R"(16)", {.char_as_number = true});
test(std::initializer_list<std::int8_t>{-2, 0, 15, -42, 127, -128},
"{-2, 0, 15, -42, 127, -128}", {.char_as_number = true});
test(std::vector<std::uint8_t>{254, 0, 15, 214, 127, 128}, "{254, 0, 15, 214, 127, 128}", {.char_as_number = true});
return suite.done(), suite.num_failed();
}

Expand Down
75 changes: 47 additions & 28 deletions aoc_lib/src/unit_test/pretty_print.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ namespace pretty_print {
template <class T>
class repr;

struct repr_state {
bool hex_float : 1 = false;
bool char_as_number : 1 = false;
};

template <class T>
struct has_custom_print_repr : std::false_type {};

Expand Down Expand Up @@ -58,13 +63,13 @@ template <template <class, class...> class C, class T>
std::iter_value_t<decltype(std::declval<C<T>>().begin())>> &&
(!std::convertible_to<C<T>, std::string_view>)
std::ostream &print_repr(std::ostream &os, const C<T> &container,
const bool result) {
const pretty_print::repr_state state) {
os << "{";
for (auto it = container.begin(); it != container.end(); ++it) {
if (it != container.begin()) {
os << ", ";
}
os << pretty_print::repr(*it, result);
os << pretty_print::repr(*it, state);
}
os << "}";
return os;
Expand All @@ -77,13 +82,13 @@ struct pretty_print::has_custom_print_repr<std::array<T, N>> : std::true_type {

template <pretty_print::has_print_repr T, std::size_t N>
std::ostream &print_repr(std::ostream &os, const std::array<T, N> &arr,
const bool result) {
const pretty_print::repr_state state) {
os << "{";
for (auto it = std::begin(arr); it != std::end(arr); ++it) {
if (it != std::begin(arr)) {
os << ", ";
}
os << pretty_print::repr(*it, result);
os << pretty_print::repr(*it, state);
}
os << "}";
return os;
Expand All @@ -99,14 +104,14 @@ template <template <class, class, class...> class M,
pretty_print::has_print_repr K, pretty_print::has_print_repr V>
requires util::concepts::any_iterable_mapping<M<K, V>, K, V>
std::ostream &print_repr(std::ostream &os, const M<K, V> &mapping,
const bool result) {
const pretty_print::repr_state state) {
using pretty_print::repr;
os << "{";
for (auto it = mapping.begin(); it != mapping.end(); ++it) {
if (it != mapping.begin()) {
os << ", ";
}
os << repr(it->first, result) << ": " << repr(it->second, result);
os << repr(it->first, state) << ": " << repr(it->second, state);
}
os << "}";
return os;
Expand All @@ -122,11 +127,11 @@ template <template <class...> class C, pretty_print::has_print_repr... Ts>
requires std::same_as<C<Ts...>, std::tuple<Ts...>> ||
std::same_as<C<Ts...>, std::pair<Ts...>>
std::ostream &print_repr(std::ostream &os, const C<Ts...> &args,
const bool result) {
const pretty_print::repr_state state) {
os << "[";
[&]<std::size_t... I>(std::index_sequence<I...>) {
((os << (I == 0 ? "" : ", ")
<< pretty_print::repr(std::get<I>(args), result)),
<< pretty_print::repr(std::get<I>(args), state)),
...);
}(std::index_sequence_for<Ts...>{});
os << "]";
Expand All @@ -140,9 +145,9 @@ struct pretty_print::has_custom_print_repr<std::optional<T>> : std::true_type {

template <pretty_print::has_print_repr T>
std::ostream &print_repr(std::ostream &os, const std::optional<T> &opt,
const bool result) {
const pretty_print::repr_state state) {
if (opt.has_value()) {
os << pretty_print::repr(*opt, result);
os << pretty_print::repr(*opt, state);
} else {
os << "{}";
}
Expand All @@ -159,7 +164,7 @@ struct pretty_print::has_custom_print_repr<std::strong_ordering>
: std::true_type {};

std::ostream &print_repr(std::ostream &os, const std::strong_ordering &value,
const bool /*result*/) {
const pretty_print::repr_state /*state*/) {
if (value < 0) {
os << "less";
} else if (value > 0) {
Expand All @@ -175,7 +180,8 @@ template <>
struct pretty_print::has_custom_print_repr<bool> : std::true_type {};

template <std::same_as<bool> B>
std::ostream &print_repr(std::ostream &os, B b, const bool /*result*/) {
std::ostream &print_repr(std::ostream &os, B b,
const pretty_print::repr_state /*state*/) {
os << (b ? "true" : "false");
return os;
}
Expand Down Expand Up @@ -220,7 +226,7 @@ template <std::convertible_to<std::string_view> S>
struct pretty_print::has_custom_print_repr<S> : std::true_type {};

std::ostream &print_repr(std::ostream &os, std::string_view sv,
const bool /*result*/) {
const pretty_print::repr_state /*state*/) {
os << '"';
for (char ch : sv) {
write_escaped_char(os, ch, '"');
Expand All @@ -234,10 +240,16 @@ template <util::concepts::same_as_any<char, signed char, unsigned char> C>
struct pretty_print::has_custom_print_repr<C> : std::true_type {};

template <util::concepts::same_as_any<char, signed char, unsigned char> C>
std::ostream &print_repr(std::ostream &os, C ch, const bool /*result*/) {
os << '\'';
write_escaped_char(os, ch, '\'');
os << '\'';
std::ostream &print_repr(std::ostream &os, C ch,
const pretty_print::repr_state state) {
if (state.char_as_number) {
// all signed or unsigned 8-bit values will fit in a short
os << static_cast<short>(ch);
} else {
os << '\'';
write_escaped_char(os, ch, '\'');
os << '\'';
}
return os;
}

Expand All @@ -246,9 +258,10 @@ template <std::floating_point FP>
struct pretty_print::has_custom_print_repr<FP> : std::true_type {};

template <std::floating_point FP>
std::ostream &print_repr(std::ostream &os, FP x, const bool result) {
std::ostream &print_repr(std::ostream &os, FP x,
const pretty_print::repr_state state) {
os << x;
if (result) {
if (state.hex_float) {
os << " (" << std::hexfloat << x << std::defaultfloat << ")";
}
return os;
Expand All @@ -259,7 +272,8 @@ template <class T>
requires(
util::concepts::stream_insertable<T> &&
!pretty_print::has_custom_print_repr<std::remove_cvref_t<T>>::value)
std::ostream &print_repr(std::ostream &os, const T &t, const bool /*result*/) {
std::ostream &print_repr(std::ostream &os, const T &t,
const pretty_print::repr_state /*state*/) {
os << t;
return os;
}
Expand All @@ -271,15 +285,15 @@ class repr {
static_assert(has_print_repr<T>, "missing stream insertion operator (<<)");

const T &val;
bool result;
repr_state state;
friend std::ostream &operator<<(std::ostream &os, const repr &r) {
print_repr(os, r.val, r.result);
print_repr(os, r.val, r.state);
return os;
}

public:
explicit repr(const T &val, bool result = false)
: val(val), result(result) {}
explicit repr(const T &val, const repr_state state = {})
: val(val), state(state) {}
};

} // namespace pretty_print
Expand All @@ -289,17 +303,22 @@ class repr {
namespace {
template <class T>
void _lint_helper_template(std::ostream &os, const T &t = T()) {
print_repr(os, t, true);
print_repr(os, t, false);
print_repr(os, t, {.hex_float = false});
print_repr(os, t, {.hex_float = true, .char_as_number = true});
os << pretty_print::repr(t);
os << pretty_print::repr(t, true);
os << pretty_print::repr(t, false);
os << pretty_print::repr(t, {.hex_float = true});
os << pretty_print::repr(t, {.char_as_number = true});
os << pretty_print::repr(t, {.hex_float = true, .char_as_number = true});
}
[[maybe_unused]] void _lint_helper(std::ostream &os) {
_lint_helper_template<int>(os);
_lint_helper_template<bool>(os);
_lint_helper_template<double>(os);
_lint_helper_template<char>(os);
_lint_helper_template<std::string>(os);
_lint_helper_template<std::string_view>(os);
_lint_helper_template<char *>(os);
_lint_helper_template<const char *>(os);
_lint_helper_template<std::vector<int>>(os);
_lint_helper_template<std::list<std::string>>(os);
_lint_helper_template<std::set<long>>(os);
Expand Down
11 changes: 6 additions & 5 deletions aoc_lib/src/unit_test/unit_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,13 +486,14 @@ struct TestRunner {
auto expected_comp = convert<R, Input, Compare>(expected);
if (!test_equality(result_comp, expected_comp, float_ulps)) {
std::ostringstream args_ss, expected_ss, actual_ss, output_ss;
args_ss << pretty_print::repr(get_args_for_printing(), false);
args_ss << pretty_print::repr(get_args_for_printing());
// add indents to each line of the output
for (std::string line; std::getline(captured_cout, line);) {
output_ss << " # " << line << '\n';
}
expected_ss << pretty_print::repr(expected_comp, true);
actual_ss << pretty_print::repr(result_comp, true);
expected_ss << pretty_print::repr(expected_comp,
{.hex_float = true});
actual_ss << pretty_print::repr(result_comp, {.hex_float = true});
return {
.passed = false,
.num_args = sizeof...(Args),
Expand Down Expand Up @@ -687,9 +688,9 @@ TestResult equal_failure_helper(const auto &actual, const auto &expected,
std::remove_cvref_t<decltype(expected)>>) {
// we can pretty-print the values being compared
std::ostringstream actual_ss, expected_ss;
actual_ss << pretty_print::repr(actual, true);
actual_ss << pretty_print::repr(actual, {.hex_float = true});
result.actual = actual_ss.str();
expected_ss << pretty_print::repr(expected, true);
expected_ss << pretty_print::repr(expected, {.hex_float = true});
result.expected = expected_ss.str();
}
return result;
Expand Down

0 comments on commit f893ad1

Please sign in to comment.