diff --git a/src/catch2/catch_config.cpp b/src/catch2/catch_config.cpp index e6a5b2ed..6d6e0831 100644 --- a/src/catch2/catch_config.cpp +++ b/src/catch2/catch_config.cpp @@ -107,8 +107,8 @@ namespace Catch { return m_data.reporterSpecifications; } - std::ostream& Config::getReporterOutputStream(std::size_t reporterIdx) const { - return m_reporterStreams.at(reporterIdx)->stream(); + IStream const* Config::getReporterOutputStream(std::size_t reporterIdx) const { + return m_reporterStreams.at(reporterIdx).get(); } TestSpec const& Config::testSpec() const { return m_testSpec; } @@ -118,7 +118,7 @@ namespace Catch { // IConfig interface bool Config::allowThrows() const { return !m_data.noThrow; } - std::ostream& Config::defaultStream() const { return m_defaultStream->stream(); } + IStream const* Config::defaultStream() const { return m_defaultStream.get(); } StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } bool Config::warnAboutMissingAssertions() const { diff --git a/src/catch2/catch_config.hpp b/src/catch2/catch_config.hpp index 2795d42e..7b33fa71 100644 --- a/src/catch2/catch_config.hpp +++ b/src/catch2/catch_config.hpp @@ -21,7 +21,7 @@ namespace Catch { - struct IStream; + class IStream; struct ConfigData { struct ReporterAndFile { @@ -91,7 +91,7 @@ namespace Catch { bool listReporters() const; std::vector const& getReportersAndOutputFiles() const; - std::ostream& getReporterOutputStream(std::size_t reporterIdx) const; + IStream const* getReporterOutputStream(std::size_t reporterIdx) const; std::vector const& getTestsOrTags() const override; std::vector const& getSectionsToRun() const override; @@ -103,7 +103,7 @@ namespace Catch { // IConfig interface bool allowThrows() const override; - std::ostream& defaultStream() const override; + IStream const* defaultStream() const override; StringRef name() const override; bool includeSuccessfulResults() const override; bool warnAboutMissingAssertions() const override; diff --git a/src/catch2/catch_session.cpp b/src/catch2/catch_session.cpp index a4a78bb6..f20531e3 100644 --- a/src/catch2/catch_session.cpp +++ b/src/catch2/catch_session.cpp @@ -44,7 +44,7 @@ namespace Catch { IStreamingReporterPtr makeReporter(Config const* config) { if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty() && config->getReportersAndOutputFiles().size() == 1) { - auto& stream = config->getReporterOutputStream(0); + auto stream = config->getReporterOutputStream(0); return createReporter(config->getReportersAndOutputFiles()[0].reporterName, ReporterConfig(config, stream)); } @@ -57,7 +57,7 @@ namespace Catch { std::size_t reporterIdx = 0; for (auto const& reporterAndFile : config->getReportersAndOutputFiles()) { - auto& stream = config->getReporterOutputStream(reporterIdx); + auto stream = config->getReporterOutputStream(reporterIdx); multi->addReporter(createReporter(reporterAndFile.reporterName, ReporterConfig(config, stream))); reporterIdx++; } diff --git a/src/catch2/interfaces/catch_interfaces_config.hpp b/src/catch2/interfaces/catch_interfaces_config.hpp index 4b2bd365..6c17081a 100644 --- a/src/catch2/interfaces/catch_interfaces_config.hpp +++ b/src/catch2/interfaces/catch_interfaces_config.hpp @@ -55,13 +55,14 @@ namespace Catch { }; }; class TestSpec; + class IStream; struct IConfig : Detail::NonCopyable { virtual ~IConfig(); virtual bool allowThrows() const = 0; - virtual std::ostream& defaultStream() const = 0; + virtual IStream const* defaultStream() const = 0; virtual StringRef name() const = 0; virtual bool includeSuccessfulResults() const = 0; virtual bool shouldDebugBreak() const = 0; diff --git a/src/catch2/interfaces/catch_interfaces_reporter.cpp b/src/catch2/interfaces/catch_interfaces_reporter.cpp index e5d3921f..5f5d274a 100644 --- a/src/catch2/interfaces/catch_interfaces_reporter.cpp +++ b/src/catch2/interfaces/catch_interfaces_reporter.cpp @@ -20,10 +20,10 @@ namespace Catch { - ReporterConfig::ReporterConfig( IConfig const* _fullConfig, std::ostream& _stream ) - : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + ReporterConfig::ReporterConfig( IConfig const* _fullConfig, IStream const* _stream ) + : m_stream( _stream ), m_fullConfig( _fullConfig ) {} - std::ostream& ReporterConfig::stream() const { return *m_stream; } + IStream const* ReporterConfig::stream() const { return m_stream; } IConfig const * ReporterConfig::fullConfig() const { return m_fullConfig; } AssertionStats::AssertionStats( AssertionResult const& _assertionResult, diff --git a/src/catch2/interfaces/catch_interfaces_reporter.hpp b/src/catch2/interfaces/catch_interfaces_reporter.hpp index c05648b7..41cf426b 100644 --- a/src/catch2/interfaces/catch_interfaces_reporter.hpp +++ b/src/catch2/interfaces/catch_interfaces_reporter.hpp @@ -30,15 +30,16 @@ namespace Catch { struct TestCaseInfo; class TestCaseHandle; struct IConfig; + class IStream; struct ReporterConfig { - ReporterConfig( IConfig const* _fullConfig, std::ostream& _stream ); + ReporterConfig( IConfig const* _fullConfig, IStream const* _stream ); - std::ostream& stream() const; + IStream const* stream() const; IConfig const* fullConfig() const; private: - std::ostream* m_stream; + IStream const* m_stream; IConfig const* m_fullConfig; }; diff --git a/src/catch2/internal/catch_console_colour.cpp b/src/catch2/internal/catch_console_colour.cpp index b3e4934e..b3d583d2 100644 --- a/src/catch2/internal/catch_console_colour.cpp +++ b/src/catch2/internal/catch_console_colour.cpp @@ -160,7 +160,7 @@ namespace { void setColour( const char* _escapeCode ) { // The escape sequence must be flushed to console, otherwise if // stdin and stderr are intermixed, we'd get accidentally coloured output. - getCurrentContext().getConfig()->defaultStream() + getCurrentContext().getConfig()->defaultStream()->stream() << '\033' << _escapeCode << std::flush; } }; diff --git a/src/catch2/internal/catch_stream.hpp b/src/catch2/internal/catch_stream.hpp index 77a35760..1b92b4dc 100644 --- a/src/catch2/internal/catch_stream.hpp +++ b/src/catch2/internal/catch_stream.hpp @@ -22,7 +22,8 @@ namespace Catch { std::ostream& cerr(); std::ostream& clog(); - struct IStream { + class IStream { + public: virtual ~IStream(); // = default virtual std::ostream& stream() const = 0; // Win32 colour supports requires us to identify whether a stream diff --git a/src/catch2/reporters/catch_reporter_common_base.hpp b/src/catch2/reporters/catch_reporter_common_base.hpp index 235c2606..b7610a93 100644 --- a/src/catch2/reporters/catch_reporter_common_base.hpp +++ b/src/catch2/reporters/catch_reporter_common_base.hpp @@ -9,6 +9,7 @@ #define CATCH_REPORTER_COMMON_BASE_HPP_INCLUDED #include +#include namespace Catch { /** @@ -23,13 +24,19 @@ namespace Catch { */ class ReporterBase : public IEventListener { protected: - //! Stream to write the output to + //! The stream wrapper as passed to us by outside code + IStream const* m_wrapped_stream; + //! Cached output stream from `m_wrapped_stream` to reduce + //! number of indirect calls needed to write output. std::ostream& m_stream; + + public: ReporterBase( ReporterConfig const& config ): IEventListener( config.fullConfig() ), - m_stream( config.stream() ) {} + m_wrapped_stream( config.stream() ), + m_stream( m_wrapped_stream->stream() ) {} /** diff --git a/src/catch2/reporters/catch_reporter_console.cpp b/src/catch2/reporters/catch_reporter_console.cpp index d5dd89c5..e79d6a63 100644 --- a/src/catch2/reporters/catch_reporter_console.cpp +++ b/src/catch2/reporters/catch_reporter_console.cpp @@ -355,7 +355,7 @@ public: ConsoleReporter::ConsoleReporter(ReporterConfig const& config) : StreamingReporterBase(config), - m_tablePrinter(Detail::make_unique(config.stream(), + m_tablePrinter(Detail::make_unique(m_stream, [&config]() -> std::vector { if (config.fullConfig()->benchmarkNoAnalysis()) { diff --git a/src/catch2/reporters/catch_reporter_xml.cpp b/src/catch2/reporters/catch_reporter_xml.cpp index 8c3338cb..78fa8647 100644 --- a/src/catch2/reporters/catch_reporter_xml.cpp +++ b/src/catch2/reporters/catch_reporter_xml.cpp @@ -24,7 +24,7 @@ namespace Catch { XmlReporter::XmlReporter( ReporterConfig const& _config ) : StreamingReporterBase( _config ), - m_xml(_config.stream()) + m_xml(m_stream) { m_preferences.shouldRedirectStdOut = true; m_preferences.shouldReportAllAssertions = true; diff --git a/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp b/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp index 40ae0179..a2416042 100644 --- a/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp +++ b/tests/SelfTest/IntrospectiveTests/Reporters.tests.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,16 @@ #include +namespace { + class StringIStream : public Catch::IStream { + public: + std::ostream& stream() const override { return sstr; } + std::string str() const { return sstr.str(); } + private: + mutable std::stringstream sstr; + }; +} + TEST_CASE( "The default listing implementation write to provided stream", "[reporters][reporter-helpers]" ) { using Catch::Matchers::ContainsSubstring; @@ -72,11 +83,11 @@ TEST_CASE( "Reporter's write listings to provided stream", "[reporters]" ) { for (auto const& factory : factories) { INFO("Tested reporter: " << factory.first); - std::stringstream sstream; + StringIStream sstream; Catch::ConfigData config_data; Catch::Config config( config_data ); - Catch::ReporterConfig rep_config( &config, sstream ); + Catch::ReporterConfig rep_config( &config, &sstream ); auto reporter = factory.second->create( rep_config ); DYNAMIC_SECTION( factory.first << " reporter lists tags" ) { @@ -162,8 +173,8 @@ TEST_CASE("Multireporter calls reporters and listeners in correct order", Catch::ConfigData config_data; Catch::Config config( config_data ); - std::stringstream sstream; - Catch::ReporterConfig rep_config( &config, sstream ); + StringIStream sstream; + Catch::ReporterConfig rep_config( &config, &sstream ); // We add reporters before listeners, to check that internally they // get sorted properly, and listeners are called first anyway. @@ -215,8 +226,8 @@ TEST_CASE("Multireporter updates ReporterPreferences properly", Catch::ConfigData config_data; Catch::Config config( config_data ); - std::stringstream sstream; - Catch::ReporterConfig rep_config( &config, sstream ); + StringIStream sstream; + Catch::ReporterConfig rep_config( &config, &sstream ); Catch::MultiReporter multiReporter( &config ); // Post init defaults