| |
| // Copyright Catch2 Authors |
| // Distributed under the Boost Software License, Version 1.0. |
| // (See accompanying file LICENSE.txt or copy at |
| // https://www.boost.org/LICENSE_1_0.txt) |
| |
| // SPDX-License-Identifier: BSL-1.0 |
| |
| #include <catch2/catch_tostring.hpp> |
| #include <catch2/interfaces/catch_interfaces_config.hpp> |
| #include <catch2/interfaces/catch_interfaces_registry_hub.hpp> |
| #include <catch2/internal/catch_context.hpp> |
| #include <catch2/internal/catch_polyfills.hpp> |
| |
| #include <iomanip> |
| |
| namespace Catch { |
| |
| namespace Detail { |
| |
| namespace { |
| const int hexThreshold = 255; |
| |
| struct Endianness { |
| enum Arch : uint8_t { |
| Big, |
| Little |
| }; |
| |
| static Arch which() { |
| int one = 1; |
| // If the lowest byte we read is non-zero, we can assume |
| // that little endian format is used. |
| auto value = *reinterpret_cast<char*>(&one); |
| return value ? Little : Big; |
| } |
| }; |
| |
| template<typename T> |
| std::string fpToString(T value, int precision) { |
| if (Catch::isnan(value)) { |
| return "nan"; |
| } |
| |
| ReusableStringStream rss; |
| rss << std::setprecision(precision) |
| << std::fixed |
| << value; |
| std::string d = rss.str(); |
| std::size_t i = d.find_last_not_of('0'); |
| if (i != std::string::npos && i != d.size() - 1) { |
| if (d[i] == '.') |
| i++; |
| d = d.substr(0, i + 1); |
| } |
| return d; |
| } |
| } // end unnamed namespace |
| |
| std::string convertIntoString(StringRef string, bool escapeInvisibles) { |
| std::string ret; |
| // This is enough for the "don't escape invisibles" case, and a good |
| // lower bound on the "escape invisibles" case. |
| ret.reserve(string.size() + 2); |
| |
| if (!escapeInvisibles) { |
| ret += '"'; |
| ret += string; |
| ret += '"'; |
| return ret; |
| } |
| |
| ret += '"'; |
| for (char c : string) { |
| switch (c) { |
| case '\r': |
| ret.append("\\r"); |
| break; |
| case '\n': |
| ret.append("\\n"); |
| break; |
| case '\t': |
| ret.append("\\t"); |
| break; |
| case '\f': |
| ret.append("\\f"); |
| break; |
| default: |
| ret.push_back(c); |
| break; |
| } |
| } |
| ret += '"'; |
| |
| return ret; |
| } |
| |
| std::string convertIntoString(StringRef string) { |
| return convertIntoString(string, getCurrentContext().getConfig()->showInvisibles()); |
| } |
| |
| std::string rawMemoryToString( const void *object, std::size_t size ) { |
| // Reverse order for little endian architectures |
| int i = 0, end = static_cast<int>( size ), inc = 1; |
| if( Endianness::which() == Endianness::Little ) { |
| i = end-1; |
| end = inc = -1; |
| } |
| |
| unsigned char const *bytes = static_cast<unsigned char const *>(object); |
| ReusableStringStream rss; |
| rss << "0x" << std::setfill('0') << std::hex; |
| for( ; i != end; i += inc ) |
| rss << std::setw(2) << static_cast<unsigned>(bytes[i]); |
| return rss.str(); |
| } |
| |
| std::string makeExceptionHappenedString() { |
| return "{ stringification failed with an exception: \"" + |
| translateActiveException() + "\" }"; |
| |
| } |
| |
| } // end Detail namespace |
| |
| |
| |
| //// ======================================================= //// |
| // |
| // Out-of-line defs for full specialization of StringMaker |
| // |
| //// ======================================================= //// |
| |
| std::string StringMaker<std::string>::convert(const std::string& str) { |
| return Detail::convertIntoString( str ); |
| } |
| |
| #ifdef CATCH_CONFIG_CPP17_STRING_VIEW |
| std::string StringMaker<std::string_view>::convert(std::string_view str) { |
| return Detail::convertIntoString( StringRef( str.data(), str.size() ) ); |
| } |
| #endif |
| |
| std::string StringMaker<char const*>::convert(char const* str) { |
| if (str) { |
| return Detail::convertIntoString( str ); |
| } else { |
| return{ "{null string}" }; |
| } |
| } |
| std::string StringMaker<char*>::convert(char* str) { // NOLINT(readability-non-const-parameter) |
| if (str) { |
| return Detail::convertIntoString( str ); |
| } else { |
| return{ "{null string}" }; |
| } |
| } |
| |
| #ifdef CATCH_CONFIG_WCHAR |
| std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) { |
| std::string s; |
| s.reserve(wstr.size()); |
| for (auto c : wstr) { |
| s += (c <= 0xff) ? static_cast<char>(c) : '?'; |
| } |
| return ::Catch::Detail::stringify(s); |
| } |
| |
| # ifdef CATCH_CONFIG_CPP17_STRING_VIEW |
| std::string StringMaker<std::wstring_view>::convert(std::wstring_view str) { |
| return StringMaker<std::wstring>::convert(std::wstring(str)); |
| } |
| # endif |
| |
| std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) { |
| if (str) { |
| return ::Catch::Detail::stringify(std::wstring{ str }); |
| } else { |
| return{ "{null string}" }; |
| } |
| } |
| std::string StringMaker<wchar_t *>::convert(wchar_t * str) { |
| if (str) { |
| return ::Catch::Detail::stringify(std::wstring{ str }); |
| } else { |
| return{ "{null string}" }; |
| } |
| } |
| #endif |
| |
| #if defined(CATCH_CONFIG_CPP17_BYTE) |
| #include <cstddef> |
| std::string StringMaker<std::byte>::convert(std::byte value) { |
| return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value)); |
| } |
| #endif // defined(CATCH_CONFIG_CPP17_BYTE) |
| |
| std::string StringMaker<int>::convert(int value) { |
| return ::Catch::Detail::stringify(static_cast<long long>(value)); |
| } |
| std::string StringMaker<long>::convert(long value) { |
| return ::Catch::Detail::stringify(static_cast<long long>(value)); |
| } |
| std::string StringMaker<long long>::convert(long long value) { |
| ReusableStringStream rss; |
| rss << value; |
| if (value > Detail::hexThreshold) { |
| rss << " (0x" << std::hex << value << ')'; |
| } |
| return rss.str(); |
| } |
| |
| std::string StringMaker<unsigned int>::convert(unsigned int value) { |
| return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); |
| } |
| std::string StringMaker<unsigned long>::convert(unsigned long value) { |
| return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); |
| } |
| std::string StringMaker<unsigned long long>::convert(unsigned long long value) { |
| ReusableStringStream rss; |
| rss << value; |
| if (value > Detail::hexThreshold) { |
| rss << " (0x" << std::hex << value << ')'; |
| } |
| return rss.str(); |
| } |
| |
| std::string StringMaker<signed char>::convert(signed char value) { |
| if (value == '\r') { |
| return "'\\r'"; |
| } else if (value == '\f') { |
| return "'\\f'"; |
| } else if (value == '\n') { |
| return "'\\n'"; |
| } else if (value == '\t') { |
| return "'\\t'"; |
| } else if ('\0' <= value && value < ' ') { |
| return ::Catch::Detail::stringify(static_cast<unsigned int>(value)); |
| } else { |
| char chstr[] = "' '"; |
| chstr[1] = value; |
| return chstr; |
| } |
| } |
| std::string StringMaker<char>::convert(char c) { |
| return ::Catch::Detail::stringify(static_cast<signed char>(c)); |
| } |
| std::string StringMaker<unsigned char>::convert(unsigned char value) { |
| return ::Catch::Detail::stringify(static_cast<char>(value)); |
| } |
| |
| int StringMaker<float>::precision = std::numeric_limits<float>::max_digits10; |
| |
| std::string StringMaker<float>::convert(float value) { |
| return Detail::fpToString(value, precision) + 'f'; |
| } |
| |
| int StringMaker<double>::precision = std::numeric_limits<double>::max_digits10; |
| |
| std::string StringMaker<double>::convert(double value) { |
| return Detail::fpToString(value, precision); |
| } |
| |
| } // end namespace Catch |