Move RTCStatsMember+Interface to a separate file.

Pure move CL. Avoids circular dependency in a future CL.

Bug: webrtc:15164
Change-Id: Ide423be95db30b7f3cfaea946e18e12980175f2b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/333920
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41496}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index 341b39c..f09b353 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -773,6 +773,7 @@
   sources = [
     "stats/rtc_stats.h",
     "stats/rtc_stats_collector_callback.h",
+    "stats/rtc_stats_member.h",
     "stats/rtc_stats_report.h",
     "stats/rtcstats_objects.h",
   ]
diff --git a/api/stats/rtc_stats.h b/api/stats/rtc_stats.h
index 6cc39a3..d71b10d 100644
--- a/api/stats/rtc_stats.h
+++ b/api/stats/rtc_stats.h
@@ -20,7 +20,7 @@
 #include <utility>
 #include <vector>
 
-#include "absl/types/optional.h"
+#include "api/stats/rtc_stats_member.h"
 #include "api/units/timestamp.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/system/rtc_export.h"
@@ -28,8 +28,6 @@
 
 namespace webrtc {
 
-class RTCStatsMemberInterface;
-
 // Abstract base class for RTCStats-derived dictionaries, see
 // https://w3c.github.io/webrtc-stats/.
 //
@@ -206,185 +204,6 @@
     return parent_class::MembersOfThisObjectAndAncestors(0);                \
   }
 
