Skip to content

Commit dff8360

Browse files
committed
common/Formatter: introduce helpers for formatting container types.
Specifically: - with_array_sections() creates a Formmater::ArraySection, then formats every element in the container using the provided callable. Special handling for std::map: the callable is called with both the key and the value. - with_obj_array_section() dumps containers of objects: First - an ArraySection is created; then - for each object in the container, the object is formatted within an ObjectSection, using the provided callable. If the container is an std::map, the callable is called with both the key and the object itself. Signed-off-by: Ronen Friedman <[email protected]>
1 parent 72f4cc5 commit dff8360

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

src/common/Formatter.h

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <deque>
99
#include <fstream>
10+
#include <functional>
1011
#include <list>
1112
#include <memory>
1213
#include <vector>
@@ -54,6 +55,122 @@ namespace ceph {
5455
}
5556
};
5657

58+
/// Helper to check if a type is a map for our purpose
59+
/// (based on fmt code)
60+
template <typename T> class is_map {
61+
template <typename U> static auto check(U*) -> typename U::mapped_type;
62+
template <typename> static void check(...);
63+
public:
64+
static constexpr const bool value =
65+
!std::is_void<decltype(check<T>(nullptr))>::value;
66+
};
67+
68+
/**
69+
* with_array_section()
70+
* Opens an array section and calls 'fn' on each element in the container.
71+
* Two overloads are provided:
72+
* 1. for maps, where the function takes a key and a value, and
73+
* 2. for other types of containers, where the function takes just an
74+
* element.
75+
*/
76+
77+
// for maps
78+
template <
79+
typename M, //!< a map<K, V>
80+
typename FN, //!< a callable to be applied to each element
81+
typename K = std::remove_cvref_t<M>::key_type,
82+
typename V = std::remove_cvref_t<M>::mapped_type>
83+
requires(
84+
is_map<M>::value && (
85+
std::is_invocable_v<FN, Formatter&, const K&, const V&, std::string_view> ||
86+
std::is_invocable_v<FN, Formatter&, const K&, const V&>))
87+
void with_array_section(std::string_view txt, const M& m, FN&& fn) {
88+
Formatter::ArraySection as(*this, txt);
89+
for (const auto& [k, v] : m) {
90+
if constexpr (std::is_invocable_v<FN, Formatter&, const K&, const V&, std::string_view>) {
91+
std::invoke(std::forward<FN>(fn), *this, k, v, txt);
92+
} else {
93+
std::invoke(std::forward<FN>(fn), *this, k, v);
94+
}
95+
}
96+
}
97+
98+
// for other types of containers
99+
template <
100+
typename M,
101+
typename FN,
102+
typename V = std::remove_cvref_t<M>::value_type>
103+
requires(
104+
!is_map<M>::value && (
105+
std::is_invocable_r_v<void, FN, Formatter&, const V&,
106+
std::string_view> ||
107+
std::is_invocable_r_v<void, FN, Formatter&, const V&>))
108+
void with_array_section(std::string_view txt, const M& m, FN&& fn) {
109+
Formatter::ArraySection as(*this, txt);
110+
for (const auto& v : m) {
111+
if constexpr (std::is_invocable_v<
112+
FN, Formatter&, const V&, std::string_view>) {
113+
std::invoke(std::forward<FN>(fn), *this, v, txt);
114+
} else {
115+
std::invoke(std::forward<FN>(fn), *this, v);
116+
}
117+
}
118+
}
119+
120+
/**
121+
* with_obj_array_section()
122+
* Opens an array section, then - iterates over the container
123+
* (which can be a map or a vector) and creates an object section
124+
* for each element in the container. The provided function 'fn' is
125+
* called on each element in the container.
126+
*
127+
* Two overloads are provided:
128+
* 1. for maps, where the function takes a key and a value, and
129+
* 2. for other types of containers, where the function is only
130+
* handed the object (value) in the container.
131+
*/
132+
133+
template <
134+
typename M, //!< a map<K, V>
135+
typename FN, //!< a callable to be applied to each element
136+
typename K = std::remove_cvref_t<M>::key_type,
137+
typename V = std::remove_cvref_t<M>::mapped_type>
138+
requires(
139+
is_map<M>::value && (
140+
std::is_invocable_v<FN, Formatter&, const K&, const V&, std::string_view> ||
141+
std::is_invocable_v<FN, Formatter&, const K&, const V&>))
142+
void with_obj_array_section(std::string_view txt, const M& m, FN&& fn) {
143+
Formatter::ArraySection as(*this, txt);
144+
for (const auto& [k, v] : m) {
145+
Formatter::ObjectSection os(*this, txt);
146+
if constexpr (std::is_invocable_v<FN, Formatter&, const K&, const V&, std::string_view>) {
147+
std::invoke(std::forward<FN>(fn), *this, k, v, txt);
148+
} else {
149+
std::invoke(std::forward<FN>(fn), *this, k, v);
150+
}
151+
}
152+
}
153+
154+
template <
155+
typename M, //!< a container (which is not a map) of 'V's
156+
typename FN,
157+
typename V = std::remove_cvref_t<M>::value_type>
158+
requires(
159+
(!is_map<M>::value) && (
160+
std::is_invocable_v<FN, Formatter&, const V&, std::string_view> ||
161+
std::is_invocable_v<FN, Formatter&, const V&>))
162+
void with_obj_array_section(std::string_view txt, const M& m, FN&& fn) {
163+
Formatter::ArraySection as(*this, txt);
164+
for (const auto& v : m) {
165+
Formatter::ObjectSection os(*this, txt);
166+
if constexpr (std::is_invocable_v<FN, Formatter&, const V&, std::string_view>) {
167+
std::invoke(std::forward<FN>(fn), *this, v, txt);
168+
} else {
169+
std::invoke(std::forward<FN>(fn), *this, v);
170+
}
171+
}
172+
}
173+
57174
static Formatter *create(std::string_view type,
58175
std::string_view default_type,
59176
std::string_view fallback);

0 commit comments

Comments
 (0)