• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_STRINGS_TO_STRING_H_
6 #define BASE_STRINGS_TO_STRING_H_
7 
8 #include <concepts>
9 #include <ios>
10 #include <memory>
11 #include <sstream>
12 #include <string>
13 #include <tuple>
14 #include <type_traits>
15 #include <utility>
16 
17 #include "base/types/supports_ostream_operator.h"
18 
19 namespace base {
20 
21 template <typename... Ts>
22 std::string ToString(const Ts&... values);
23 
24 namespace internal {
25 
26 template <typename T>
requires(const T & t)27 concept SupportsToString = requires(const T& t) { t.ToString(); };
28 
29 // I/O manipulators are function pointers, but should be sent directly to the
30 // `ostream` instead of being cast to `const void*` like other function
31 // pointers.
32 template <typename T>
33 constexpr bool IsIomanip = false;
34 template <typename T>
35   requires(std::derived_from<T, std::ios_base>)
36 constexpr bool IsIomanip<T&(T&)> = true;
37 
38 // Function pointers implicitly convert to `bool`, so use this to avoid printing
39 // function pointers as 1 or 0.
40 template <typename T>
41 concept WillBeIncorrectlyStreamedAsBool =
42     std::is_function_v<std::remove_pointer_t<T>> &&
43     !IsIomanip<std::remove_pointer_t<T>>;
44 
45 // Fallback case when there is no better representation.
46 template <typename T>
47 struct ToStringHelper {
StringifyToStringHelper48   static void Stringify(const T& v, std::ostringstream& ss) {
49     // We cast to `void*` to avoid converting a char-like type to char-like*
50     // which operator<< treats as a string but does not support for multi-byte
51     // char-like types.
52     ss << "[" << sizeof(v) << "-byte object at 0x"
53        << static_cast<const void*>(std::addressof(v)) << "]";
54   }
55 };
56 
57 // Most streamables.
58 template <typename T>
59   requires(SupportsOstreamOperator<const T&> &&
60            !WillBeIncorrectlyStreamedAsBool<T>)
61 struct ToStringHelper<T> {
62   static void Stringify(const T& v, std::ostringstream& ss) { ss << v; }
63 };
64 
65 // Functions and function pointers.
66 template <typename T>
67   requires(SupportsOstreamOperator<const T&> &&
68            WillBeIncorrectlyStreamedAsBool<T>)
69 struct ToStringHelper<T> {
70   static void Stringify(const T& v, std::ostringstream& ss) {
71     ToStringHelper<const void*>::Stringify(reinterpret_cast<const void*>(v),
72                                            ss);
73   }
74 };
75 
76 // Integral types that can't stream, like char16_t.
77 template <typename T>
78   requires(!SupportsOstreamOperator<const T&> && std::is_integral_v<T>)
79 struct ToStringHelper<T> {
80   static void Stringify(const T& v, std::ostringstream& ss) {
81     if constexpr (std::is_signed_v<T>) {
82       static_assert(sizeof(T) <= 8);
83       ss << static_cast<int64_t>(v);
84     } else {
85       static_assert(sizeof(T) <= 8);
86       ss << static_cast<uint64_t>(v);
87     }
88   }
89 };
90 
91 // Non-streamables that have a `ToString` member.
92 template <typename T>
93   requires(!SupportsOstreamOperator<const T&> && SupportsToString<const T&>)
94 struct ToStringHelper<T> {
95   static void Stringify(const T& v, std::ostringstream& ss) {
96     // .ToString() may not return a std::string, e.g. blink::WTF::String.
97     ToStringHelper<decltype(v.ToString())>::Stringify(v.ToString(), ss);
98   }
99 };
100 
101 // Non-streamable enums (i.e. scoped enums where no `operator<<` overload was
102 // declared).
103 template <typename T>
104   requires(!SupportsOstreamOperator<const T&> && std::is_enum_v<T>)
105 struct ToStringHelper<T> {
106   static void Stringify(const T& v, std::ostringstream& ss) {
107     using UT = typename std::underlying_type_t<T>;
108     ToStringHelper<UT>::Stringify(static_cast<UT>(v), ss);
109   }
110 };
111 
112 // Tuples. Will recursively apply `ToString()` to each value in the tuple.
113 template <typename... T>
114 struct ToStringHelper<std::tuple<T...>> {
115   template <size_t... I>
116   static void StringifyHelper(const std::tuple<T...>& values,
117                               std::index_sequence<I...>,
118                               std::ostringstream& ss) {
119     ss << "<";
120     (..., (ss << (I == 0 ? "" : ", "), ss << ToString(std::get<I>(values))));
121     ss << ">";
122   }
123 
124   static void Stringify(const std::tuple<T...>& v, std::ostringstream& ss) {
125     StringifyHelper(v, std::make_index_sequence<sizeof...(T)>(), ss);
126   }
127 };
128 
129 }  // namespace internal
130 
131 // Converts any type to a string, preferring defined operator<<() or ToString()
132 // methods if they exist. If multiple `values` are given, returns the
133 // concatenation of the result of applying `ToString` to each value.
134 template <typename... Ts>
135 std::string ToString(const Ts&... values) {
136   std::ostringstream ss;
137   (...,
138    internal::ToStringHelper<std::remove_cvref_t<decltype(values)>>::Stringify(
139        values, ss));
140   return ss.str();
141 }
142 
143 }  // namespace base
144 
145 #endif  // BASE_STRINGS_TO_STRING_H_
146