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 <ios> 9 #include <memory> 10 #include <sstream> 11 #include <string> 12 #include <type_traits> 13 14 #include "base/template_util.h" 15 #include "base/types/supports_ostream_operator.h" 16 17 namespace base { 18 19 namespace internal { 20 21 // I/O manipulators are function pointers, but should be sent directly to the 22 // `ostream` instead of being cast to `const void*` like other function 23 // pointers. 24 template <typename T, typename = void> 25 constexpr bool IsIomanip = false; 26 template <typename T> 27 constexpr bool 28 IsIomanip<T&(T&), std::enable_if_t<std::is_base_of_v<std::ios_base, T>>> = 29 true; 30 31 // Function pointers implicitly convert to `bool`, so use this to avoid printing 32 // function pointers as 1 or 0. 33 template <typename T, typename = void> 34 constexpr bool WillBeIncorrectlyStreamedAsBool = false; 35 template <typename T> 36 constexpr bool WillBeIncorrectlyStreamedAsBool< 37 T, 38 std::enable_if_t<std::is_function_v<std::remove_pointer_t<T>> && 39 !IsIomanip<std::remove_pointer_t<T>>>> = true; 40 41 // Fallback case when there is no better representation. 42 template <typename T, typename = void> 43 struct ToStringHelper { StringifyToStringHelper44 static void Stringify(const T& v, std::ostringstream& ss) { 45 ss << "[" << sizeof(v) << "-byte object at 0x" << std::addressof(v) << "]"; 46 } 47 }; 48 49 // Most streamables. 50 template <typename T> 51 struct ToStringHelper< 52 T, 53 std::enable_if_t<SupportsOstreamOperator<const T&>::value && 54 !WillBeIncorrectlyStreamedAsBool<T>>> { 55 static void Stringify(const T& v, std::ostringstream& ss) { ss << v; } 56 }; 57 58 // Functions and function pointers. 59 template <typename T> 60 struct ToStringHelper< 61 T, 62 std::enable_if_t<SupportsOstreamOperator<const T&>::value && 63 WillBeIncorrectlyStreamedAsBool<T>>> { 64 static void Stringify(const T& v, std::ostringstream& ss) { 65 ToStringHelper<const void*>::Stringify(reinterpret_cast<const void*>(v), 66 ss); 67 } 68 }; 69 70 // Non-streamables that have a `ToString` member. 71 template <typename T> 72 struct ToStringHelper< 73 T, 74 std::enable_if_t<!SupportsOstreamOperator<const T&>::value && 75 SupportsToString<const T&>::value>> { 76 static void Stringify(const T& v, std::ostringstream& ss) { 77 // .ToString() may not return a std::string, e.g. blink::WTF::String. 78 ToStringHelper<decltype(v.ToString())>::Stringify(v.ToString(), ss); 79 } 80 }; 81 82 // Non-streamable enums (i.e. scoped enums where no `operator<<` overload was 83 // declared). 84 template <typename T> 85 struct ToStringHelper< 86 T, 87 std::enable_if_t<!SupportsOstreamOperator<const T&>::value && 88 std::is_enum_v<T>>> { 89 static void Stringify(const T& v, std::ostringstream& ss) { 90 using UT = typename std::underlying_type_t<T>; 91 ToStringHelper<UT>::Stringify(static_cast<UT>(v), ss); 92 } 93 }; 94 95 } // namespace internal 96 97 // Converts any type to a string, preferring defined operator<<() or ToString() 98 // methods if they exist. 99 template <typename... Ts> 100 std::string ToString(const Ts&... values) { 101 std::ostringstream ss; 102 (internal::ToStringHelper<remove_cvref_t<decltype(values)>>::Stringify(values, 103 ss), 104 ...); 105 return ss.str(); 106 } 107 108 } // namespace base 109 110 #endif // BASE_STRINGS_TO_STRING_H_ 111