Eliminate dynamic cast by implementing MultipleReporters as a linked list

This eliminates one use of RTTI in Catch. I also tend to think that this
is a more sensible design.
This commit is contained in:
Eirik Byrkjeflot Anonsen 2016-04-05 18:21:49 +02:00
parent c984fc3ecd
commit 2ebd7dc10e

View File

@ -13,132 +13,121 @@
namespace Catch { namespace Catch {
class MultipleReporters : public SharedImpl<IStreamingReporter> { class MultipleReporters : public SharedImpl<IStreamingReporter> {
typedef std::vector<Ptr<IStreamingReporter> > Reporters; Ptr<IStreamingReporter> m_reporter;
Reporters m_reporters; Ptr<MultipleReporters> m_next;
public: public:
MultipleReporters( Ptr<IStreamingReporter> const& reporter )
: m_reporter( reporter )
, m_next( CATCH_NULL )
{
}
MultipleReporters( Ptr<IStreamingReporter> const& first, Ptr<IStreamingReporter> const& second )
: m_reporter( first )
, m_next( new MultipleReporters( second ) )
{
}
void add( Ptr<IStreamingReporter> const& reporter ) { void add( Ptr<IStreamingReporter> const& reporter ) {
m_reporters.push_back( reporter ); if( m_next )
m_next->add( reporter );
else
m_next = new MultipleReporters( reporter );
} }
public: // IStreamingReporter public: // IStreamingReporter
virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
return m_reporters[0]->getPreferences(); return m_reporter->getPreferences();
} }
virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); m_reporter->noMatchingTestCases( spec );
it != itEnd; if( m_next )
++it ) m_next->noMatchingTestCases( spec );
(*it)->noMatchingTestCases( spec );
} }
virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE {
for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); m_reporter->testRunStarting( testRunInfo );
it != itEnd; if( m_next )
++it ) m_next->testRunStarting( testRunInfo );
(*it)->testRunStarting( testRunInfo );
} }
virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); m_reporter->testGroupStarting( groupInfo );
it != itEnd; if( m_next )
++it ) m_next->testGroupStarting( groupInfo );
(*it)->testGroupStarting( groupInfo );
} }
virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); m_reporter->testCaseStarting( testInfo );
it != itEnd; if( m_next )
++it ) m_next->testCaseStarting( testInfo );
(*it)->testCaseStarting( testInfo );
} }
virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); m_reporter->sectionStarting( sectionInfo );
it != itEnd; if( m_next )
++it ) m_next->sectionStarting( sectionInfo );
(*it)->sectionStarting( sectionInfo );
} }
virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE {
for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); m_reporter->assertionStarting( assertionInfo );
it != itEnd; if( m_next )
++it ) m_next->assertionStarting( assertionInfo );
(*it)->assertionStarting( assertionInfo );
} }
// The return value indicates if the messages buffer should be cleared: // The return value indicates if the messages buffer should be cleared:
virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
bool clearBuffer = false; bool clearBuffer = m_reporter->assertionEnded( assertionStats );
for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); // Be careful with how the two calls' return values are anded.
it != itEnd; // We don't want short-circuiting to eliminate one of the calls.
++it ) if( m_next && m_next->assertionEnded( assertionStats ) )
clearBuffer |= (*it)->assertionEnded( assertionStats ); return true;
return clearBuffer; return clearBuffer;
} }
virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); m_reporter->sectionEnded( sectionStats );
it != itEnd; if( m_next )
++it ) m_next->sectionEnded( sectionStats );
(*it)->sectionEnded( sectionStats );
} }
virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); m_reporter->testCaseEnded( testCaseStats );
it != itEnd; if( m_next )
++it ) m_next->testCaseEnded( testCaseStats );
(*it)->testCaseEnded( testCaseStats );
} }
virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); m_reporter->testGroupEnded( testGroupStats );
it != itEnd; if( m_next )
++it ) m_next->testGroupEnded( testGroupStats );
(*it)->testGroupEnded( testGroupStats );
} }
virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); m_reporter->testRunEnded( testRunStats );
it != itEnd; if( m_next )
++it ) m_next->testRunEnded( testRunStats );
(*it)->testRunEnded( testRunStats );
} }
virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); m_reporter->skipTest( testInfo );
it != itEnd; if( m_next )
++it ) m_next->skipTest( testInfo );
(*it)->skipTest( testInfo );
} }
}; };
Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) { Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) {
Ptr<IStreamingReporter> resultingReporter; if( !existingReporter )
return additionalReporter;
if( existingReporter ) { return Ptr<IStreamingReporter>( new MultipleReporters(existingReporter, additionalReporter) );
MultipleReporters* multi = dynamic_cast<MultipleReporters*>( existingReporter.get() );
if( !multi ) {
multi = new MultipleReporters;
resultingReporter = Ptr<IStreamingReporter>( multi );
if( existingReporter )
multi->add( existingReporter );
}
else
resultingReporter = existingReporter;
multi->add( additionalReporter );
}
else
resultingReporter = additionalReporter;
return resultingReporter;
} }