-// Interface for `RTCStats` members, which have a name and a value of a type
-// defined in a subclass. Only the types listed in `Type` are supported, these
-// are implemented by `RTCStatsMember<T>`. The value of a member may be
-// undefined, the value can only be read if `is_defined`.
-class RTCStatsMemberInterface {
- public:
-  // Member value types.
-  enum Type {
-    kBool,    // bool
-    kInt32,   // int32_t
-    kUint32,  // uint32_t
-    kInt64,   // int64_t
-    kUint64,  // uint64_t
-    kDouble,  // double
-    kString,  // std::string
-
-    kSequenceBool,    // std::vector<bool>
-    kSequenceInt32,   // std::vector<int32_t>
-    kSequenceUint32,  // std::vector<uint32_t>
-    kSequenceInt64,   // std::vector<int64_t>
-    kSequenceUint64,  // std::vector<uint64_t>
-    kSequenceDouble,  // std::vector<double>
-    kSequenceString,  // std::vector<std::string>
-
-    kMapStringUint64,  // std::map<std::string, uint64_t>
-    kMapStringDouble,  // std::map<std::string, double>
-  };
-
-  virtual ~RTCStatsMemberInterface() {}
-
-  const char* name() const { return name_; }
-  virtual Type type() const = 0;
-  virtual bool is_sequence() const = 0;
-  virtual bool is_string() const = 0;
-  virtual bool is_defined() const = 0;
-  // Type and value comparator. The names are not compared. These operators are
-  // exposed for testing.
-  bool operator==(const RTCStatsMemberInterface& other) const {
-    return IsEqual(other);
-  }
-  bool operator!=(const RTCStatsMemberInterface& other) const {
-    return !(*this == other);
-  }
-  virtual std::string ValueToString() const = 0;
-  // This is the same as ValueToString except for kInt64 and kUint64 types,
-  // where the value is represented as a double instead of as an integer.
-  // Since JSON stores numbers as floating point numbers, very large integers
-  // cannot be accurately represented, so we prefer to display them as doubles
-  // instead.
-  virtual std::string ValueToJson() const = 0;
-
-  template <typename T>
-  const T& cast_to() const {
-    RTC_DCHECK_EQ(type(), T::StaticType());
-    return static_cast<const T&>(*this);
-  }
-
- protected:
-  explicit RTCStatsMemberInterface(const char* name) : name_(name) {}
-
-  virtual bool IsEqual(const RTCStatsMemberInterface& other) const = 0;
-
-  const char* const name_;
-};
-
-// Template implementation of `RTCStatsMemberInterface`.
-// The supported types are the ones described by
-// `RTCStatsMemberInterface::Type`.
-template <typename T>
-class RTCStatsMember : public RTCStatsMemberInterface {
- public:
-  explicit RTCStatsMember(const char* name)
-      : RTCStatsMemberInterface(name), value_() {}
-  RTCStatsMember(const char* name, const T& value)
-      : RTCStatsMemberInterface(name), value_(value) {}
-  RTCStatsMember(const char* name, T&& value)
-      : RTCStatsMemberInterface(name), value_(std::move(value)) {}
-  explicit RTCStatsMember(const RTCStatsMember<T>& other)
-      : RTCStatsMemberInterface(other.name_), value_(other.value_) {}
-  explicit RTCStatsMember(RTCStatsMember<T>&& other)
-      : RTCStatsMemberInterface(other.name_), value_(std::move(other.value_)) {}
-
-  static Type StaticType();
-  Type type() const override { return StaticType(); }
-  bool is_sequence() const override;
-  bool is_string() const override;
-  bool is_defined() const override { return value_.has_value(); }
-  std::string ValueToString() const override;
-  std::string ValueToJson() const override;
-
-  template <typename U>
-  inline T ValueOrDefault(U default_value) const {
-    return value_.value_or(default_value);
-  }
-
-  // Assignment operators.
-  T& operator=(const T& value) {
-    value_ = value;
-    return value_.value();
-  }
-  T& operator=(const T&& value) {
-    value_ = std::move(value);
-    return value_.value();
-  }
-
-  // Getter methods that look the same as absl::optional<T>. Please prefer these
-  // in order to unblock replacing RTCStatsMember<T> with absl::optional<T> in
-  // the future (https://crbug.com/webrtc/15164).
-  bool has_value() const { return value_.has_value(); }
-  const T& value() const { return value_.value(); }
-  T& value() { return value_.value(); }
-  T& operator*() {
-    RTC_DCHECK(value_);
-    return *value_;
-  }
-  const T& operator*() const {
-    RTC_DCHECK(value_);
-    return *value_;
-  }
-  T* operator->() {
-    RTC_DCHECK(value_);
-    return &(*value_);
-  }
-  const T* operator->() const {
-    RTC_DCHECK(value_);
-    return &(*value_);
-  }
-
- protected:
-  bool IsEqual(const RTCStatsMemberInterface& other) const override {
-    if (type() != other.type())
-      return false;
-    const RTCStatsMember<T>& other_t =
-        static_cast<const RTCStatsMember<T>&>(other);
-    return value_ == other_t.value_;
-  }
-
- private:
-  absl::optional<T> value_;
-};
-
-namespace rtc_stats_internal {
-
-typedef std::map<std::string, uint64_t> MapStringUint64;
-typedef std::map<std::string, double> MapStringDouble;
-
-}  // namespace rtc_stats_internal
-
-#define WEBRTC_DECLARE_RTCSTATSMEMBER(T)                                    \
-  template <>                                                               \
-  RTC_EXPORT RTCStatsMemberInterface::Type RTCStatsMember<T>::StaticType(); \
-  template <>                                                               \
-  RTC_EXPORT bool RTCStatsMember<T>::is_sequence() const;                   \
-  template <>                                                               \
-  RTC_EXPORT bool RTCStatsMember<T>::is_string() const;                     \
-  template <>                                                               \
-  RTC_EXPORT std::string RTCStatsMember<T>::ValueToString() const;          \
-  template <>                                                               \
-  RTC_EXPORT std::string RTCStatsMember<T>::ValueToJson() const;            \
-  extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT)             \
-      RTCStatsMember<T>
-
-WEBRTC_DECLARE_RTCSTATSMEMBER(bool);
-WEBRTC_DECLARE_RTCSTATSMEMBER(int32_t);
-WEBRTC_DECLARE_RTCSTATSMEMBER(uint32_t);
-WEBRTC_DECLARE_RTCSTATSMEMBER(int64_t);
-WEBRTC_DECLARE_RTCSTATSMEMBER(uint64_t);
-WEBRTC_DECLARE_RTCSTATSMEMBER(double);
-WEBRTC_DECLARE_RTCSTATSMEMBER(std::string);
-WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<bool>);
-WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<int32_t>);
-WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<uint32_t>);
-WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<int64_t>);
-WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<uint64_t>);
-WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<double>);
-WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<std::string>);
-WEBRTC_DECLARE_RTCSTATSMEMBER(rtc_stats_internal::MapStringUint64);
-WEBRTC_DECLARE_RTCSTATSMEMBER(rtc_stats_internal::MapStringDouble);
-
 }  // namespace webrtc
 
 #endif  // API_STATS_RTC_STATS_H_
