Skip to content

Commit 1ca5f87

Browse files
committed
:soarkles: Allow fmt formatter to format runtime enum values
Problem: - Runtime enumerations cannot in general be formatted by the fmt formatter, but work fine with the binary formatter. Solution: - Decay enumeration values to their `underlying_type` iff no `format_as` function is provided for them.
1 parent 3a6da7f commit 1ca5f87

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

include/log/fmt/logger.hpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ namespace fmt_detail {
2424
using namespace std::string_view_literals;
2525
constexpr std::array level_text{"MAX"sv, "FATAL"sv, "ERROR"sv, "WARN"sv,
2626
"INFO"sv, "USER1"sv, "USER2"sv, "TRACE"sv};
27+
28+
template <typename T> auto decay_enum_value(T const &t) -> decltype(auto) {
29+
if constexpr (requires { format_as(t); }) {
30+
return (t);
31+
} else if constexpr (std::is_enum_v<T>) {
32+
return stdx::to_underlying(t);
33+
} else {
34+
return (t);
35+
}
36+
}
2737
} // namespace fmt_detail
2838

2939
template <logging::level L>
@@ -55,7 +65,8 @@ template <typename TDestinations> struct log_handler {
5565
constexpr auto fmtstr =
5666
std::string_view{decltype(fr.str)::value};
5767
fr.args.apply([&](auto const &...args) {
58-
::fmt::format_to(out, fmtstr, args...);
68+
::fmt::format_to(out, fmtstr,
69+
fmt_detail::decay_enum_value(args)...);
5970
});
6071
*out = '\n';
6172
},

test/log/fmt_logger.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,35 @@ TEST_CASE("logging doesn't use dynamic memory", "[fmt_logger]") {
5353
}
5454

5555
TEST_CASE("logging behavior can be properly overridden", "[fmt_logger]") {
56-
buffer.reserve(100);
5756
buffer.clear();
5857
log_test_override();
5958
CAPTURE(buffer);
6059
CHECK(buffer.substr(buffer.size() - std::size("Hello")) == "Hello\n");
6160
}
6261

62+
namespace detail {
63+
enum struct E1 { A, B, C };
64+
enum struct E2 { A, B, C };
65+
[[maybe_unused]] static auto format_as(E2) { return 42; }
66+
} // namespace detail
67+
68+
TEST_CASE("fmt logger can deal with runtime enum values", "[fmt_logger]") {
69+
buffer.clear();
70+
auto x = detail::E1::A;
71+
CIB_INFO("Hello {}", x);
72+
CAPTURE(buffer);
73+
CHECK(buffer.substr(buffer.size() - std::size("Hello 0")) == "Hello 0\n");
74+
}
75+
76+
TEST_CASE("fmt logger can deal with formattable runtime enum values",
77+
"[fmt_logger]") {
78+
buffer.clear();
79+
auto x = detail::E2::A;
80+
CIB_INFO("Hello {}", x);
81+
CAPTURE(buffer);
82+
CHECK(buffer.substr(buffer.size() - std::size("Hello 42")) == "Hello 42\n");
83+
}
84+
6385
TEST_CASE("log levels are properly represented", "[fmt_logger]") {
6486
{
6587
std::string level{};

0 commit comments

Comments
 (0)