Avoid copying TokenStreams while parsing

The code is now even worse mess than before, due to the ad-hoc
implementation of Result-ish type based on virtual functions in
Clara, but it has dropped the allocations for empty binary down
to 151.
This commit is contained in:
Martin Hořeňovský 2023-12-27 21:11:34 +01:00
parent 5d637d4c6b
commit eaafd07674
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
2 changed files with 66 additions and 42 deletions

View File

@ -145,8 +145,8 @@ namespace Catch {
} }
ParseState::ParseState( ParseResultType type, ParseState::ParseState( ParseResultType type,
TokenStream const& remainingTokens ): TokenStream remainingTokens ):
m_type( type ), m_remainingTokens( remainingTokens ) {} m_type( type ), m_remainingTokens( CATCH_MOVE(remainingTokens) ) {}
ParserResult BoundFlagRef::setFlag( bool flag ) { ParserResult BoundFlagRef::setFlag( bool flag ) {
m_ref = flag; m_ref = flag;
@ -164,16 +164,15 @@ namespace Catch {
} // namespace Detail } // namespace Detail
Detail::InternalParseResult Arg::parse(std::string const&, Detail::InternalParseResult Arg::parse(std::string const&,
Detail::TokenStream const& tokens) const { Detail::TokenStream tokens) const {
auto validationResult = validate(); auto validationResult = validate();
if (!validationResult) if (!validationResult)
return Detail::InternalParseResult(validationResult); return Detail::InternalParseResult(validationResult);
auto remainingTokens = tokens; auto token = *tokens;
auto const& token = *remainingTokens;
if (token.type != Detail::TokenType::Argument) if (token.type != Detail::TokenType::Argument)
return Detail::InternalParseResult::ok(Detail::ParseState( return Detail::InternalParseResult::ok(Detail::ParseState(
ParseResultType::NoMatch, remainingTokens)); ParseResultType::NoMatch, CATCH_MOVE(tokens)));
assert(!m_ref->isFlag()); assert(!m_ref->isFlag());
auto valueRef = auto valueRef =
@ -183,8 +182,9 @@ namespace Catch {
if ( !result ) if ( !result )
return Detail::InternalParseResult( result ); return Detail::InternalParseResult( result );
else else
return Detail::InternalParseResult::ok(Detail::ParseState( return Detail::InternalParseResult::ok(
ParseResultType::Matched, ++remainingTokens)); Detail::ParseState( ParseResultType::Matched,
CATCH_MOVE( ++tokens ) ) );
} }
Opt::Opt(bool& ref) : Opt::Opt(bool& ref) :
@ -215,15 +215,14 @@ namespace Catch {
} }
Detail::InternalParseResult Opt::parse(std::string const&, Detail::InternalParseResult Opt::parse(std::string const&,
Detail::TokenStream const& tokens) const { Detail::TokenStream tokens) const {
auto validationResult = validate(); auto validationResult = validate();
if (!validationResult) if (!validationResult)
return Detail::InternalParseResult(validationResult); return Detail::InternalParseResult(validationResult);
auto remainingTokens = tokens; if (tokens &&
if (remainingTokens && tokens->type == Detail::TokenType::Option) {
remainingTokens->type == Detail::TokenType::Option) { auto const& token = *tokens;
auto const& token = *remainingTokens;
if (isMatch(token.token)) { if (isMatch(token.token)) {
if (m_ref->isFlag()) { if (m_ref->isFlag()) {
auto flagRef = auto flagRef =
@ -235,17 +234,17 @@ namespace Catch {
if (result.value() == if (result.value() ==
ParseResultType::ShortCircuitAll) ParseResultType::ShortCircuitAll)
return Detail::InternalParseResult::ok(Detail::ParseState( return Detail::InternalParseResult::ok(Detail::ParseState(
result.value(), remainingTokens)); result.value(), CATCH_MOVE(tokens)));
} else { } else {
auto valueRef = auto valueRef =
static_cast<Detail::BoundValueRefBase*>( static_cast<Detail::BoundValueRefBase*>(
m_ref.get()); m_ref.get());
++remainingTokens; ++tokens;
if (!remainingTokens) if (!tokens)
return Detail::InternalParseResult::runtimeError( return Detail::InternalParseResult::runtimeError(
"Expected argument following " + "Expected argument following " +
token.token); token.token);
auto const& argToken = *remainingTokens; auto const& argToken = *tokens;
if (argToken.type != Detail::TokenType::Argument) if (argToken.type != Detail::TokenType::Argument)
return Detail::InternalParseResult::runtimeError( return Detail::InternalParseResult::runtimeError(
"Expected argument following " + "Expected argument following " +
@ -256,14 +255,14 @@ namespace Catch {
if (result.value() == if (result.value() ==
ParseResultType::ShortCircuitAll) ParseResultType::ShortCircuitAll)
return Detail::InternalParseResult::ok(Detail::ParseState( return Detail::InternalParseResult::ok(Detail::ParseState(
result.value(), remainingTokens)); result.value(), CATCH_MOVE(tokens)));
} }
return Detail::InternalParseResult::ok(Detail::ParseState( return Detail::InternalParseResult::ok(Detail::ParseState(
ParseResultType::Matched, ++remainingTokens)); ParseResultType::Matched, CATCH_MOVE(++tokens)));
} }
} }
return Detail::InternalParseResult::ok( return Detail::InternalParseResult::ok(
Detail::ParseState(ParseResultType::NoMatch, remainingTokens)); Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens)));
} }
Detail::Result Opt::validate() const { Detail::Result Opt::validate() const {
@ -295,9 +294,9 @@ namespace Catch {
Detail::InternalParseResult Detail::InternalParseResult
ExeName::parse(std::string const&, ExeName::parse(std::string const&,
Detail::TokenStream const& tokens) const { Detail::TokenStream tokens) const {
return Detail::InternalParseResult::ok( return Detail::InternalParseResult::ok(
Detail::ParseState(ParseResultType::NoMatch, tokens)); Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens)));
} }
ParserResult ExeName::set(std::string const& newName) { ParserResult ExeName::set(std::string const& newName) {
@ -394,7 +393,7 @@ namespace Catch {
Detail::InternalParseResult Detail::InternalParseResult
Parser::parse( std::string const& exeName, Parser::parse( std::string const& exeName,
Detail::TokenStream const& tokens ) const { Detail::TokenStream tokens ) const {
struct ParserInfo { struct ParserInfo {
ParserBase const* parser = nullptr; ParserBase const* parser = nullptr;
@ -412,7 +411,7 @@ namespace Catch {
m_exeName.set( exeName ); m_exeName.set( exeName );
auto result = Detail::InternalParseResult::ok( auto result = Detail::InternalParseResult::ok(
Detail::ParseState( ParseResultType::NoMatch, tokens ) ); Detail::ParseState( ParseResultType::NoMatch, CATCH_MOVE(tokens) ) );
while ( result.value().remainingTokens() ) { while ( result.value().remainingTokens() ) {
bool tokenParsed = false; bool tokenParsed = false;
@ -420,7 +419,7 @@ namespace Catch {
if ( parseInfo.parser->cardinality() == 0 || if ( parseInfo.parser->cardinality() == 0 ||
parseInfo.count < parseInfo.parser->cardinality() ) { parseInfo.count < parseInfo.parser->cardinality() ) {
result = parseInfo.parser->parse( result = parseInfo.parser->parse(
exeName, result.value().remainingTokens() ); exeName, CATCH_MOVE(result).value().remainingTokens() );
if ( !result ) if ( !result )
return result; return result;
if ( result.value().type() != if ( result.value().type() !=

View File

@ -166,10 +166,14 @@ namespace Catch {
template <typename T> template <typename T>
class ResultValueBase : public ResultBase { class ResultValueBase : public ResultBase {
public: public:
auto value() const -> T const& { T const& value() const& {
enforceOk(); enforceOk();
return m_value; return m_value;
} }
T&& value() && {
enforceOk();
return CATCH_MOVE( m_value );
}
protected: protected:
ResultValueBase( ResultType type ): ResultBase( type ) {} ResultValueBase( ResultType type ): ResultBase( type ) {}
@ -179,13 +183,23 @@ namespace Catch {
if ( m_type == ResultType::Ok ) if ( m_type == ResultType::Ok )
new ( &m_value ) T( other.m_value ); new ( &m_value ) T( other.m_value );
} }
ResultValueBase( ResultValueBase&& other ):
ResultValueBase( ResultType, T const& value ): ResultBase( ResultType::Ok ) { ResultBase( other ) {
new ( &m_value ) T( value ); if ( m_type == ResultType::Ok )
new ( &m_value ) T( CATCH_MOVE(other.m_value) );
} }
auto operator=( ResultValueBase const& other )
-> ResultValueBase& { ResultValueBase( ResultType, T const& value ):
ResultBase( ResultType::Ok ) {
new ( &m_value ) T( value );
}
ResultValueBase( ResultType, T&& value ):
ResultBase( ResultType::Ok ) {
new ( &m_value ) T( CATCH_MOVE(value) );
}
ResultValueBase& operator=( ResultValueBase const& other ) {
if ( m_type == ResultType::Ok ) if ( m_type == ResultType::Ok )
m_value.~T(); m_value.~T();
ResultBase::operator=( other ); ResultBase::operator=( other );
@ -193,6 +207,14 @@ namespace Catch {
new ( &m_value ) T( other.m_value ); new ( &m_value ) T( other.m_value );
return *this; return *this;
} }
ResultValueBase& operator=( ResultValueBase&& other ) {
if ( m_type == ResultType::Ok ) m_value.~T();
ResultBase::operator=( other );
if ( m_type == ResultType::Ok )
new ( &m_value ) T( CATCH_MOVE(other.m_value) );
return *this;
}
~ResultValueBase() override { ~ResultValueBase() override {
if ( m_type == ResultType::Ok ) if ( m_type == ResultType::Ok )
@ -220,8 +242,8 @@ namespace Catch {
} }
template <typename U> template <typename U>
static auto ok( U const& value ) -> BasicResult { static auto ok( U&& value ) -> BasicResult {
return { ResultType::Ok, value }; return { ResultType::Ok, CATCH_FORWARD(value) };
} }
static auto ok() -> BasicResult { return { ResultType::Ok }; } static auto ok() -> BasicResult { return { ResultType::Ok }; }
static auto logicError( std::string&& message ) static auto logicError( std::string&& message )
@ -268,12 +290,15 @@ namespace Catch {
class ParseState { class ParseState {
public: public:
ParseState( ParseResultType type, ParseState( ParseResultType type,
TokenStream const& remainingTokens ); TokenStream remainingTokens );
ParseResultType type() const { return m_type; } ParseResultType type() const { return m_type; }
TokenStream const& remainingTokens() const { TokenStream const& remainingTokens() const& {
return m_remainingTokens; return m_remainingTokens;
} }
TokenStream&& remainingTokens() && {
return CATCH_MOVE( m_remainingTokens );
}
private: private:
ParseResultType m_type; ParseResultType m_type;
@ -446,7 +471,7 @@ namespace Catch {
virtual ~ParserBase() = default; virtual ~ParserBase() = default;
virtual auto validate() const -> Result { return Result::ok(); } virtual auto validate() const -> Result { return Result::ok(); }
virtual auto parse( std::string const& exeName, virtual auto parse( std::string const& exeName,
TokenStream const& tokens ) const TokenStream tokens ) const
-> InternalParseResult = 0; -> InternalParseResult = 0;
virtual size_t cardinality() const; virtual size_t cardinality() const;
@ -538,7 +563,7 @@ namespace Catch {
Detail::InternalParseResult Detail::InternalParseResult
parse(std::string const&, parse(std::string const&,
Detail::TokenStream const& tokens) const override; Detail::TokenStream tokens) const override;
}; };
// A parser for options // A parser for options
@ -587,7 +612,7 @@ namespace Catch {
Detail::InternalParseResult Detail::InternalParseResult
parse(std::string const&, parse(std::string const&,
Detail::TokenStream const& tokens) const override; Detail::TokenStream tokens) const override;
Detail::Result validate() const override; Detail::Result validate() const override;
}; };
@ -610,7 +635,7 @@ namespace Catch {
// handled specially // handled specially
Detail::InternalParseResult Detail::InternalParseResult
parse(std::string const&, parse(std::string const&,
Detail::TokenStream const& tokens) const override; Detail::TokenStream tokens) const override;
std::string const& name() const { return *m_name; } std::string const& name() const { return *m_name; }
Detail::ParserResult set(std::string const& newName); Detail::ParserResult set(std::string const& newName);
@ -672,7 +697,7 @@ namespace Catch {
using ParserBase::parse; using ParserBase::parse;
Detail::InternalParseResult Detail::InternalParseResult
parse(std::string const& exeName, parse(std::string const& exeName,
Detail::TokenStream const& tokens) const override; Detail::TokenStream tokens) const override;
}; };
/** /**