From dff8360855fd9fae30f9f8749bf70282c89c1a3f Mon Sep 17 00:00:00 2001 From: Ronen Friedman Date: Tue, 18 Mar 2025 04:03:39 -0500 Subject: [PATCH] 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 --- src/common/Formatter.h | 117 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/src/common/Formatter.h b/src/common/Formatter.h index 9561f44b840..05814ec66c3 100644 --- a/src/common/Formatter.h +++ b/src/common/Formatter.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -54,6 +55,122 @@ namespace ceph { } }; + /// Helper to check if a type is a map for our purpose + /// (based on fmt code) + template class is_map { + template static auto check(U*) -> typename U::mapped_type; + template static void check(...); + public: + static constexpr const bool value = + !std::is_void(nullptr))>::value; + }; + + /** + * with_array_section() + * Opens an array section and calls 'fn' on each element in the container. + * Two overloads are provided: + * 1. for maps, where the function takes a key and a value, and + * 2. for other types of containers, where the function takes just an + * element. + */ + + // for maps + template < + typename M, //!< a map + typename FN, //!< a callable to be applied to each element + typename K = std::remove_cvref_t::key_type, + typename V = std::remove_cvref_t::mapped_type> + requires( + is_map::value && ( + std::is_invocable_v || + std::is_invocable_v)) + void with_array_section(std::string_view txt, const M& m, FN&& fn) { + Formatter::ArraySection as(*this, txt); + for (const auto& [k, v] : m) { + if constexpr (std::is_invocable_v) { + std::invoke(std::forward(fn), *this, k, v, txt); + } else { + std::invoke(std::forward(fn), *this, k, v); + } + } + } + + // for other types of containers + template < + typename M, + typename FN, + typename V = std::remove_cvref_t::value_type> + requires( + !is_map::value && ( + std::is_invocable_r_v || + std::is_invocable_r_v)) + void with_array_section(std::string_view txt, const M& m, FN&& fn) { + Formatter::ArraySection as(*this, txt); + for (const auto& v : m) { + if constexpr (std::is_invocable_v< + FN, Formatter&, const V&, std::string_view>) { + std::invoke(std::forward(fn), *this, v, txt); + } else { + std::invoke(std::forward(fn), *this, v); + } + } + } + + /** + * with_obj_array_section() + * Opens an array section, then - iterates over the container + * (which can be a map or a vector) and creates an object section + * for each element in the container. The provided function 'fn' is + * called on each element in the container. + * + * Two overloads are provided: + * 1. for maps, where the function takes a key and a value, and + * 2. for other types of containers, where the function is only + * handed the object (value) in the container. + */ + + template < + typename M, //!< a map + typename FN, //!< a callable to be applied to each element + typename K = std::remove_cvref_t::key_type, + typename V = std::remove_cvref_t::mapped_type> + requires( + is_map::value && ( + std::is_invocable_v || + std::is_invocable_v)) + void with_obj_array_section(std::string_view txt, const M& m, FN&& fn) { + Formatter::ArraySection as(*this, txt); + for (const auto& [k, v] : m) { + Formatter::ObjectSection os(*this, txt); + if constexpr (std::is_invocable_v) { + std::invoke(std::forward(fn), *this, k, v, txt); + } else { + std::invoke(std::forward(fn), *this, k, v); + } + } + } + + template < + typename M, //!< a container (which is not a map) of 'V's + typename FN, + typename V = std::remove_cvref_t::value_type> + requires( + (!is_map::value) && ( + std::is_invocable_v || + std::is_invocable_v)) + void with_obj_array_section(std::string_view txt, const M& m, FN&& fn) { + Formatter::ArraySection as(*this, txt); + for (const auto& v : m) { + Formatter::ObjectSection os(*this, txt); + if constexpr (std::is_invocable_v) { + std::invoke(std::forward(fn), *this, v, txt); + } else { + std::invoke(std::forward(fn), *this, v); + } + } + } + static Formatter *create(std::string_view type, std::string_view default_type, std::string_view fallback); -- 2.39.5