diff --git a/api/stats/rtc_stats_member.h b/api/stats/rtc_stats_member.h
new file mode 100644
index 0000000..09e3836
--- /dev/null
+++ b/api/stats/rtc_stats_member.h
@@ -0,0 +1,207 @@
+/*
+ *  Copyright 2023 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_STATS_RTC_STATS_MEMBER_H_
+#define API_STATS_RTC_STATS_MEMBER_H_
+
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/system/rtc_export.h"
+#include "rtc_base/system/rtc_export_template.h"
+
+namespace webrtc {
+
+// Interface for `RTCStats` members, which have a name and a value of a type
+// defined in a subclass. Only the types listed in `Type` are supported, these
+// are implemented by `RTCStatsMember<T>`. The value of a member may be
+// undefined, the value can only be read if `is_defined`.
+class RTCStatsMemberInterface {
+ public:
+  // Member value types.
+  enum Type {
+    kBool,    // bool
+    kInt32,   // int32_t
+    kUint32,  // uint32_t
+    kInt64,   // int64_t
+    kUint64,  // uint64_t
+    kDouble,  // double
+    kString,  // std::string
+
+    kSequenceBool,    // std::vector<bool>
+    kSequenceInt32,   // std::vector<int32_t>
+    kSequenceUint32,  // std::vector<uint32_t>
+    kSequenceInt64,   // std::vector<int64_t>
+    kSequenceUint64,  // std::vector<uint64_t>
+    kSequenceDouble,  // std::vector<double>
+    kSequenceString,  // std::vector<std::string>
+
+    kMapStringUint64,  // std::map<std::string, uint64_t>
+    kMapStringDouble,  // std::map<std::string, double>
+  };
+
+  virtual ~RTCStatsMemberInterface() {}
+
+  const char* name() const { return name_; }
+  virtual Type type() const = 0;
+  virtual bool is_sequence() const = 0;
+  virtual bool is_string() const = 0;
+  virtual bool is_defined() const = 0;
+  // Type and value comparator. The names are not compared. These operators are
+  // exposed for testing.
+  bool operator==(const RTCStatsMemberInterface& other) const {
+    return IsEqual(other);
+  }
+  bool operator!=(const RTCStatsMemberInterface& other) const {
+    return !(*this == other);
+  }
+  virtual std::string ValueToString() const = 0;
+  // This is the same as ValueToString except for kInt64 and kUint64 types,
+  // where the value is represented as a double instead of as an integer.
+  // Since JSON stores numbers as floating point numbers, very large integers
+  // cannot be accurately represented, so we prefer to display them as doubles
+  // instead.
+  virtual std::string ValueToJson() const = 0;
+
+  template <typename T>
+  const T& cast_to() const {
+    RTC_DCHECK_EQ(type(), T::StaticType());
+    return static_cast<const T&>(*this);
+  }
+
+ protected:
+  explicit RTCStatsMemberInterface(const char* name) : name_(name) {}
+
+  virtual bool IsEqual(const RTCStatsMemberInterface& other) const = 0;
+
+  const char* const name_;
+};
+
+// Template implementation of `RTCStatsMemberInterface`.
+// The supported types are the ones described by
+// `RTCStatsMemberInterface::Type`.
+template <typename T>
+class RTCStatsMember : public RTCStatsMemberInterface {
+ public:
+  explicit RTCStatsMember(const char* name)
+      : RTCStatsMemberInterface(name), value_() {}
+  RTCStatsMember(const char* name, const T& value)
+      : RTCStatsMemberInterface(name), value_(value) {}
+  RTCStatsMember(const char* name, T&& value)
+      : RTCStatsMemberInterface(name), value_(std::move(value)) {}
+  explicit RTCStatsMember(const RTCStatsMember<T>& other)
+      : RTCStatsMemberInterface(other.name_), value_(other.value_) {}
+  explicit RTCStatsMember(RTCStatsMember<T>&& other)
+      : RTCStatsMemberInterface(other.name_), value_(std::move(other.value_)) {}
+
+  static Type StaticType();
+  Type type() const override { return StaticType(); }
+  bool is_sequence() const override;
+  bool is_string() const override;
+  bool is_defined() const override { return value_.has_value(); }
+  std::string ValueToString() const override;
+  std::string ValueToJson() const override;
+
+  template <typename U>
+  inline T ValueOrDefault(U default_value) const {
+    return value_.value_or(default_value);
+  }
+
+  // Assignment operators.
+  T& operator=(const T& value) {
+    value_ = value;
+    return value_.value();
+  }
+  T& operator=(const T&& value) {
+    value_ = std::move(value);
+    return value_.value();
+  }
+
+  // Getter methods that look the same as absl::optional<T>. Please prefer these
+  // in order to unblock replacing RTCStatsMember<T> with absl::optional<T> in
+  // the future (https://crbug.com/webrtc/15164).
+  bool has_value() const { return value_.has_value(); }
+  const T& value() const { return value_.value(); }
+  T& value() { return value_.value(); }
+  T& operator*() {
+    RTC_DCHECK(value_);
+    return *value_;
+  }
+  const T& operator*() const {
+    RTC_DCHECK(value_);
+    return *value_;
+  }
+  T* operator->() {
+    RTC_DCHECK(value_);
+    return &(*value_);
+  }
+  const T* operator->() const {
+    RTC_DCHECK(value_);
+    return &(*value_);
+  }
+
+ protected:
+  bool IsEqual(const RTCStatsMemberInterface& other) const override {
+    if (type() != other.type())
+      return false;
+    const RTCStatsMember<T>& other_t =
+        static_cast<const RTCStatsMember<T>&>(other);
+    return value_ == other_t.value_;
+  }
+
+ private:
+  absl::optional<T> value_;
+};
+
+namespace rtc_stats_internal {
+
+typedef std::map<std::string, uint64_t> MapStringUint64;
+typedef std::map<std::string, double> MapStringDouble;
+
+}  // namespace rtc_stats_internal
+
+#define WEBRTC_DECLARE_RTCSTATSMEMBER(T)                                    \
+  template <>                                                               \
+  RTC_EXPORT RTCStatsMemberInterface::Type RTCStatsMember<T>::StaticType(); \
+  template <>                                                               \
+  RTC_EXPORT bool RTCStatsMember<T>::is_sequence() const;                   \
+  template <>                                                               \
+  RTC_EXPORT bool RTCStatsMember<T>::is_string() const;                     \
+  template <>                                                               \
+  RTC_EXPORT std::string RTCStatsMember<T>::ValueToString() const;          \
+  template <>                                                               \
+  RTC_EXPORT std::string RTCStatsMember<T>::ValueToJson() const;            \
+  extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT)             \
+      RTCStatsMember<T>
+
+WEBRTC_DECLARE_RTCSTATSMEMBER(bool);
+WEBRTC_DECLARE_RTCSTATSMEMBER(int32_t);
+WEBRTC_DECLARE_RTCSTATSMEMBER(uint32_t);
+WEBRTC_DECLARE_RTCSTATSMEMBER(int64_t);
+WEBRTC_DECLARE_RTCSTATSMEMBER(uint64_t);
+WEBRTC_DECLARE_RTCSTATSMEMBER(double);
+WEBRTC_DECLARE_RTCSTATSMEMBER(std::string);
+WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<bool>);
+WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<int32_t>);
+WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<uint32_t>);
+WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<int64_t>);
+WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<uint64_t>);
+WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<double>);
+WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector<std::string>);
+WEBRTC_DECLARE_RTCSTATSMEMBER(rtc_stats_internal::MapStringUint64);
+WEBRTC_DECLARE_RTCSTATSMEMBER(rtc_stats_internal::MapStringDouble);
+
+}  // namespace webrtc
+
+#endif  // API_STATS_RTC_STATS_MEMBER_H_
diff --git a/stats/BUILD.gn b/stats/BUILD.gn
index b2a0c20..5890149 100644
--- a/stats/BUILD.gn
+++ b/stats/BUILD.gn
@@ -17,6 +17,7 @@
   cflags = []
   sources = [
     "rtc_stats.cc",
+    "rtc_stats_member.cc",
     "rtc_stats_report.cc",
     "rtcstats_objects.cc",
   ]
diff --git a/stats/rtc_stats.cc b/stats/rtc_stats.cc
index 25bde28..21e5d05f 100644
--- a/stats/rtc_stats.cc
+++ b/stats/rtc_stats.cc
@@ -12,112 +12,10 @@
 
 #include <cstdio>
 
-#include "rtc_base/arraysize.h"
-#include "rtc_base/string_encode.h"
 #include "rtc_base/strings/string_builder.h"
 
 namespace webrtc {
 
-namespace {
-
-// Produces "[a,b,c]". Works for non-vector `RTCStatsMemberInterface::Type`
-// types.
-template <typename T>
-std::string VectorToString(const std::vector<T>& vector) {
-  rtc::StringBuilder sb;
-  sb << "[";
-  const char* separator = "";
-  for (const T& element : vector) {
-    sb << separator << rtc::ToString(element);
-    separator = ",";
-  }
-  sb << "]";
-  return sb.Release();
-}
-
-// This overload is required because std::vector<bool> range loops don't
-// return references but objects, causing -Wrange-loop-analysis diagnostics.
-std::string VectorToString(const std::vector<bool>& vector) {
-  rtc::StringBuilder sb;
-  sb << "[";
-  const char* separator = "";
-  for (bool element : vector) {
-    sb << separator << rtc::ToString(element);
-    separator = ",";
-  }
-  sb << "]";
-  return sb.Release();
-}
-
-// Produces "[\"a\",\"b\",\"c\"]". Works for vectors of both const char* and
-// std::string element types.
-template <typename T>
-std::string VectorOfStringsToString(const std::vector<T>& strings) {
-  rtc::StringBuilder sb;
-  sb << "[";
-  const char* separator = "";
-  for (const T& element : strings) {
-    sb << separator << "\"" << rtc::ToString(element) << "\"";
-    separator = ",";
-  }
-  sb << "]";
-  return sb.Release();
-}
-
-template <typename T>
-std::string MapToString(const std::map<std::string, T>& map) {
-  rtc::StringBuilder sb;
-  sb << "{";
-  const char* separator = "";
-  for (const auto& element : map) {
-    sb << separator << rtc::ToString(element.first) << ":"
-       << rtc::ToString(element.second);
-    separator = ",";
-  }
-  sb << "}";
-  return sb.Release();
-}
-
-template <typename T>
-std::string ToStringAsDouble(const T value) {
-  // JSON represents numbers as floating point numbers with about 15 decimal
-  // digits of precision.
-  char buf[32];
-  const int len = std::snprintf(&buf[0], arraysize(buf), "%.16g",
-                                static_cast<double>(value));
-  RTC_DCHECK_LE(len, arraysize(buf));
-  return std::string(&buf[0], len);
-}
-
-template <typename T>
-std::string VectorToStringAsDouble(const std::vector<T>& vector) {
-  rtc::StringBuilder sb;
-  sb << "[";
-  const char* separator = "";
-  for (const T& element : vector) {
-    sb << separator << ToStringAsDouble<T>(element);
-    separator = ",";
-  }
-  sb << "]";
-  return sb.Release();
-}
-
-template <typename T>
-std::string MapToStringAsDouble(const std::map<std::string, T>& map) {
-  rtc::StringBuilder sb;
-  sb << "{";
-  const char* separator = "";
-  for (const auto& element : map) {
-    sb << separator << "\"" << rtc::ToString(element.first)
-       << "\":" << ToStringAsDouble(element.second);
-    separator = ",";
-  }
-  sb << "}";
-  return sb.Release();
-}
-
-}  // namespace
-
 bool RTCStats::operator==(const RTCStats& other) const {
   if (type() != other.type() || id() != other.id())
     return false;
@@ -172,126 +70,4 @@
   return members;
 }
 
-#define WEBRTC_DEFINE_RTCSTATSMEMBER(T, type, is_seq, is_str, to_str, to_json) \
-  template <>                                                                  \
-  RTCStatsMemberInterface::Type RTCStatsMember<T>::StaticType() {              \
-    return type;                                                               \
-  }                                                                            \
-  template <>                                                                  \
-  bool RTCStatsMember<T>::is_sequence() const {                                \
-    return is_seq;                                                             \
-  }                                                                            \
-  template <>                                                                  \
-  bool RTCStatsMember<T>::is_string() const {                                  \
-    return is_str;                                                             \
-  }                                                                            \
-  template <>                                                                  \
-  std::string RTCStatsMember<T>::ValueToString() const {                       \
-    RTC_DCHECK(value_.has_value());                                            \
-    return to_str;                                                             \
-  }                                                                            \
-  template <>                                                                  \
-  std::string RTCStatsMember<T>::ValueToJson() const {                         \
-    RTC_DCHECK(value_.has_value());                                            \
-    return to_json;                                                            \
-  }                                                                            \
-  template class RTC_EXPORT_TEMPLATE_DEFINE(RTC_EXPORT) RTCStatsMember<T>
-
-WEBRTC_DEFINE_RTCSTATSMEMBER(bool,
-                             kBool,
-                             false,
-                             false,
-                             rtc::ToString(*value_),
-                             rtc::ToString(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(int32_t,
-                             kInt32,
-                             false,
-                             false,
-                             rtc::ToString(*value_),
-                             rtc::ToString(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(uint32_t,
-                             kUint32,
-                             false,
-                             false,
-                             rtc::ToString(*value_),
-                             rtc::ToString(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(int64_t,
-                             kInt64,
-                             false,
-                             false,
-                             rtc::ToString(*value_),
-                             ToStringAsDouble(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(uint64_t,
-                             kUint64,
-                             false,
-                             false,
-                             rtc::ToString(*value_),
-                             ToStringAsDouble(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(double,
-                             kDouble,
-                             false,
-                             false,
-                             rtc::ToString(*value_),
-                             ToStringAsDouble(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(std::string,
-                             kString,
-                             false,
-                             true,
-                             *value_,
-                             *value_);
-WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<bool>,
-                             kSequenceBool,
-                             true,
-                             false,
-                             VectorToString(*value_),
-                             VectorToString(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<int32_t>,
-                             kSequenceInt32,
-                             true,
-                             false,
-                             VectorToString(*value_),
-                             VectorToString(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<uint32_t>,
-                             kSequenceUint32,
-                             true,
-                             false,
-                             VectorToString(*value_),
-                             VectorToString(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<int64_t>,
-                             kSequenceInt64,
-                             true,
-                             false,
-                             VectorToString(*value_),
-                             VectorToStringAsDouble(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<uint64_t>,
-                             kSequenceUint64,
-                             true,
-                             false,
-                             VectorToString(*value_),
-                             VectorToStringAsDouble(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<double>,
-                             kSequenceDouble,
-                             true,
-                             false,
-                             VectorToString(*value_),
-                             VectorToStringAsDouble(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<std::string>,
-                             kSequenceString,
-                             true,
-                             false,
-                             VectorOfStringsToString(*value_),
-                             VectorOfStringsToString(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(rtc_stats_internal::MapStringUint64,
-                             kMapStringUint64,
-                             false,
-                             false,
-                             MapToString(*value_),
-                             MapToStringAsDouble(*value_));
-WEBRTC_DEFINE_RTCSTATSMEMBER(rtc_stats_internal::MapStringDouble,
-                             kMapStringDouble,
-                             false,
-                             false,
-                             MapToString(*value_),
-                             MapToStringAsDouble(*value_));
-
 }  // namespace webrtc
diff --git a/stats/rtc_stats_member.cc b/stats/rtc_stats_member.cc
new file mode 100644
index 0000000..d7a3f6c
--- /dev/null
+++ b/stats/rtc_stats_member.cc
@@ -0,0 +1,240 @@
+/*
+ *  Copyright 2023 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/stats/rtc_stats_member.h"
+
+#include "rtc_base/arraysize.h"
+#include "rtc_base/strings/string_builder.h"
+
+namespace webrtc {
+
+namespace {
+
+// Produces "[a,b,c]". Works for non-vector `RTCStatsMemberInterface::Type`
+// types.
+template <typename T>
+std::string VectorToString(const std::vector<T>& vector) {
+  rtc::StringBuilder sb;
+  sb << "[";
+  const char* separator = "";
+  for (const T& element : vector) {
+    sb << separator << rtc::ToString(element);
+    separator = ",";
+  }
+  sb << "]";
+  return sb.Release();
+}
+
+// This overload is required because std::vector<bool> range loops don't
+// return references but objects, causing -Wrange-loop-analysis diagnostics.
+std::string VectorToString(const std::vector<bool>& vector) {
+  rtc::StringBuilder sb;
+  sb << "[";
+  const char* separator = "";
+  for (bool element : vector) {
+    sb << separator << rtc::ToString(element);
+    separator = ",";
+  }
+  sb << "]";
+  return sb.Release();
+}
+
+// Produces "[\"a\",\"b\",\"c\"]". Works for vectors of both const char* and
+// std::string element types.
+template <typename T>
+std::string VectorOfStringsToString(const std::vector<T>& strings) {
+  rtc::StringBuilder sb;
+  sb << "[";
+  const char* separator = "";
+  for (const T& element : strings) {
+    sb << separator << "\"" << rtc::ToString(element) << "\"";
+    separator = ",";
+  }
+  sb << "]";
+  return sb.Release();
+}
+
+template <typename T>
+std::string MapToString(const std::map<std::string, T>& map) {
+  rtc::StringBuilder sb;
+  sb << "{";
+  const char* separator = "";
+  for (const auto& element : map) {
+    sb << separator << rtc::ToString(element.first) << ":"
+       << rtc::ToString(element.second);
+    separator = ",";
+  }
+  sb << "}";
+  return sb.Release();
+}
+
+template <typename T>
+std::string ToStringAsDouble(const T value) {
+  // JSON represents numbers as floating point numbers with about 15 decimal
+  // digits of precision.
+  char buf[32];
+  const int len = std::snprintf(&buf[0], arraysize(buf), "%.16g",
+                                static_cast<double>(value));
+  RTC_DCHECK_LE(len, arraysize(buf));
+  return std::string(&buf[0], len);
+}
+
+template <typename T>
+std::string VectorToStringAsDouble(const std::vector<T>& vector) {
+  rtc::StringBuilder sb;
+  sb << "[";
+  const char* separator = "";
+  for (const T& element : vector) {
+    sb << separator << ToStringAsDouble<T>(element);
+    separator = ",";
+  }
+  sb << "]";
+  return sb.Release();
+}
+
+template <typename T>
+std::string MapToStringAsDouble(const std::map<std::string, T>& map) {
+  rtc::StringBuilder sb;
+  sb << "{";
+  const char* separator = "";
+  for (const auto& element : map) {
+    sb << separator << "\"" << rtc::ToString(element.first)
+       << "\":" << ToStringAsDouble(element.second);
+    separator = ",";
+  }
+  sb << "}";
+  return sb.Release();
+}
+
+}  // namespace
+
+#define WEBRTC_DEFINE_RTCSTATSMEMBER(T, type, is_seq, is_str, to_str, to_json) \
+  template <>                                                                  \
+  RTCStatsMemberInterface::Type RTCStatsMember<T>::StaticType() {              \
+    return type;                                                               \
+  }                                                                            \
+  template <>                                                                  \
+  bool RTCStatsMember<T>::is_sequence() const {                                \
+    return is_seq;                                                             \
+  }                                                                            \
+  template <>                                                                  \
+  bool RTCStatsMember<T>::is_string() const {                                  \
+    return is_str;                                                             \
+  }                                                                            \
+  template <>                                                                  \
+  std::string RTCStatsMember<T>::ValueToString() const {                       \
+    RTC_DCHECK(value_.has_value());                                            \
+    return to_str;                                                             \
+  }                                                                            \
+  template <>                                                                  \
+  std::string RTCStatsMember<T>::ValueToJson() const {                         \
+    RTC_DCHECK(value_.has_value());                                            \
+    return to_json;                                                            \
+  }                                                                            \
+  template class RTC_EXPORT_TEMPLATE_DEFINE(RTC_EXPORT) RTCStatsMember<T>
+
+WEBRTC_DEFINE_RTCSTATSMEMBER(bool,
+                             kBool,
+                             false,
+                             false,
+                             rtc::ToString(*value_),
+                             rtc::ToString(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(int32_t,
+                             kInt32,
+                             false,
+                             false,
+                             rtc::ToString(*value_),
+                             rtc::ToString(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(uint32_t,
+                             kUint32,
+                             false,
+                             false,
+                             rtc::ToString(*value_),
+                             rtc::ToString(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(int64_t,
+                             kInt64,
+                             false,
+                             false,
+                             rtc::ToString(*value_),
+                             ToStringAsDouble(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(uint64_t,
+                             kUint64,
+                             false,
+                             false,
+                             rtc::ToString(*value_),
+                             ToStringAsDouble(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(double,
+                             kDouble,
+                             false,
+                             false,
+                             rtc::ToString(*value_),
+                             ToStringAsDouble(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(std::string,
+                             kString,
+                             false,
+                             true,
+                             *value_,
+                             *value_);
+WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<bool>,
+                             kSequenceBool,
+                             true,
+                             false,
+                             VectorToString(*value_),
+                             VectorToString(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<int32_t>,
+                             kSequenceInt32,
+                             true,
+                             false,
+                             VectorToString(*value_),
+                             VectorToString(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<uint32_t>,
+                             kSequenceUint32,
+                             true,
+                             false,
+                             VectorToString(*value_),
+                             VectorToString(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<int64_t>,
+                             kSequenceInt64,
+                             true,
+                             false,
+                             VectorToString(*value_),
+                             VectorToStringAsDouble(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<uint64_t>,
+                             kSequenceUint64,
+                             true,
+                             false,
+                             VectorToString(*value_),
+                             VectorToStringAsDouble(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<double>,
+                             kSequenceDouble,
+                             true,
+                             false,
+                             VectorToString(*value_),
+                             VectorToStringAsDouble(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(std::vector<std::string>,
+                             kSequenceString,
+                             true,
+                             false,
+                             VectorOfStringsToString(*value_),
+                             VectorOfStringsToString(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(rtc_stats_internal::MapStringUint64,
+                             kMapStringUint64,
+                             false,
+                             false,
+                             MapToString(*value_),
+                             MapToStringAsDouble(*value_));
+WEBRTC_DEFINE_RTCSTATSMEMBER(rtc_stats_internal::MapStringDouble,
+                             kMapStringDouble,
+                             false,
+                             false,
+                             MapToString(*value_),
+                             MapToStringAsDouble(*value_));
+
+}  // namespace webrtc