Various small string usage performance improvements.

* Empty strings are now direct constructed as `std::string()`, not as empty string literals.
* `startsWith` and `endsWith` no longer construct new a string. This should be an improvement
for libstdc++ when using older standards, as it doesn't use SSO but COW and thus even short
strings are expensive to first create.
* Various places now use char literal instead of string literals containing single char.
** `startsWith` and `endsWith` now also have overload that takes single char.

Generally the performance improvements under VS2015 are small, as going from short string
to char is mostly meaningless because of SSO (Catch doesn't push string handling that hard)
and previous commit removed most string handling if tests pass, which is the expect case.
This commit is contained in:
Martin Hořeňovský 2017-01-15 09:41:33 +01:00
parent 877fd523bc
commit 3b98a0166f
14 changed files with 47 additions and 33 deletions

View File

@ -85,10 +85,10 @@ namespace Catch {
std::string line; std::string line;
while( std::getline( f, line ) ) { while( std::getline( f, line ) ) {
line = trim(line); line = trim(line);
if( !line.empty() && !startsWith( line, "#" ) ) { if( !line.empty() && !startsWith( line, '#' ) ) {
if( !startsWith( line, "\"" ) ) if( !startsWith( line, '"' ) )
line = "\"" + line + "\""; line = "\"" + line + "\"";
addTestOrTags( config, line + "," ); addTestOrTags( config, line + ',' );
} }
} }
} }

View File

@ -79,7 +79,10 @@ namespace Catch {
} }
bool startsWith( std::string const& s, std::string const& prefix ); bool startsWith( std::string const& s, std::string const& prefix );
bool startsWith( std::string const& s, char prefix );
bool endsWith( std::string const& s, std::string const& suffix ); bool endsWith( std::string const& s, std::string const& suffix );
bool endsWith( std::string const& s, char suffix );
bool contains( std::string const& s, std::string const& infix );
bool contains( std::string const& s, std::string const& infix ); bool contains( std::string const& s, std::string const& infix );
void toLowerInPlace( std::string& s ); void toLowerInPlace( std::string& s );
std::string toLower( std::string const& s ); std::string toLower( std::string const& s );

View File

@ -10,17 +10,28 @@
#include "catch_common.h" #include "catch_common.h"
#include <algorithm>
namespace Catch { namespace Catch {
bool startsWith( std::string const& s, std::string const& prefix ) { bool startsWith( std::string const& s, std::string const& prefix ) {
return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
}
bool startsWith( std::string const& s, char prefix ) {
return !s.empty() && s.front() == prefix;
} }
bool endsWith( std::string const& s, std::string const& suffix ) { bool endsWith( std::string const& s, std::string const& suffix ) {
return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
}
bool endsWith( std::string const& s, char suffix ) {
return !s.empty() && s.back() == suffix;
} }
bool contains( std::string const& s, std::string const& infix ) { bool contains( std::string const& s, std::string const& infix ) {
return s.find( infix ) != std::string::npos; return s.find( infix ) != std::string::npos;
} }
bool contains( std::string const& s, char infix ) {
return s.find(infix);
}
char toLowerCh(char c) { char toLowerCh(char c) {
return static_cast<char>( ::tolower( c ) ); return static_cast<char>( ::tolower( c ) );
} }
@ -37,7 +48,7 @@ namespace Catch {
std::string::size_type start = str.find_first_not_of( whitespaceChars ); std::string::size_type start = str.find_first_not_of( whitespaceChars );
std::string::size_type end = str.find_last_not_of( whitespaceChars ); std::string::size_type end = str.find_last_not_of( whitespaceChars );
return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
} }
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
@ -95,9 +106,9 @@ namespace Catch {
std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
#ifndef __GNUG__ #ifndef __GNUG__
os << info.file << "(" << info.line << ")"; os << info.file << '(' << info.line << ')';
#else #else
os << info.file << ":" << info.line; os << info.file << ':' << info.line;
#endif #endif
return os; return os;
} }

View File

