mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-22 13:26:10 +01:00
Args and TokenStream use StringRefs instead of std::strings
Because TokenStream is copied around a lot, moving it to use `StringRef` removes _a lot_ of allocations per `Opt` in the parser. Args are not copied around much, but changing them as well makes it obvious that they do not participate in the ownership. The changes add up to removing ~180 allocations for "empty" invocation of the test binary. (`./tests/ExtraTests/NoTests --allow-running-no-tests -o /dev/null` is down to 317 allocs)
This commit is contained in:
parent
cd3c7ebe87
commit
5d637d4c6b
@ -37,6 +37,19 @@ namespace {
|
|||||||
return optName;
|
return optName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t find_first_separator(Catch::StringRef sr) {
|
||||||
|
auto is_separator = []( char c ) {
|
||||||
|
return c == ' ' || c == ':' || c == '=';
|
||||||
|
};
|
||||||
|
size_t pos = 0;
|
||||||
|
while (pos < sr.size()) {
|
||||||
|
if (is_separator(sr[pos])) { return pos; }
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Catch::StringRef::npos;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
@ -52,23 +65,23 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( it != itEnd ) {
|
if ( it != itEnd ) {
|
||||||
auto const& next = *it;
|
StringRef next = *it;
|
||||||
if ( isOptPrefix( next[0] ) ) {
|
if ( isOptPrefix( next[0] ) ) {
|
||||||
auto delimiterPos = next.find_first_of( " :=" );
|
auto delimiterPos = find_first_separator(next);
|
||||||
if ( delimiterPos != std::string::npos ) {
|
if ( delimiterPos != StringRef::npos ) {
|
||||||
m_tokenBuffer.push_back(
|
m_tokenBuffer.push_back(
|
||||||
{ TokenType::Option,
|
{ TokenType::Option,
|
||||||
next.substr( 0, delimiterPos ) } );
|
next.substr( 0, delimiterPos ) } );
|
||||||
m_tokenBuffer.push_back(
|
m_tokenBuffer.push_back(
|
||||||
{ TokenType::Argument,
|
{ TokenType::Argument,
|
||||||
next.substr( delimiterPos + 1 ) } );
|
next.substr( delimiterPos + 1, next.size() ) } );
|
||||||
} else {
|
} else {
|
||||||
if ( next[1] != '-' && next.size() > 2 ) {
|
if ( next[1] != '-' && next.size() > 2 ) {
|
||||||
std::string opt = "- ";
|
// Combined short args, e.g. "-ab" for "-a -b"
|
||||||
for ( size_t i = 1; i < next.size(); ++i ) {
|
for ( size_t i = 1; i < next.size(); ++i ) {
|
||||||
opt[1] = next[i];
|
|
||||||
m_tokenBuffer.push_back(
|
m_tokenBuffer.push_back(
|
||||||
{ TokenType::Option, opt } );
|
{ TokenType::Option,
|
||||||
|
next.substr( i, 1 ) } );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
m_tokenBuffer.push_back(
|
m_tokenBuffer.push_back(
|
||||||
@ -128,7 +141,7 @@ namespace Catch {
|
|||||||
size_t ParserBase::cardinality() const { return 1; }
|
size_t ParserBase::cardinality() const { return 1; }
|
||||||
|
|
||||||
InternalParseResult ParserBase::parse( Args const& args ) const {
|
InternalParseResult ParserBase::parse( Args const& args ) const {
|
||||||
return parse( args.exeName(), TokenStream( args ) );
|
return parse( static_cast<std::string>(args.exeName()), TokenStream( args ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
ParseState::ParseState( ParseResultType type,
|
ParseState::ParseState( ParseResultType type,
|
||||||
@ -166,7 +179,7 @@ namespace Catch {
|
|||||||
auto valueRef =
|
auto valueRef =
|
||||||
static_cast<Detail::BoundValueRefBase*>(m_ref.get());
|
static_cast<Detail::BoundValueRefBase*>(m_ref.get());
|
||||||
|
|
||||||
auto result = valueRef->setValue(remainingTokens->token);
|
auto result = valueRef->setValue(static_cast<std::string>(token.token));
|
||||||
if (!result)
|
if (!result)
|
||||||
return Detail::InternalParseResult(result);
|
return Detail::InternalParseResult(result);
|
||||||
else
|
else
|
||||||
@ -192,7 +205,7 @@ namespace Catch {
|
|||||||
return { oss.str(), m_description };
|
return { oss.str(), m_description };
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Opt::isMatch(std::string const& optToken) const {
|
bool Opt::isMatch(StringRef optToken) const {
|
||||||
auto normalisedToken = normaliseOpt(optToken);
|
auto normalisedToken = normaliseOpt(optToken);
|
||||||
for (auto const& name : m_optNames) {
|
for (auto const& name : m_optNames) {
|
||||||
if (normaliseOpt(name) == normalisedToken)
|
if (normaliseOpt(name) == normalisedToken)
|
||||||
@ -237,7 +250,7 @@ namespace Catch {
|
|||||||
return Detail::InternalParseResult::runtimeError(
|
return Detail::InternalParseResult::runtimeError(
|
||||||
"Expected argument following " +
|
"Expected argument following " +
|
||||||
token.token);
|
token.token);
|
||||||
const auto result = valueRef->setValue(argToken.token);
|
const auto result = valueRef->setValue(static_cast<std::string>(argToken.token));
|
||||||
if (!result)
|
if (!result)
|
||||||
return Detail::InternalParseResult(result);
|
return Detail::InternalParseResult(result);
|
||||||
if (result.value() ==
|
if (result.value() ==
|
||||||
@ -433,7 +446,7 @@ namespace Catch {
|
|||||||
Args::Args(int argc, char const* const* argv) :
|
Args::Args(int argc, char const* const* argv) :
|
||||||
m_exeName(argv[0]), m_args(argv + 1, argv + argc) {}
|
m_exeName(argv[0]), m_args(argv + 1, argv + argc) {}
|
||||||
|
|
||||||
Args::Args(std::initializer_list<std::string> args) :
|
Args::Args(std::initializer_list<StringRef> args) :
|
||||||
m_exeName(*args.begin()),
|
m_exeName(*args.begin()),
|
||||||
m_args(args.begin() + 1, args.end()) {}
|
m_args(args.begin() + 1, args.end()) {}
|
||||||
|
|
||||||
|
@ -102,17 +102,16 @@ namespace Catch {
|
|||||||
enum class TokenType { Option, Argument };
|
enum class TokenType { Option, Argument };
|
||||||
struct Token {
|
struct Token {
|
||||||
TokenType type;
|
TokenType type;
|
||||||
std::string token;
|
StringRef token;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Abstracts iterators into args as a stream of tokens, with option
|
// Abstracts iterators into args as a stream of tokens, with option
|
||||||
// arguments uniformly handled
|
// arguments uniformly handled
|
||||||
class TokenStream {
|
class TokenStream {
|
||||||
using Iterator = std::vector<std::string>::const_iterator;
|
using Iterator = std::vector<StringRef>::const_iterator;
|
||||||
Iterator it;
|
Iterator it;
|
||||||
Iterator itEnd;
|
Iterator itEnd;
|
||||||
std::vector<Token> m_tokenBuffer;
|
std::vector<Token> m_tokenBuffer;
|
||||||
|
|
||||||
void loadBuffer();
|
void loadBuffer();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -582,7 +581,7 @@ namespace Catch {
|
|||||||
|
|
||||||
Detail::HelpColumns getHelpColumns() const;
|
Detail::HelpColumns getHelpColumns() const;
|
||||||
|
|
||||||
bool isMatch(std::string const& optToken) const;
|
bool isMatch(StringRef optToken) const;
|
||||||
|
|
||||||
using ParserBase::parse;
|
using ParserBase::parse;
|
||||||
|
|
||||||
@ -676,18 +675,20 @@ namespace Catch {
|
|||||||
Detail::TokenStream const& tokens) const override;
|
Detail::TokenStream const& tokens) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Transport for raw args (copied from main args, or supplied via
|
/**
|
||||||
// init list for testing)
|
* Wrapper over argc + argv, assumes that the inputs outlive it
|
||||||
|
*/
|
||||||
class Args {
|
class Args {
|
||||||
friend Detail::TokenStream;
|
friend Detail::TokenStream;
|
||||||
std::string m_exeName;
|
StringRef m_exeName;
|
||||||
std::vector<std::string> m_args;
|
std::vector<StringRef> m_args;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Args(int argc, char const* const* argv);
|
Args(int argc, char const* const* argv);
|
||||||
Args(std::initializer_list<std::string> args);
|
// Helper constructor for testing
|
||||||
|
Args(std::initializer_list<StringRef> args);
|
||||||
|
|
||||||
std::string const& exeName() const { return m_exeName; }
|
StringRef exeName() const { return m_exeName; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ namespace Catch {
|
|||||||
using size_type = std::size_t;
|
using size_type = std::size_t;
|
||||||
using const_iterator = const char*;
|
using const_iterator = const char*;
|
||||||
|
|
||||||
|
static constexpr size_type npos{ static_cast<size_type>( -1 ) };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr char const* const s_empty = "";
|
static constexpr char const* const s_empty = "";
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user