lazily stringify expressions

This commit is contained in:
Mickey Rose 2017-01-09 13:23:10 +01:00 committed by Martin Hořeňovský
parent 3b7511e564
commit a1e9b841ff
7 changed files with 223 additions and 110 deletions

View File

@ -13,6 +13,27 @@
namespace Catch { namespace Catch {
struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
struct DecomposedExpression
{
virtual ~DecomposedExpression() {}
virtual bool isBinaryExpression() const {
return false;
}
virtual void reconstructExpression( std::string& dest ) const = 0;
// Only simple binary comparisons can be decomposed.
// If more complex check is required then wrap sub-expressions in parentheses.
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& );
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& );
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& );
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& );
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& );
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& );
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& );
};
struct AssertionInfo struct AssertionInfo
{ {
AssertionInfo() {} AssertionInfo() {}
@ -29,11 +50,41 @@ namespace Catch {
struct AssertionResultData struct AssertionResultData
{ {
AssertionResultData() : resultType( ResultWas::Unknown ) {} AssertionResultData() : decomposedExpression( CATCH_NULL )
, resultType( ResultWas::Unknown )
, negated( false )
, parenthesized( false ) {}
std::string reconstructedExpression; void negate( bool parenthesize ) {
negated = !negated;
parenthesized = parenthesize;
if( resultType == ResultWas::Ok )
resultType = ResultWas::ExpressionFailed;
else if( resultType == ResultWas::ExpressionFailed )
resultType = ResultWas::Ok;
}
std::string const& reconstructExpression() const {
if( decomposedExpression != CATCH_NULL ) {
decomposedExpression->reconstructExpression( reconstructedExpression );
if( parenthesized ) {
reconstructedExpression.insert( 0, 1, '(' );
reconstructedExpression.append( 1, ')' );
}
if( negated ) {
reconstructedExpression.insert( 0, 1, '!' );
}
decomposedExpression = CATCH_NULL;
}
return reconstructedExpression;
}
mutable DecomposedExpression const* decomposedExpression;
mutable std::string reconstructedExpression;
std::string message; std::string message;
ResultWas::OfType resultType; ResultWas::OfType resultType;
bool negated;
bool parenthesized;
}; };
class AssertionResult { class AssertionResult {
@ -60,6 +111,8 @@ namespace Catch {
std::string getMessage() const; std::string getMessage() const;
SourceLineInfo getSourceInfo() const; SourceLineInfo getSourceInfo() const;
std::string getTestMacroName() const; std::string getTestMacroName() const;
void discardDecomposedExpression() const;
void expandDecomposedExpression() const;
protected: protected:
AssertionInfo m_info; AssertionInfo m_info;

View File

@ -72,7 +72,7 @@ namespace Catch {
} }
std::string AssertionResult::getExpandedExpression() const { std::string AssertionResult::getExpandedExpression() const {
return m_resultData.reconstructedExpression; return m_resultData.reconstructExpression();
} }
std::string AssertionResult::getMessage() const { std::string AssertionResult::getMessage() const {
@ -86,6 +86,14 @@ namespace Catch {
return m_info.macroName; return m_info.macroName;
} }
void AssertionResult::discardDecomposedExpression() const {
m_resultData.decomposedExpression = CATCH_NULL;
}
void AssertionResult::expandDecomposedExpression() const {
m_resultData.reconstructExpression();
}
} // end namespace Catch } // end namespace Catch
#endif // TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED #endif // TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED

View File

@ -132,13 +132,7 @@
do { \ do { \
Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
try { \ try { \
std::string matcherAsString = (matcher).toString(); \ __catchResult.captureMatch( arg, matcher, #matcher ); \
__catchResult \
.setLhs( Catch::toString( arg ) ) \
.setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \
.setOp( "matches" ) \
.setResultType( (matcher).match( arg ) ); \
__catchResult.captureExpression(); \
} catch( ... ) { \ } catch( ... ) { \
__catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
} \ } \

View File

@ -14,90 +14,155 @@
namespace Catch { namespace Catch {
// Wraps the LHS of an expression and captures the operator and RHS (if any) - template<typename LhsT, Internal::Operator Op, typename RhsT>
// wrapping them all in a ResultBuilder object class BinaryExpression;
template<typename T>
class ExpressionLhs {
ExpressionLhs& operator = ( ExpressionLhs const& );
# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
ExpressionLhs& operator = ( ExpressionLhs && ) = delete;
# endif
template<typename ArgT, typename MatcherT>
class MatchExpression;
// Wraps the LHS of an expression and overloads comparison operators
// for also capturing those and RHS (if any)
template<typename T>
class ExpressionLhs : public DecomposedExpression {
public: public:
ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {} ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {}
# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
ExpressionLhs( ExpressionLhs const& ) = default;
ExpressionLhs( ExpressionLhs && ) = default;
# endif
template<typename RhsT> template<typename RhsT>
ResultBuilder& operator == ( RhsT const& rhs ) { BinaryExpression<T, Internal::IsEqualTo, RhsT const&>
operator == ( RhsT const& rhs ) const {
return captureExpression<Internal::IsEqualTo>( rhs ); return captureExpression<Internal::IsEqualTo>( rhs );
} }
template<typename RhsT> template<typename RhsT>
ResultBuilder& operator != ( RhsT const& rhs ) { BinaryExpression<T, Internal::IsNotEqualTo, RhsT const&>
operator != ( RhsT const& rhs ) const {
return captureExpression<Internal::IsNotEqualTo>( rhs ); return captureExpression<Internal::IsNotEqualTo>( rhs );
} }
template<typename RhsT> template<typename RhsT>
ResultBuilder& operator < ( RhsT const& rhs ) { BinaryExpression<T, Internal::IsLessThan, RhsT const&>
operator < ( RhsT const& rhs ) const {
return captureExpression<Internal::IsLessThan>( rhs ); return captureExpression<Internal::IsLessThan>( rhs );
} }
template<typename RhsT> template<typename RhsT>
ResultBuilder& operator > ( RhsT const& rhs ) { BinaryExpression<T, Internal::IsGreaterThan, RhsT const&>
operator > ( RhsT const& rhs ) const {
return captureExpression<Internal::IsGreaterThan>( rhs ); return captureExpression<Internal::IsGreaterThan>( rhs );
} }
template<typename RhsT> template<typename RhsT>
ResultBuilder& operator <= ( RhsT const& rhs ) { BinaryExpression<T, Internal::IsLessThanOrEqualTo, RhsT const&>
operator <= ( RhsT const& rhs ) const {
return captureExpression<Internal::IsLessThanOrEqualTo>( rhs ); return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
} }
template<typename RhsT> template<typename RhsT>
ResultBuilder& operator >= ( RhsT const& rhs ) { BinaryExpression<T, Internal::IsGreaterThanOrEqualTo, RhsT const&>
operator >= ( RhsT const& rhs ) const {
return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs ); return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
} }
ResultBuilder& operator == ( bool rhs ) { BinaryExpression<T, Internal::IsEqualTo, bool> operator == ( bool rhs ) const {
return captureExpression<Internal::IsEqualTo>( rhs ); return captureExpression<Internal::IsEqualTo>( rhs );
} }
ResultBuilder& operator != ( bool rhs ) { BinaryExpression<T, Internal::IsNotEqualTo, bool> operator != ( bool rhs ) const {
return captureExpression<Internal::IsNotEqualTo>( rhs ); return captureExpression<Internal::IsNotEqualTo>( rhs );
} }
void endExpression() { void endExpression() {
bool value = m_lhs ? true : false; m_truthy = m_lhs ? true : false;
m_rb m_rb
.setLhs( Catch::toString( value ) ) .setResultType( m_truthy )
.setResultType( value ) .endExpression( *this );
.endExpression();
} }
// Only simple binary expressions are allowed on the LHS. virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
// If more complex compositions are required then place the sub expression in parentheses dest = Catch::toString( m_truthy );
template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); }
template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& );
template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& );
template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& );
template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
private: private:
template<Internal::Operator Op, typename RhsT> template<Internal::Operator Op, typename RhsT>
ResultBuilder& captureExpression( RhsT const& rhs ) { BinaryExpression<T, Op, RhsT&> captureExpression( RhsT& rhs ) const {
return m_rb return BinaryExpression<T, Op, RhsT&>( m_rb, m_lhs, rhs );
.setResultType( Internal::compare<Op>( m_lhs, rhs ) ) }
.setLhs( Catch::toString( m_lhs ) )
.setRhs( Catch::toString( rhs ) ) template<Internal::Operator Op>
.setOp( Internal::OperatorTraits<Op>::getName() ); BinaryExpression<T, Op, bool> captureExpression( bool rhs ) const {
return BinaryExpression<T, Op, bool>( m_rb, m_lhs, rhs );
} }
private: private:
ResultBuilder& m_rb; ResultBuilder& m_rb;
T m_lhs; T m_lhs;
bool m_truthy;
};
template<typename LhsT, Internal::Operator Op, typename RhsT>
class BinaryExpression : public DecomposedExpression {
public:
BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs )
: m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {}
void endExpression() const {
m_rb
.setResultType( Internal::compare<Op>( m_lhs, m_rhs ) )
.endExpression( *this );
}
virtual bool isBinaryExpression() const CATCH_OVERRIDE {
return true;
}
virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
std::string lhs = Catch::toString( m_lhs );
std::string rhs = Catch::toString( m_rhs );
char delim = lhs.size() + rhs.size() < 40 &&
lhs.find('\n') == std::string::npos &&
rhs.find('\n') == std::string::npos ? ' ' : '\n';
dest.reserve( 7 + lhs.size() + rhs.size() );
// 2 for spaces around operator
// 2 for operator
// 2 for parentheses (conditionally added later)
// 1 for negation (conditionally added later)
dest = lhs;
dest += delim;
dest += Internal::OperatorTraits<Op>::getName();
dest += delim;
dest += rhs;
}
private:
ResultBuilder& m_rb;
LhsT m_lhs;
RhsT m_rhs;
};
template<typename ArgT, typename MatcherT>
class MatchExpression : public DecomposedExpression {
public:
MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString )
: m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {}
virtual bool isBinaryExpression() const CATCH_OVERRIDE {
return true;
}
virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
std::string matcherAsString = m_matcher.toString();
dest = Catch::toString( m_arg );
dest += ' ';
if( matcherAsString == Detail::unprintableString )
dest += m_matcherString;
else
dest += matcherAsString;
}
private:
ArgT m_arg;
MatcherT m_matcher;
char const* m_matcherString;
}; };
} // end namespace Catch } // end namespace Catch

