mirror of
https://github.com/catchorg/Catch2.git
synced 2025-08-03 22:05:39 +02:00
Cleanup and optimize stringifying of string-like types
More specifically, made the actual implementation of string-like type handling take argument as `Catch::StringRef`, instead of taking `std::string const&`. This means that string-like types that are not `std::string` no longer need to pay for an extra construction of `std::string` (including the potential allocation), before they can be stringified. The actual string stringification routine is now also better about reserving sufficient space.
This commit is contained in:
@@ -54,6 +54,48 @@ namespace Detail {
|
||||
}
|
||||
} // end unnamed namespace
|
||||
|
||||
std::string convertIntoString(StringRef string, bool escape_invisibles) {
|
||||
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 (!escape_invisibles) {
|
||||
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;
|
||||
@@ -80,44 +122,25 @@ namespace Detail {
|
||||
//// ======================================================= ////
|
||||
|
||||
std::string StringMaker<std::string>::convert(const std::string& str) {
|
||||
if (!getCurrentContext().getConfig()->showInvisibles()) {
|
||||
return '"' + str + '"';
|
||||
}
|
||||
|
||||
std::string s("\"");
|
||||
for (char c : str) {
|
||||
switch (c) {
|
||||
case '\n':
|
||||
s.append("\\n");
|
||||
break;
|
||||
case '\t':
|
||||
s.append("\\t");
|
||||
break;
|
||||
default:
|
||||
s.push_back(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
s.append("\"");
|
||||
return s;
|
||||
return Detail::convertIntoString( str );
|
||||
}
|
||||
|
||||
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
|
||||
std::string StringMaker<std::string_view>::convert(std::string_view str) {
|
||||
return ::Catch::Detail::stringify(std::string{ str });
|
||||
return Detail::convertIntoString( StringRef( str.data(), str.size() ) );
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string StringMaker<char const*>::convert(char const* str) {
|
||||
if (str) {
|
||||
return ::Catch::Detail::stringify(std::string{ str });
|
||||
return Detail::convertIntoString( str );
|
||||
} else {
|
||||
return{ "{null string}" };
|
||||
}
|
||||
}
|
||||
std::string StringMaker<char*>::convert(char* str) {
|
||||
if (str) {
|
||||
return ::Catch::Detail::stringify(std::string{ str });
|
||||
return Detail::convertIntoString( str );
|
||||
} else {
|
||||
return{ "{null string}" };
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
||||
#include <catch2/internal/catch_config_wchar.hpp>
|
||||
#include <catch2/internal/catch_stream.hpp>
|
||||
@@ -32,6 +33,13 @@ namespace Catch {
|
||||
|
||||
constexpr StringRef unprintableString = "{?}"_sr;
|
||||
|
||||
//! Encases `string in quotes, and optionally escapes invisibles
|
||||
std::string convertIntoString( StringRef string, bool escapeInvisibles );
|
||||
|
||||
//! Encases `string` in quotes, and escapes invisibles if user requested
|
||||
//! it via CLI
|
||||
std::string convertIntoString( StringRef string );
|
||||
|
||||
std::string rawMemoryToString( const void *object, std::size_t size );
|
||||
|
||||
template<typename T>
|
||||
@@ -186,24 +194,31 @@ namespace Catch {
|
||||
};
|
||||
#endif // CATCH_CONFIG_WCHAR
|
||||
|
||||
// TBD: Should we use `strnlen` to ensure that we don't go out of the buffer,
|
||||
// while keeping string semantics?
|
||||
template<int SZ>
|
||||
struct StringMaker<char[SZ]> {
|
||||
static std::string convert(char const* str) {
|
||||
return ::Catch::Detail::stringify(std::string{ str });
|
||||
// Note that `strnlen` is not actually part of standard C++,
|
||||
// but both POSIX and Windows cstdlib provide it.
|
||||
return Detail::convertIntoString(
|
||||
StringRef( str, strnlen( str, SZ ) ) );
|
||||
}
|
||||
};
|
||||
template<int SZ>
|
||||
struct StringMaker<signed char[SZ]> {
|
||||
static std::string convert(signed char const* str) {
|
||||
return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
|
||||
// See the plain `char const*` overload
|
||||
auto reinterpreted = reinterpret_cast<char const*>(str);
|
||||
return Detail::convertIntoString(
|
||||
StringRef(reinterpreted, strnlen(reinterpreted, SZ)));
|
||||
}
|
||||
};
|
||||
template<int SZ>
|
||||
struct StringMaker<unsigned char[SZ]> {
|
||||
static std::string convert(unsigned char const* str) {
|
||||
return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
|
||||
// See the plain `char const*` overload
|
||||
auto reinterpreted = reinterpret_cast<char const*>(str);
|
||||
return Detail::convertIntoString(
|
||||
StringRef(reinterpreted, strnlen(reinterpreted, SZ)));
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user