Allows absl::StrCat to accept types that implement AbslStringify()

PiperOrigin-RevId: 478050535
Change-Id: I8e4a4b01aceb8d712476101633eac0ce8647823a
diff --git a/absl/strings/str_cat.cc b/absl/strings/str_cat.cc
index 2f9df67..6981347 100644
--- a/absl/strings/str_cat.cc
+++ b/absl/strings/str_cat.cc
@@ -17,16 +17,52 @@
 #include <assert.h>
 
 #include <algorithm>
+#include <cstddef>
 #include <cstdint>
 #include <cstring>
+#include <string>
 
 #include "absl/strings/ascii.h"
 #include "absl/strings/internal/resize_uninitialized.h"
 #include "absl/strings/numbers.h"
+#include "absl/strings/string_view.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
+namespace strings_internal {
+void StringifySink::Append(size_t count, char ch) { buffer_.append(count, ch); }
+
+void StringifySink::Append(string_view v) {
+  buffer_.append(v.data(), v.size());
+}
+
+bool StringifySink::PutPaddedString(string_view v, int width, int precision,
+                                    bool left) {
+  size_t space_remaining = 0;
+
+  if (width >= 0) space_remaining = static_cast<size_t>(width);
+
+  size_t n = v.size();
+
+  if (precision >= 0) n = (std::min)(n, static_cast<size_t>(precision));
+
+  string_view shown(v.data(), n);
+
+  if (shown.size() < space_remaining) {
+    space_remaining = space_remaining - shown.size();
+  } else {
+    space_remaining = 0;
+  }
+
+  if (!left) Append(space_remaining, ' ');
+  Append(shown);
+  if (left) Append(space_remaining, ' ');
+  return true;
+}
+
+}  // namespace strings_internal
+
 AlphaNum::AlphaNum(Hex hex) {
   static_assert(numbers_internal::kFastToBufferSize >= 32,
                 "This function only works when output buffer >= 32 bytes long");
diff --git a/absl/strings/str_cat.h b/absl/strings/str_cat.h
index a94bc5d..05728ab 100644
--- a/absl/strings/str_cat.h
+++ b/absl/strings/str_cat.h
@@ -57,6 +57,7 @@
 #include <cstdint>
 #include <string>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "absl/base/port.h"
@@ -76,6 +77,27 @@
   size_t size;
 };
 
+class StringifySink {
+ public:
+  void Append(size_t count, char ch);
+
+  void Append(string_view v);
+
+  bool PutPaddedString(string_view v, int width, int precision, bool left);
+
+  template <typename T>
+  friend string_view ExtractStringification(StringifySink& sink, const T& v);
+
+ private:
+  std::string buffer_;
+};
+
+template <typename T>
+string_view ExtractStringification(StringifySink& sink, const T& v) {
+  AbslStringify(sink, v);
+  return sink.buffer_;
+}
+
 }  // namespace strings_internal
 
 // Enum that specifies the number of significant digits to return in a `Hex` or
@@ -208,6 +230,15 @@
 // `StrAppend()`, providing efficient conversion of numeric, boolean, and
 // hexadecimal values (through the `Hex` type) into strings.
 
+template <typename T, typename = void>
+struct HasAbslStringify : std::false_type {};
+
+template <typename T>
+struct HasAbslStringify<T, std::enable_if_t<std::is_void<decltype(AbslStringify(
+                               std::declval<strings_internal::StringifySink&>(),
+                               std::declval<const T&>()))>::value>>
+    : std::true_type {};
+
 class AlphaNum {
  public:
   // No bool ctor -- bools convert to an integral type.
@@ -255,6 +286,13 @@
       : piece_(NullSafeStringView(c_str)) {}      // NOLINT(runtime/explicit)
   AlphaNum(absl::string_view pc) : piece_(pc) {}  // NOLINT(runtime/explicit)
 
+  template <typename T, typename = typename std::enable_if<
+                            HasAbslStringify<T>::value>::type>
+  AlphaNum(                                         // NOLINT(runtime/explicit)
+      const T& v,                                   // NOLINT(runtime/explicit)
+      strings_internal::StringifySink&& sink = {})  // NOLINT(runtime/explicit)
+      : piece_(strings_internal::ExtractStringification(sink, v)) {}
+
   template <typename Allocator>
   AlphaNum(  // NOLINT(runtime/explicit)
       const std::basic_string<char, std::char_traits<char>, Allocator>& str)
diff --git a/absl/strings/str_cat_test.cc b/absl/strings/str_cat_test.cc
index 69df250..868b9bc 100644
--- a/absl/strings/str_cat_test.cc
+++ b/absl/strings/str_cat_test.cc
@@ -612,4 +612,24 @@
   TestFastPrints();
 }
 
+struct PointStringify {
+  template <typename FormatSink>
+  friend void AbslStringify(FormatSink& sink, const PointStringify& p) {
+    sink.Append("(");
+    sink.Append(absl::StrCat(p.x));
+    sink.Append(", ");
+    sink.Append(absl::StrCat(p.y));
+    sink.Append(")");
+  }
+
+  double x = 10.0;
+  double y = 20.0;
+};
+
+TEST(StrCat, AbslStringifyExample) {
+  PointStringify p;
+  EXPECT_EQ(absl::StrCat(p), "(10, 20)");
+  EXPECT_EQ(absl::StrCat("a ", p, " z"), "a (10, 20) z");
+}
+
 }  // namespace