@ -51,9 +51,9 @@ namespace Catch {
} }
if( !config.testSpec().hasFilters() ) if( !config.testSpec().hasFilters() )
Catch::cout() << pluralise( matchedTests, "test case" ) << "\n" << std::endl; Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl;
else else
Catch::cout() << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl;
return matchedTests; return matchedTests;
} }
@ -68,7 +68,7 @@ namespace Catch {
++it ) { ++it ) {
matchedTests++; matchedTests++;
TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
if( startsWith( testCaseInfo.name, "#" ) ) if( startsWith( testCaseInfo.name, '#' ) )
Catch::cout() << "\"" << testCaseInfo.name << "\"" << std::endl; Catch::cout() << "\"" << testCaseInfo.name << "\"" << std::endl;
else else
Catch::cout() << testCaseInfo.name << std::endl; Catch::cout() << testCaseInfo.name << std::endl;

View File

@ -184,7 +184,7 @@ namespace Matchers {
{ {
return m_caseSensitivity == CaseSensitive::No return m_caseSensitivity == CaseSensitive::No
? " (case insensitive)" ? " (case insensitive)"
: ""; : std::string();
} }
CaseSensitive::Choice m_caseSensitivity; CaseSensitive::Choice m_caseSensitivity;
std::string m_str; std::string m_str;

View File

@ -74,7 +74,7 @@ namespace Catch {
return new T( config ); return new T( config );
} }
virtual std::string getDescription() const { virtual std::string getDescription() const {
return ""; return std::string();
} }
}; };

View File

@ -25,7 +25,7 @@ namespace Catch {
oss << other.oss.str(); oss << other.oss.str();
} }
CopyableStream& operator=( CopyableStream const& other ) { CopyableStream& operator=( CopyableStream const& other ) {
oss.str(""); oss.str(std::string());
oss << other.oss.str(); oss << other.oss.str();
return *this; return *this;
} }

View File