View File

@ -19,8 +19,6 @@ namespace Catch {
template<typename T> class ExpressionLhs; template<typename T> class ExpressionLhs;
struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
struct CopyableStream { struct CopyableStream {
CopyableStream() {} CopyableStream() {}
CopyableStream( CopyableStream const& other ) { CopyableStream( CopyableStream const& other ) {
@ -34,7 +32,7 @@ namespace Catch {
std::ostringstream oss; std::ostringstream oss;
}; };
class ResultBuilder { class ResultBuilder : public DecomposedExpression {
public: public:
ResultBuilder( char const* macroName, ResultBuilder( char const* macroName,
SourceLineInfo const& lineInfo, SourceLineInfo const& lineInfo,
@ -52,19 +50,15 @@ namespace Catch {
return *this; return *this;
} }
template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
ResultBuilder& setResultType( ResultWas::OfType result ); ResultBuilder& setResultType( ResultWas::OfType result );
ResultBuilder& setResultType( bool result ); ResultBuilder& setResultType( bool result );
ResultBuilder& setLhs( std::string const& lhs );
ResultBuilder& setRhs( std::string const& rhs );
ResultBuilder& setOp( std::string const& op );
void endExpression(); void endExpression( DecomposedExpression const& expr );
virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE;
std::string reconstructExpression() const;
AssertionResult build() const; AssertionResult build() const;
AssertionResult build( DecomposedExpression const& expr ) const;
void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
void captureResult( ResultWas::OfType resultType ); void captureResult( ResultWas::OfType resultType );
@ -76,14 +70,12 @@ namespace Catch {
bool shouldDebugBreak() const; bool shouldDebugBreak() const;
bool allowThrows() const; bool allowThrows() const;
template<typename ArgT, typename MatcherT>
void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString );
private: private:
AssertionInfo m_assertionInfo; AssertionInfo m_assertionInfo;
AssertionResultData m_data; AssertionResultData m_data;
struct ExprComponents {
ExprComponents() : testFalse( false ) {}
bool testFalse;
std::string lhs, rhs, op;
} m_exprComponents;
CopyableStream m_stream; CopyableStream m_stream;
bool m_shouldDebugBreak; bool m_shouldDebugBreak;
@ -106,6 +98,14 @@ namespace Catch {
return ExpressionLhs<bool>( *this, value ); return ExpressionLhs<bool>( *this, value );
} }
template<typename ArgT, typename MatcherT>
inline void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher,
char const* matcherString ) {
MatchExpression<ArgT const&, MatcherT const&> expr( arg, matcher, matcherString );
setResultType( matcher.match( arg ) );
endExpression( expr );
}
} // namespace Catch } // namespace Catch
#endif // TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED #endif // TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED

View File

@ -41,22 +41,10 @@ namespace Catch {
m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
return *this; return *this;
} }
ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) {
m_exprComponents.lhs = lhs;
return *this;
}
ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) {
m_exprComponents.rhs = rhs;
return *this;
}
ResultBuilder& ResultBuilder::setOp( std::string const& op ) {
m_exprComponents.op = op;
return *this;
}
void ResultBuilder::endExpression() { void ResultBuilder::endExpression( DecomposedExpression const& expr ) {
m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition ); AssertionResult result = build( expr );
captureExpression(); handleResult( result );
} }
void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
@ -69,6 +57,7 @@ namespace Catch {
setResultType( resultType ); setResultType( resultType );
captureExpression(); captureExpression();
} }
void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) {
if( expectedMessage.empty() ) if( expectedMessage.empty() )
captureExpectedException( Matchers::Impl::Generic::AllOf<std::string>() ); captureExpectedException( Matchers::Impl::Generic::AllOf<std::string>() );
@ -78,7 +67,7 @@ namespace Catch {
void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher ) { void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher ) {
assert( m_exprComponents.testFalse == false ); assert( !isFalseTest( m_assertionInfo.resultDisposition ) );
AssertionResultData data = m_data; AssertionResultData data = m_data;
data.resultType = ResultWas::Ok; data.resultType = ResultWas::Ok;
data.reconstructedExpression = m_assertionInfo.capturedExpression; data.reconstructedExpression = m_assertionInfo.capturedExpression;
@ -96,6 +85,7 @@ namespace Catch {
AssertionResult result = build(); AssertionResult result = build();
handleResult( result ); handleResult( result );
} }
void ResultBuilder::handleResult( AssertionResult const& result ) void ResultBuilder::handleResult( AssertionResult const& result )
{ {
getResultCapture().assertionEnded( result ); getResultCapture().assertionEnded( result );
@ -107,6 +97,7 @@ namespace Catch {
m_shouldThrow = true; m_shouldThrow = true;
} }
} }
void ResultBuilder::react() { void ResultBuilder::react() {
if( m_shouldThrow ) if( m_shouldThrow )
throw Catch::TestFailureException(); throw Catch::TestFailureException();
@ -117,43 +108,32 @@ namespace Catch {
AssertionResult ResultBuilder::build() const AssertionResult ResultBuilder::build() const
{ {
assert( m_data.resultType != ResultWas::Unknown ); return build( *this );
}
// CAVEAT: The returned AssertionResult stores a pointer to the argument expr,
// a temporary DecomposedExpression, which in turn holds references to
// operands, possibly temporary as well.
// It should immediately be passed to handleResult; if the expression
// needs to be reported, its string expansion must be composed before
// the temporaries are destroyed.
AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const
{
assert( m_data.resultType != ResultWas::Unknown );
AssertionResultData data = m_data; AssertionResultData data = m_data;
// Flip bool results if testFalse is set // Flip bool results if FalseTest flag is set
if( m_exprComponents.testFalse ) { if( isFalseTest( m_assertionInfo.resultDisposition ) ) {
if( data.resultType == ResultWas::Ok ) data.negate( expr.isBinaryExpression() );
data.resultType = ResultWas::ExpressionFailed;
else if( data.resultType == ResultWas::ExpressionFailed )
data.resultType = ResultWas::Ok;
} }
data.message = m_stream.oss.str(); data.message = m_stream.oss.str();
data.reconstructedExpression = reconstructExpression(); data.decomposedExpression = &expr; // for lazy reconstruction
if( m_exprComponents.testFalse ) {
if( m_exprComponents.op == "" )
data.reconstructedExpression = "!" + data.reconstructedExpression;
else
data.reconstructedExpression = "!(" + data.reconstructedExpression + ")";
}
return AssertionResult( m_assertionInfo, data ); return AssertionResult( m_assertionInfo, data );
} }
std::string ResultBuilder::reconstructExpression() const {
if( m_exprComponents.op == "" ) void ResultBuilder::reconstructExpression( std::string& dest ) const {
return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.lhs; dest = m_assertionInfo.capturedExpression;
else if( m_exprComponents.op == "matches" )
return m_exprComponents.lhs + " " + m_exprComponents.rhs;
else if( m_exprComponents.op != "!" ) {
if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 &&
m_exprComponents.lhs.find("\n") == std::string::npos &&
m_exprComponents.rhs.find("\n") == std::string::npos )
return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs;
else
return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs;
}
else
return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}";
} }
} // end namespace Catch } // end namespace Catch

View File

@ -170,6 +170,12 @@ namespace Catch {
assert( !m_sectionStack.empty() ); assert( !m_sectionStack.empty() );
SectionNode& sectionNode = *m_sectionStack.back(); SectionNode& sectionNode = *m_sectionStack.back();
sectionNode.assertions.push_back( assertionStats ); sectionNode.assertions.push_back( assertionStats );
// AssertionResult holds a pointer to a temporary DecomposedExpression,
// which getExpandedExpression() calls to build the expression string.
// Our section stack copy of the assertionResult will likely outlive the
// temporary, so it must be expanded or discarded now to avoid calling
// a destroyed object later.
prepareExpandedExpression( sectionNode.assertions.back().assertionResult );
return true; return true;
} }
virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
@ -204,6 +210,13 @@ namespace Catch {
virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {}
virtual void prepareExpandedExpression( AssertionResult& result ) const {
if( result.isOk() )
result.discardDecomposedExpression();
else
result.expandDecomposedExpression();
}
Ptr<IConfig const> m_config; Ptr<IConfig const> m_config;
std::ostream& stream; std::ostream& stream;
std::vector<AssertionStats> m_assertions; std::vector<AssertionStats> m_assertions;