catch_tostring.cpp
1 2 // Copyright Catch2 Authors 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE.txt or copy at 5 // https://www.boost.org/LICENSE_1_0.txt) 6 7 // SPDX-License-Identifier: BSL-1.0 8 9 #include <catch2/catch_tostring.hpp> 10 #include <catch2/interfaces/catch_interfaces_config.hpp> 11 #include <catch2/internal/catch_context.hpp> 12 #include <catch2/internal/catch_polyfills.hpp> 13 14 #include <cmath> 15 #include <iomanip> 16 17 namespace Catch { 18 19 namespace Detail { 20 21 namespace { 22 const int hexThreshold = 255; 23 24 struct Endianness { 25 enum Arch { Big, Little }; 26 27 static Arch which() { 28 int one = 1; 29 // If the lowest byte we read is non-zero, we can assume 30 // that little endian format is used. 31 auto value = *reinterpret_cast<char*>(&one); 32 return value ? Little : Big; 33 } 34 }; 35 36 template<typename T> 37 std::string fpToString(T value, int precision) { 38 if (Catch::isnan(value)) { 39 return "nan"; 40 } 41 42 ReusableStringStream rss; 43 rss << std::setprecision(precision) 44 << std::fixed 45 << value; 46 std::string d = rss.str(); 47 std::size_t i = d.find_last_not_of('0'); 48 if (i != std::string::npos && i != d.size() - 1) { 49 if (d[i] == '.') 50 i++; 51 d = d.substr(0, i + 1); 52 } 53 return d; 54 } 55 } // end unnamed namespace 56 57 std::string convertIntoString(StringRef string, bool escape_invisibles) { 58 std::string ret; 59 // This is enough for the "don't escape invisibles" case, and a good 60 // lower bound on the "escape invisibles" case. 61 ret.reserve(string.size() + 2); 62 63 if (!escape_invisibles) { 64 ret += '"'; 65 ret += string; 66 ret += '"'; 67 return ret; 68 } 69 70 ret += '"'; 71 for (char c : string) { 72 switch (c) { 73 case '\r': 74 ret.append("\\r"); 75 break; 76 case '\n': 77 ret.append("\\n"); 78 break; 79 case '\t': 80 ret.append("\\t"); 81 break; 82 case '\f': 83 ret.append("\\f"); 84 break; 85 default: 86 ret.push_back(c); 87 break; 88 } 89 } 90 ret += '"'; 91 92 return ret; 93 } 94 95 std::string convertIntoString(StringRef string) { 96 return convertIntoString(string, getCurrentContext().getConfig()->showInvisibles()); 97 } 98 99 std::string rawMemoryToString( const void *object, std::size_t size ) { 100 // Reverse order for little endian architectures 101 int i = 0, end = static_cast<int>( size ), inc = 1; 102 if( Endianness::which() == Endianness::Little ) { 103 i = end-1; 104 end = inc = -1; 105 } 106 107 unsigned char const *bytes = static_cast<unsigned char const *>(object); 108 ReusableStringStream rss; 109 rss << "0x" << std::setfill('0') << std::hex; 110 for( ; i != end; i += inc ) 111 rss << std::setw(2) << static_cast<unsigned>(bytes[i]); 112 return rss.str(); 113 } 114 } // end Detail namespace 115 116 117 118 //// ======================================================= //// 119 // 120 // Out-of-line defs for full specialization of StringMaker 121 // 122 //// ======================================================= //// 123 124 std::string StringMaker<std::string>::convert(const std::string& str) { 125 return Detail::convertIntoString( str ); 126 } 127 128 #ifdef CATCH_CONFIG_CPP17_STRING_VIEW 129 std::string StringMaker<std::string_view>::convert(std::string_view str) { 130 return Detail::convertIntoString( StringRef( str.data(), str.size() ) ); 131 } 132 #endif 133 134 std::string StringMaker<char const*>::convert(char const* str) { 135 if (str) { 136 return Detail::convertIntoString( str ); 137 } else { 138 return{ "{null string}" }; 139 } 140 } 141 std::string StringMaker<char*>::convert(char* str) { 142 if (str) { 143 return Detail::convertIntoString( str ); 144 } else { 145 return{ "{null string}" }; 146 } 147 } 148 149 #ifdef CATCH_CONFIG_WCHAR 150 std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) { 151 std::string s; 152 s.reserve(wstr.size()); 153 for (auto c : wstr) { 154 s += (c <= 0xff) ? static_cast<char>(c) : '?'; 155 } 156 return ::Catch::Detail::stringify(s); 157 } 158 159 # ifdef CATCH_CONFIG_CPP17_STRING_VIEW 160 std::string StringMaker<std::wstring_view>::convert(std::wstring_view str) { 161 return StringMaker<std::wstring>::convert(std::wstring(str)); 162 } 163 # endif 164 165 std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) { 166 if (str) { 167 return ::Catch::Detail::stringify(std::wstring{ str }); 168 } else { 169 return{ "{null string}" }; 170 } 171 } 172 std::string StringMaker<wchar_t *>::convert(wchar_t * str) { 173 if (str) { 174 return ::Catch::Detail::stringify(std::wstring{ str }); 175 } else { 176 return{ "{null string}" }; 177 } 178 } 179 #endif 180 181 #if defined(CATCH_CONFIG_CPP17_BYTE) 182 #include <cstddef> 183 std::string StringMaker<std::byte>::convert(std::byte value) { 184 return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value)); 185 } 186 #endif // defined(CATCH_CONFIG_CPP17_BYTE) 187 188 std::string StringMaker<int>::convert(int value) { 189 return ::Catch::Detail::stringify(static_cast<long long>(value)); 190 } 191 std::string StringMaker<long>::convert(long value) { 192 return ::Catch::Detail::stringify(static_cast<long long>(value)); 193 } 194 std::string StringMaker<long long>::convert(long long value) { 195 ReusableStringStream rss; 196 rss << value; 197 if (value > Detail::hexThreshold) { 198 rss << " (0x" << std::hex << value << ')'; 199 } 200 return rss.str(); 201 } 202 203 std::string StringMaker<unsigned int>::convert(unsigned int value) { 204 return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); 205 } 206 std::string StringMaker<unsigned long>::convert(unsigned long value) { 207 return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); 208 } 209 std::string StringMaker<unsigned long long>::convert(unsigned long long value) { 210 ReusableStringStream rss; 211 rss << value; 212 if (value > Detail::hexThreshold) { 213 rss << " (0x" << std::hex << value << ')'; 214 } 215 return rss.str(); 216 } 217 218 std::string StringMaker<signed char>::convert(signed char value) { 219 if (value == '\r') { 220 return "'\\r'"; 221 } else if (value == '\f') { 222 return "'\\f'"; 223 } else if (value == '\n') { 224 return "'\\n'"; 225 } else if (value == '\t') { 226 return "'\\t'"; 227 } else if ('\0' <= value && value < ' ') { 228 return ::Catch::Detail::stringify(static_cast<unsigned int>(value)); 229 } else { 230 char chstr[] = "' '"; 231 chstr[1] = value; 232 return chstr; 233 } 234 } 235 std::string StringMaker<char>::convert(char c) { 236 return ::Catch::Detail::stringify(static_cast<signed char>(c)); 237 } 238 std::string StringMaker<unsigned char>::convert(unsigned char c) { 239 return ::Catch::Detail::stringify(static_cast<char>(c)); 240 } 241 242 int StringMaker<float>::precision = 5; 243 244 std::string StringMaker<float>::convert(float value) { 245 return Detail::fpToString(value, precision) + 'f'; 246 } 247 248 int StringMaker<double>::precision = 10; 249 250 std::string StringMaker<double>::convert(double value) { 251 return Detail::fpToString(value, precision); 252 } 253 254 } // end namespace Catch