@ -146,7 +146,7 @@ namespace Catch {
m_messages.clear(); m_messages.clear();
// Reset working state // Reset working state
m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); m_lastAssertionInfo = AssertionInfo( std::string(), m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
m_lastResult = result; m_lastResult = result;
} }
@ -217,7 +217,7 @@ namespace Catch {
virtual std::string getCurrentTestName() const { virtual std::string getCurrentTestName() const {
return m_activeTestCase return m_activeTestCase
? m_activeTestCase->getTestCaseInfo().name ? m_activeTestCase->getTestCaseInfo().name
: ""; : std::string();
} }
virtual const AssertionResult* getLastResult() const { virtual const AssertionResult* getLastResult() const {
@ -247,11 +247,11 @@ namespace Catch {
deltaTotals.testCases.failed = 1; deltaTotals.testCases.failed = 1;
m_reporter->testCaseEnded( TestCaseStats( testInfo, m_reporter->testCaseEnded( TestCaseStats( testInfo,
deltaTotals, deltaTotals,
"", std::string(),
"", std::string(),
false ) ); false ) );
m_totals.testCases.failed++; m_totals.testCases.failed++;
testGroupEnded( "", m_totals, 1, 1 ); testGroupEnded( std::string(), m_totals, 1, 1 );
m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) );
} }
@ -270,7 +270,7 @@ namespace Catch {
Counts prevAssertions = m_totals.assertions; Counts prevAssertions = m_totals.assertions;
double duration = 0; double duration = 0;
try { try {
m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, std::string(), ResultDisposition::Normal );
seedRng( *m_config ); seedRng( *m_config );

View File

@ -43,7 +43,7 @@ namespace Catch {
void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) { if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) {
std::ostringstream oss; std::ostringstream oss;
oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo;
throw std::domain_error( oss.str().c_str() ); throw std::domain_error( oss.str().c_str() );
@ -51,7 +51,7 @@ namespace Catch {
if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
std::ostringstream oss; std::ostringstream oss;
oss << "error: tag alias, \"" << alias << "\" already registered.\n" oss << "error: tag alias, \"" << alias << "\" already registered.\n"
<< "\tFirst seen at " << find(alias)->lineInfo << "\n" << "\tFirst seen at " << find(alias)->lineInfo << '\n'
<< "\tRedefined at " << lineInfo; << "\tRedefined at " << lineInfo;
throw std::domain_error( oss.str().c_str() ); throw std::domain_error( oss.str().c_str() );
} }

View File

@ -16,7 +16,7 @@
namespace Catch { namespace Catch {
inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
if( startsWith( tag, "." ) || if( startsWith( tag, '.' ) ||
tag == "hide" || tag == "hide" ||
tag == "!hide" ) tag == "!hide" )
return TestCaseInfo::IsHidden; return TestCaseInfo::IsHidden;

View File

@ -78,7 +78,7 @@ namespace Catch {
ss << Colour( Colour::Red ) ss << Colour( Colour::Red )
<< "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
<< "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n'
<< "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
throw std::runtime_error(ss.str()); throw std::runtime_error(ss.str());
@ -110,7 +110,7 @@ namespace Catch {
virtual void registerTest( TestCase const& testCase ) { virtual void registerTest( TestCase const& testCase ) {
std::string name = testCase.getTestCaseInfo().name; std::string name = testCase.getTestCaseInfo().name;
if( name == "" ) { if( name.empty() ) {
std::ostringstream oss; std::ostringstream oss;
oss << "Anonymous test case " << ++m_unnamedCount; oss << "Anonymous test case " << ++m_unnamedCount;
return registerTest( testCase.withName( oss.str() ) ); return registerTest( testCase.withName( oss.str() ) );
@ -159,7 +159,7 @@ namespace Catch {
inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
std::string className = classOrQualifiedMethodName; std::string className = classOrQualifiedMethodName;
if( startsWith( className, "&" ) ) if( startsWith( className, '&' ) )
{ {
std::size_t lastColons = className.rfind( "::" ); std::size_t lastColons = className.rfind( "::" );
std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); std::size_t penultimateColons = className.rfind( "::", lastColons-1 );

View File

@ -102,7 +102,7 @@ std::string toString( int value ) {
std::ostringstream oss; std::ostringstream oss;
oss << value; oss << value;
if( value > Detail::hexThreshold ) if( value > Detail::hexThreshold )
oss << " (0x" << std::hex << value << ")"; oss << " (0x" << std::hex << value << ')';
return oss.str(); return oss.str();
} }
@ -110,7 +110,7 @@ std::string toString( unsigned long value ) {
std::ostringstream oss; std::ostringstream oss;
oss << value; oss << value;
if( value > Detail::hexThreshold ) if( value > Detail::hexThreshold )
oss << " (0x" << std::hex << value << ")"; oss << " (0x" << std::hex << value << ')';
return oss.str(); return oss.str();
} }
@ -164,14 +164,14 @@ std::string toString( long long value ) {
std::ostringstream oss; std::ostringstream oss;
oss << value; oss << value;
if( value > Detail::hexThreshold ) if( value > Detail::hexThreshold )
oss << " (0x" << std::hex << value << ")"; oss << " (0x" << std::hex << value << ')';
return oss.str(); return oss.str();
} }
std::string toString( unsigned long long value ) { std::string toString( unsigned long long value ) {
std::ostringstream oss; std::ostringstream oss;
oss << value; oss << value;
if( value > Detail::hexThreshold ) if( value > Detail::hexThreshold )
oss << " (0x" << std::hex << value << ")"; oss << " (0x" << std::hex << value << ')';
return oss.str(); return oss.str();
} }
#endif #endif

View File

@ -27,11 +27,11 @@ namespace Catch
m_wildcard( NoWildcard ), m_wildcard( NoWildcard ),
m_pattern( adjustCase( pattern ) ) m_pattern( adjustCase( pattern ) )
{ {
if( startsWith( m_pattern, "*" ) ) { if( startsWith( m_pattern, '*' ) ) {
m_pattern = m_pattern.substr( 1 ); m_pattern = m_pattern.substr( 1 );
m_wildcard = WildcardAtStart; m_wildcard = WildcardAtStart;
} }
if( endsWith( m_pattern, "*" ) ) { if( endsWith( m_pattern, '*' ) ) {
m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd ); m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
} }

View File

@ -202,7 +202,7 @@ namespace Catch {
XmlWriter& writeBlankLine() { XmlWriter& writeBlankLine() {
ensureTagClosed(); ensureTagClosed();
stream() << "\n"; stream() << '\n';
return *this; return *this;
} }