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;
while( std::getline( f, line ) ) {
line = trim(line);
if( !line.empty() && !startsWith( line, "#" ) ) {
if( !startsWith( line, "\"" ) )
if( !line.empty() && !startsWith( line, '#' ) ) {
if( !startsWith( 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, char prefix );
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 );
void toLowerInPlace( std::string& s );
std::string toLower( std::string const& s );

View File

@ -10,17 +10,28 @@
#include "catch_common.h"
#include <algorithm>
namespace Catch {
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 ) {
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 ) {
return s.find( infix ) != std::string::npos;
}
bool contains( std::string const& s, char infix ) {
return s.find(infix);
}
char toLowerCh(char 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 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 ) {
@ -95,9 +106,9 @@ namespace Catch {
std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
#ifndef __GNUG__
os << info.file << "(" << info.line << ")";
os << info.file << '(' << info.line << ')';
#else
os << info.file << ":" << info.line;
os << info.file << ':' << info.line;
#endif
return os;
}

View File

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

View File

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

View File

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

View File

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

View File

@ -146,7 +146,7 @@ namespace Catch {
m_messages.clear();
// 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;
}
@ -217,7 +217,7 @@ namespace Catch {
virtual std::string getCurrentTestName() const {
return m_activeTestCase
? m_activeTestCase->getTestCaseInfo().name
: "";
: std::string();
}
virtual const AssertionResult* getLastResult() const {
@ -247,11 +247,11 @@ namespace Catch {
deltaTotals.testCases.failed = 1;
m_reporter->testCaseEnded( TestCaseStats( testInfo,
deltaTotals,
"",
"",
std::string(),
std::string(),
false ) );
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 ) );
}
@ -270,7 +270,7 @@ namespace Catch {
Counts prevAssertions = m_totals.assertions;
double duration = 0;
try {
m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, std::string(), ResultDisposition::Normal );
seedRng( *m_config );

View File

@ -43,7 +43,7 @@ namespace Catch {
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;
oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo;
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 ) {
std::ostringstream oss;
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;
throw std::domain_error( oss.str().c_str() );
}

View File

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

View File

@ -78,7 +78,7 @@ namespace Catch {
ss << Colour( Colour::Red )
<< "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;
throw std::runtime_error(ss.str());
@ -110,7 +110,7 @@ namespace Catch {
virtual void registerTest( TestCase const& testCase ) {
std::string name = testCase.getTestCaseInfo().name;
if( name == "" ) {
if( name.empty() ) {
std::ostringstream oss;
oss << "Anonymous test case " << ++m_unnamedCount;
return registerTest( testCase.withName( oss.str() ) );
@ -159,7 +159,7 @@ namespace Catch {
inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
std::string className = classOrQualifiedMethodName;
if( startsWith( className, "&" ) )
if( startsWith( className, '&' ) )
{
std::size_t lastColons = className.rfind( "::" );
std::size_t penultimateColons = className.rfind( "::", lastColons-1 );

View File

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

View File

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

View File

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