]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
common/Formatter: introduce helpers for formatting container types.
authorRonen Friedman <rfriedma@redhat.com>
Tue, 18 Mar 2025 09:03:39 +0000 (04:03 -0500)
committerRonen Friedman <rfriedma@redhat.com>
Mon, 31 Mar 2025 15:23:18 +0000 (10:23 -0500)
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 <rfriedma@redhat.com>
src/common/Formatter.h

index 9561f44b840469e7dc78cbf2c54163f920dd6014..05814ec66c3bffcc2d2f9173ad69857ffbe76c83 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <deque>
 #include <fstream>
+#include <functional>
 #include <list>
 #include <memory>
 #include <vector>
@@ -54,6 +55,122 @@ namespace ceph {
       }
     };
 
+    /// Helper to check if a type is a map for our purpose
+    /// (based on fmt code)
+    template <typename T> class is_map {
+      template <typename U> static auto check(U*) -> typename U::mapped_type;
+      template <typename> static void check(...);
+    public:
+      static constexpr const bool value =
+        !std::is_void<decltype(check<T>(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<K, V>
+       typename FN,    //!< a callable to be applied to each element
+       typename K = std::remove_cvref_t<M>::key_type,
+       typename V = std::remove_cvref_t<M>::mapped_type>
+      requires(
+         is_map<M>::value && (
+          std::is_invocable_v<FN, Formatter&, const K&, const V&, std::string_view> ||
+          std::is_invocable_v<FN, Formatter&, const K&, const 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<FN, Formatter&, const K&, const V&, std::string_view>) {
+         std::invoke(std::forward<FN>(fn), *this, k, v, txt);
+        } else {
+          std::invoke(std::forward<FN>(fn), *this, k, v);
+        }
+      }
+    }
+
+    // for other types of containers
+    template <
+       typename M,
+       typename FN,
+       typename V = std::remove_cvref_t<M>::value_type>
+      requires(
+         !is_map<M>::value && (
+          std::is_invocable_r_v<void, FN, Formatter&, const V&,
+              std::string_view> ||
+         std::is_invocable_r_v<void, FN, Formatter&, const 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>(fn), *this, v, txt);
+       } else {
+         std::invoke(std::forward<FN>(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<K, V>
+       typename FN,    //!< a callable to be applied to each element
+       typename K = std::remove_cvref_t<M>::key_type,
+       typename V = std::remove_cvref_t<M>::mapped_type>
+      requires(
+         is_map<M>::value && (
+          std::is_invocable_v<FN, Formatter&, const K&, const V&, std::string_view> ||
+          std::is_invocable_v<FN, Formatter&, const K&, const 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<FN, Formatter&, const K&, const V&, std::string_view>) {
+            std::invoke(std::forward<FN>(fn), *this, k, v, txt);
+        } else {
+            std::invoke(std::forward<FN>(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<M>::value_type>
+      requires(
+         (!is_map<M>::value) && (
+          std::is_invocable_v<FN, Formatter&, const V&, std::string_view> ||
+          std::is_invocable_v<FN, Formatter&, const 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<FN, Formatter&, const V&, std::string_view>) {
+            std::invoke(std::forward<FN>(fn), *this, v, txt);
+        } else {
+            std::invoke(std::forward<FN>(fn), *this, v);
+        }
+      }
+    }
+
     static Formatter *create(std::string_view type,
                             std::string_view default_type,
                             std::string_view fallback);