pw_metric: add _STATIC variants

This adds PW_METRIC_STATIC and PW_METRIC_GROUP_STATIC, which do the same
as their non-_STATIC siblings, except that the object that they create
gets a `static` storage.

This can be useful, e.g. to have metrics inside of functions, when the
function itself is to be instrumented.

This CL also removes an extraneous stringification in the metric and
group names, which resulted in double double quotes.

Ah, and I also implemented PW_METRIC_GROUP with 3 parameters, which was
referenced in the documentation but not formally implemented.

Change-Id: Ife06f3759c49cb5cc15a571334e65b8f48051a6c
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/16980
Commit-Queue: Paul Mathieu <paulmathieu@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
diff --git a/pw_metric/docs.rst b/pw_metric/docs.rst
index 0f98e5b..721883c 100644
--- a/pw_metric/docs.rst
+++ b/pw_metric/docs.rst
@@ -254,6 +254,8 @@
 
 .. cpp:function:: PW_METRIC(identifier, name, value)
 .. cpp:function:: PW_METRIC(group, identifier, name, value)
+.. cpp:function:: PW_METRIC_STATIC(identifier, name, value)
+.. cpp:function:: PW_METRIC_STATIC(group, identifier, name, value)
 
   Declare a metric, optionally adding it to a group.
 
@@ -274,6 +276,9 @@
   ``pw::metric::Metric``, and works in three contexts: global, local, and
   member.
 
+  If the `_STATIC` variant is used, the macro declares a variable with static
+  storage. These can be used in function scopes, but not in classes.
+
   1. At global scope:
 
     .. code::
@@ -323,10 +328,16 @@
     centrally register in a single place.
 
 .. cpp:function:: PW_METRIC_GROUP(identifier, name)
+.. cpp:function:: PW_METRIC_GROUP(parent_group, identifier, name)
+.. cpp:function:: PW_METRIC_GROUP_STATIC(identifier, name)
+.. cpp:function:: PW_METRIC_GROUP_STATIC(parent_group, identifier, name)
 
   Declares a ``pw::metric::Group`` with name name; the name is tokenized.
   Works similar to ``PW_METRIC`` and can be used in the same contexts (global,
-  local, and member).
+  local, and member). Optionally, the group can be added to a parent group.
+
+  If the `_STATIC` variant is used, the macro declares a variable with static
+  storage. These can be used in function scopes, but not in classes.
 
   Example:
 
diff --git a/pw_metric/metric_test.cc b/pw_metric/metric_test.cc
index 980994a..0286485 100644
--- a/pw_metric/metric_test.cc
+++ b/pw_metric/metric_test.cc
@@ -210,4 +210,36 @@
   i2c_bus.stats().Dump();
 }
 
+// PW_METRIC_STATIC doesn't support class scopes, since a definition must be
+// provided outside of the class body.
+// TODO(paulmathieu): add support for class scopes and enable this test
+#if 0
+class MetricTest: public ::testing::Test {
+  public:
+    void Increment() {
+      metric_.Increment();
+    }
+
+  private:
+    PW_METRIC_STATIC(metric_, "metric", 0u);
+};
+
+TEST_F(MetricTest, StaticWithinAClass) {
+  Increment();
+}
+#endif
+
+Metric* StaticMetricIncrement() {
+  PW_METRIC_STATIC(metric, "metric", 0u);
+  metric.Increment();
+  return &metric;
+}
+
+TEST(Metric, StaticWithinAFunction) {
+  Metric* metric = StaticMetricIncrement();
+  EXPECT_EQ(metric->as_int(), 1u);
+  StaticMetricIncrement();
+  EXPECT_EQ(metric->as_int(), 2u);
+}
+
 }  // namespace pw::metric
diff --git a/pw_metric/public/pw_metric/metric.h b/pw_metric/public/pw_metric/metric.h
index 89873e9..25f79b9 100644
--- a/pw_metric/public/pw_metric/metric.h
+++ b/pw_metric/public/pw_metric/metric.h
@@ -242,7 +242,9 @@
 // NOTE: If you want a globally registered metric, see pw_metric/global.h; in
 // that contexts, metrics are globally registered without the need to centrally
 // register in a single place.
-#define PW_METRIC(...) PW_DELEGATE_BY_ARG_COUNT(_PW_METRIC_, __VA_ARGS__)
+#define PW_METRIC(...) PW_DELEGATE_BY_ARG_COUNT(_PW_METRIC_, , __VA_ARGS__)
+#define PW_METRIC_STATIC(...) \
+  PW_DELEGATE_BY_ARG_COUNT(_PW_METRIC_, static, __VA_ARGS__)
 
 // Force conversion to uint32_t for non-float types, no matter what the
 // platform uses as the "u" suffix literal. This enables dispatching to the
@@ -253,18 +255,18 @@
                      uint32_t>
 
 // Case: PW_METRIC(name, initial_value)
-#define _PW_METRIC_3(variable_name, metric_name, init)                        \
-  static constexpr uint32_t variable_name##_token =                           \
-      PW_TOKENIZE_STRING_DOMAIN("metrics", #metric_name);                     \
-  ::pw::metric::TypedMetric<_PW_METRIC_FLOAT_OR_UINT32(init)> variable_name = \
-      {variable_name##_token, init}
+#define _PW_METRIC_4(static_def, variable_name, metric_name, init)       \
+  static constexpr uint32_t variable_name##_token =                      \
+      PW_TOKENIZE_STRING_DOMAIN("metrics", metric_name);                 \
+  static_def ::pw::metric::TypedMetric<_PW_METRIC_FLOAT_OR_UINT32(init)> \
+      variable_name = {variable_name##_token, init}
 
 // Case: PW_METRIC(group, name, initial_value)
-#define _PW_METRIC_4(group, variable_name, metric_name, init)                 \
-  static constexpr uint32_t variable_name##_token =                           \
-      PW_TOKENIZE_STRING_DOMAIN("metrics", #metric_name);                     \
-  ::pw::metric::TypedMetric<_PW_METRIC_FLOAT_OR_UINT32(init)> variable_name = \
-      {variable_name##_token, init, group.metrics()}
+#define _PW_METRIC_5(static_def, group, variable_name, metric_name, init) \
+  static constexpr uint32_t variable_name##_token =                       \
+      PW_TOKENIZE_STRING_DOMAIN("metrics", metric_name);                  \
+  static_def ::pw::metric::TypedMetric<_PW_METRIC_FLOAT_OR_UINT32(init)>  \
+      variable_name = {variable_name##_token, init, group.metrics()}
 
 // Define a metric group. Works like PW_METRIC, and works in the same contexts.
 //
@@ -287,9 +289,20 @@
 //     PW_METRIC(metrics_, successes_, "successes", 0u);
 //   };
 //
-#define PW_METRIC_GROUP(variable_name, group_name)       \
-  static constexpr uint32_t variable_name##_token =      \
-      PW_TOKENIZE_STRING_DOMAIN("metrics", #group_name); \
-  ::pw::metric::Group variable_name = {variable_name##_token};
+#define PW_METRIC_GROUP(...) \
+  PW_DELEGATE_BY_ARG_COUNT(_PW_METRIC_GROUP_, , __VA_ARGS__)
+#define PW_METRIC_GROUP_STATIC(...) \
+  PW_DELEGATE_BY_ARG_COUNT(_PW_METRIC_GROUP_, static, __VA_ARGS__)
+
+#define _PW_METRIC_GROUP_3(static_def, variable_name, group_name) \
+  static constexpr uint32_t variable_name##_token =               \
+      PW_TOKENIZE_STRING_DOMAIN("metrics", group_name);           \
+  static_def ::pw::metric::Group variable_name = {variable_name##_token};
+
+#define _PW_METRIC_GROUP_4(static_def, parent, variable_name, group_name) \
+  static constexpr uint32_t variable_name##_token =                       \
+      PW_TOKENIZE_STRING_DOMAIN("metrics", group_name);                   \
+  static_def ::pw::metric::Group variable_name = {variable_name##_token,  \
+                                                  parent.children()};
 
 }  // namespace pw::metric