Introduced ReusableStringStream and removed all uses of std::ostringstream from the main path

ReusableStringStream holds a std::ostringstream internally, but only exposes the ostream interface.
It caches a pool of ostringstreams in a vector which is currently global, but will be made thread-local.

Altogether this should enable both runtime and compile-time benefits. although more work is needed to realise the compile time opportunities.
This commit is contained in:
Phil Nash 2017-11-07 18:01:10 +00:00
parent 868e125d49
commit 56e1075613
26 changed files with 202 additions and 114 deletions

View File

@ -36,9 +36,9 @@ namespace Detail {
} }
std::string Approx::toString() const { std::string Approx::toString() const {
std::ostringstream oss; ReusableStringStream rss;
oss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )";
return oss.str(); return rss.str();
} }
bool Approx::equalityComparisonImpl(const double other) const { bool Approx::equalityComparisonImpl(const double other) const {

View File

@ -8,10 +8,10 @@
#ifndef TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED #ifndef TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED #define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
#include "catch_enforce.h"
#include "catch_tostring.h" #include "catch_tostring.h"
#include <type_traits> #include <type_traits>
#include <stdexcept>
namespace Catch { namespace Catch {
namespace Detail { namespace Detail {
@ -83,9 +83,12 @@ namespace Detail {
template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
Approx& epsilon( T const& newEpsilon ) { Approx& epsilon( T const& newEpsilon ) {
double epsilonAsDouble = static_cast<double>(newEpsilon); double epsilonAsDouble = static_cast<double>(newEpsilon);
CATCH_ENFORCE(epsilonAsDouble >= 0 && epsilonAsDouble <= 1.0, if( epsilonAsDouble < 0 || epsilonAsDouble > 1.0 ) {
"Invalid Approx::epsilon: " << epsilonAsDouble throw std::domain_error
<< ", Approx::epsilon has to be between 0 and 1"); ( "Invalid Approx::epsilon: " +
Catch::Detail::stringify( epsilonAsDouble ) +
", Approx::epsilon has to be between 0 and 1" );
}
m_epsilon = epsilonAsDouble; m_epsilon = epsilonAsDouble;
return *this; return *this;
} }
@ -93,9 +96,13 @@ namespace Detail {
template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type> template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
Approx& margin( T const& newMargin ) { Approx& margin( T const& newMargin ) {
double marginAsDouble = static_cast<double>(newMargin); double marginAsDouble = static_cast<double>(newMargin);
CATCH_ENFORCE(marginAsDouble >= 0, if( marginAsDouble < 0 ) {
"Invalid Approx::margin: " << marginAsDouble throw std::domain_error
<< ", Approx::Margin has to be non-negative."); ( "Invalid Approx::margin: " +
Catch::Detail::stringify( marginAsDouble ) +
", Approx::Margin has to be non-negative." );
}
m_margin = marginAsDouble; m_margin = marginAsDouble;
return *this; return *this;
} }

View File

@ -17,10 +17,9 @@ namespace Catch {
if( reconstructedExpression.empty() ) { if( reconstructedExpression.empty() ) {
if( lazyExpression ) { if( lazyExpression ) {
// !TBD Use stringstream for now, but rework above to pass stream in ReusableStringStream rss;
std::ostringstream oss; rss << lazyExpression;
oss << lazyExpression; reconstructedExpression = rss.str();
reconstructedExpression = oss.str();
} }
} }
return reconstructedExpression; return reconstructedExpression;

View File

@ -23,6 +23,8 @@
#include "catch_debugger.h" #include "catch_debugger.h"
#include "catch_windows_h_proxy.h" #include "catch_windows_h_proxy.h"
#include <sstream>
namespace Catch { namespace Catch {
namespace { namespace {

View File

@ -8,12 +8,13 @@
#define TWOBLUECUBES_CATCH_ENFORCE_H_INCLUDED #define TWOBLUECUBES_CATCH_ENFORCE_H_INCLUDED
#include "catch_common.h" #include "catch_common.h"
#include "catch_stream.h"
#include <sstream>
#include <stdexcept> #include <stdexcept>
#include <iosfwd>
#define CATCH_PREPARE_EXCEPTION( type, msg ) \ #define CATCH_PREPARE_EXCEPTION( type, msg ) \
type( static_cast<std::ostringstream&&>( std::ostringstream() << msg ).str() ) type( static_cast<std::ostringstream&&>( Catch::ReusableStringStream().get() << msg ).str() )
#define CATCH_INTERNAL_ERROR( msg ) \ #define CATCH_INTERNAL_ERROR( msg ) \
throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg); throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg);
#define CATCH_ERROR( msg ) \ #define CATCH_ERROR( msg ) \

View File

@ -115,13 +115,14 @@ namespace Catch {
} }
for( auto const& tagCount : tagCounts ) { for( auto const& tagCount : tagCounts ) {
std::ostringstream oss; ReusableStringStream rss;
oss << " " << std::setw(2) << tagCount.second.count << " "; rss << " " << std::setw(2) << tagCount.second.count << " ";
auto str = rss.str();
auto wrapper = Column( tagCount.second.all() ) auto wrapper = Column( tagCount.second.all() )
.initialIndent( 0 ) .initialIndent( 0 )
.indent( oss.str().size() ) .indent( str.size() )
.width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); .width( CATCH_CONFIG_CONSOLE_WIDTH-10 );
Catch::cout() << oss.str() << wrapper << '\n'; Catch::cout() << str << wrapper << '\n';
} }
Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl;
return tagCounts.size(); return tagCounts.size();

View File

@ -9,9 +9,9 @@
#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED #define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
#include <string> #include <string>
#include <sstream>
#include "catch_result_type.h" #include "catch_result_type.h"
#include "catch_common.h" #include "catch_common.h"
#include "catch_stream.h"
namespace Catch { namespace Catch {
@ -40,8 +40,7 @@ namespace Catch {
return *this; return *this;
} }
// !TBD reuse a global/ thread-local stream ReusableStringStream m_stream;
std::ostringstream m_stream;
}; };
struct MessageBuilder : MessageStream { struct MessageBuilder : MessageStream {

View File

@ -6,6 +6,7 @@
#include <cassert> #include <cassert>
#include <algorithm> #include <algorithm>
#include <sstream>
namespace Catch { namespace Catch {
@ -13,7 +14,7 @@ namespace Catch {
: m_stream(stream), : m_stream(stream),
m_prevBuf(stream.rdbuf()), m_prevBuf(stream.rdbuf()),
m_targetString(targetString) { m_targetString(targetString) {
stream.rdbuf(m_oss.rdbuf()); stream.rdbuf(m_oss.get().rdbuf());
} }
StreamRedirect::~StreamRedirect() { StreamRedirect::~StreamRedirect() {
@ -24,8 +25,8 @@ namespace Catch {
StdErrRedirect::StdErrRedirect(std::string & targetString) StdErrRedirect::StdErrRedirect(std::string & targetString)
:m_cerrBuf(cerr().rdbuf()), m_clogBuf(clog().rdbuf()), :m_cerrBuf(cerr().rdbuf()), m_clogBuf(clog().rdbuf()),
m_targetString(targetString) { m_targetString(targetString) {
cerr().rdbuf(m_oss.rdbuf()); cerr().rdbuf(m_oss.get().rdbuf());
clog().rdbuf(m_oss.rdbuf()); clog().rdbuf(m_oss.get().rdbuf());
} }
StdErrRedirect::~StdErrRedirect() { StdErrRedirect::~StdErrRedirect() {

View File

@ -32,13 +32,12 @@ namespace Catch {
public: public:
StreamRedirect(std::ostream& stream, std::string& targetString); StreamRedirect(std::ostream& stream, std::string& targetString);
~StreamRedirect(); ~StreamRedirect();
private: private:
std::ostream& m_stream; std::ostream& m_stream;
std::streambuf* m_prevBuf; std::streambuf* m_prevBuf;
std::ostringstream m_oss; ReusableStringStream m_oss;
std::string& m_targetString; std::string& m_targetString;
}; };
@ -52,7 +51,7 @@ namespace Catch {
private: private:
std::streambuf* m_cerrBuf; std::streambuf* m_cerrBuf;
std::streambuf* m_clogBuf; std::streambuf* m_clogBuf;
std::ostringstream m_oss; ReusableStringStream m_oss;
std::string& m_targetString; std::string& m_targetString;
}; };

View File

@ -13,10 +13,16 @@
#include "catch_debug_console.h" #include "catch_debug_console.h"
#include "catch_stringref.h" #include "catch_stringref.h"
#include <stdexcept>
#include <cstdio> #include <cstdio>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <sstream>
#include <vector>
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wexit-time-destructors"
#endif
namespace Catch { namespace Catch {
@ -132,18 +138,64 @@ namespace Catch {
return new detail::FileStream( filename ); return new detail::FileStream( filename );
} }
// This class encapsulates the idea of a pool of ostringstreams that can be reused.
struct StringStreams {
std::vector<std::unique_ptr<std::ostringstream>> m_streams;
std::vector<std::size_t> m_unused;
std::ostringstream m_referenceStream; // Used for copy state/ flags from
auto add() -> std::size_t {
if( m_unused.empty() ) {
m_streams.push_back( std::unique_ptr<std::ostringstream>( new std::ostringstream ) );
return m_streams.size()-1;
}
else {
auto index = m_unused.back();
m_unused.pop_back();
return index;
}
}
void release( std::size_t index ) {
m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state
m_unused.push_back(index);
}
// !TBD: put in TLS
static auto instance() -> StringStreams& {
static StringStreams s_stringStreams;
return s_stringStreams;
}
};
ReusableStringStream::ReusableStringStream()
: m_index( StringStreams::instance().add() ),
m_oss( StringStreams::instance().m_streams[m_index].get() )
{}
ReusableStringStream::~ReusableStringStream() {
static_cast<std::ostringstream*>( m_oss )->str("");
m_oss->clear();
StringStreams::instance().release( m_index );
}
auto ReusableStringStream::str() const -> std::string {
return static_cast<std::ostringstream*>( m_oss )->str();
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
std::ostream& cout() { std::ostream& cout() { return std::cout; }
return std::cout; std::ostream& cerr() { return std::cerr; }
} std::ostream& clog() { return std::clog; }
std::ostream& cerr() {
return std::cerr;
}
std::ostream& clog() {
return std::clog;
}
#endif #endif
} }
#if defined(__clang__)
# pragma clang diagnostic pop
#endif

View File

@ -10,6 +10,8 @@
#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
#include <iosfwd> #include <iosfwd>
#include <cstddef>
#include <ostream>
namespace Catch { namespace Catch {
@ -25,6 +27,23 @@ namespace Catch {
}; };
auto makeStream( StringRef const &filename ) -> IStream const*; auto makeStream( StringRef const &filename ) -> IStream const*;
class ReusableStringStream {
std::size_t m_index;
std::ostream* m_oss;
public:
ReusableStringStream();
~ReusableStringStream();
auto str() const -> std::string;
template<typename T>
auto operator << ( T const& value ) -> ReusableStringStream& {
*m_oss << value;
return *this;
}
auto get() -> std::ostream& { return *m_oss; }
};
} }
#endif // TWOBLUECUBES_CATCH_STREAM_H_INCLUDED #endif // TWOBLUECUBES_CATCH_STREAM_H_INCLUDED

View File

@ -10,9 +10,10 @@
#include "catch_console_colour.h" #include "catch_console_colour.h"
#include "catch_enforce.h" #include "catch_enforce.h"
#include "catch_interfaces_registry_hub.h" #include "catch_interfaces_registry_hub.h"
#include "catch_stream.h"
#include "catch_string_manip.h" #include "catch_string_manip.h"
#include <sstream>
namespace Catch { namespace Catch {
TagAliasRegistry::~TagAliasRegistry() {} TagAliasRegistry::~TagAliasRegistry() {}

View File

@ -15,6 +15,7 @@
#include <cctype> #include <cctype>
#include <exception> #include <exception>
#include <algorithm> #include <algorithm>
#include <sstream>
namespace Catch { namespace Catch {

View File

@ -66,9 +66,9 @@ namespace Catch {
void TestRegistry::registerTest( TestCase const& testCase ) { void TestRegistry::registerTest( TestCase const& testCase ) {
std::string name = testCase.getTestCaseInfo().name; std::string name = testCase.getTestCaseInfo().name;
if( name.empty() ) { if( name.empty() ) {
std::ostringstream oss; ReusableStringStream rss;
oss << "Anonymous test case " << ++m_unnamedCount; rss << "Anonymous test case " << ++m_unnamedCount;
return registerTest( testCase.withName( oss.str() ) ); return registerTest( testCase.withName( rss.str() ) );
} }
m_functions.push_back( testCase ); m_functions.push_back( testCase );
} }

View File

@ -13,6 +13,7 @@
#include <assert.h> #include <assert.h>
#include <stdexcept> #include <stdexcept>
#include <memory> #include <memory>
#include <sstream>
#if defined(__clang__) #if defined(__clang__)
# pragma clang diagnostic push # pragma clang diagnostic push

View File

@ -52,22 +52,22 @@ namespace Detail {
} }
unsigned char const *bytes = static_cast<unsigned char const *>(object); unsigned char const *bytes = static_cast<unsigned char const *>(object);
std::ostringstream os; ReusableStringStream rss;
os << "0x" << std::setfill('0') << std::hex; rss << "0x" << std::setfill('0') << std::hex;
for( ; i != end; i += inc ) for( ; i != end; i += inc )
os << std::setw(2) << static_cast<unsigned>(bytes[i]); rss << std::setw(2) << static_cast<unsigned>(bytes[i]);
return os.str(); return rss.str();
} }
} }
template<typename T> template<typename T>
std::string fpToString( T value, int precision ) { std::string fpToString( T value, int precision ) {
std::ostringstream oss; ReusableStringStream rss;
oss << std::setprecision( precision ) rss << std::setprecision( precision )
<< std::fixed << std::fixed
<< value; << value;
std::string d = oss.str(); std::string d = rss.str();
std::size_t i = d.find_last_not_of( '0' ); std::size_t i = d.find_last_not_of( '0' );
if( i != std::string::npos && i != d.size()-1 ) { if( i != std::string::npos && i != d.size()-1 ) {
if( d[i] == '.' ) if( d[i] == '.' )
@ -153,12 +153,12 @@ std::string StringMaker<long>::convert(long value) {
return ::Catch::Detail::stringify(static_cast<long long>(value)); return ::Catch::Detail::stringify(static_cast<long long>(value));
} }
std::string StringMaker<long long>::convert(long long value) { std::string StringMaker<long long>::convert(long long value) {
std::ostringstream oss; ReusableStringStream rss;
oss << value; rss << value;
if (value > Detail::hexThreshold) { if (value > Detail::hexThreshold) {
oss << " (0x" << std::hex << value << ')'; rss << " (0x" << std::hex << value << ')';
} }
return oss.str(); return rss.str();
} }
std::string StringMaker<unsigned int>::convert(unsigned int value) { std::string StringMaker<unsigned int>::convert(unsigned int value) {
@ -168,12 +168,12 @@ std::string StringMaker<unsigned long>::convert(unsigned long value) {
return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); return ::Catch::Detail::stringify(static_cast<unsigned long long>(value));
} }
std::string StringMaker<unsigned long long>::convert(unsigned long long value) { std::string StringMaker<unsigned long long>::convert(unsigned long long value) {
std::ostringstream oss; ReusableStringStream rss;
oss << value; rss << value;
if (value > Detail::hexThreshold) { if (value > Detail::hexThreshold) {
oss << " (0x" << std::hex << value << ')'; rss << " (0x" << std::hex << value << ')';
} }
return oss.str(); return rss.str();
} }

View File

@ -9,11 +9,11 @@
#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED #define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
#include <sstream>
#include <vector> #include <vector>
#include <cstddef> #include <cstddef>
#include <type_traits> #include <type_traits>
#include <string> #include <string>
#include "catch_stream.h"
#ifdef __OBJC__ #ifdef __OBJC__
#include "catch_objc_arc.hpp" #include "catch_objc_arc.hpp"
@ -66,9 +66,9 @@ namespace Catch {
static static
typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
convert(const Fake& t) { convert(const Fake& t) {
std::ostringstream sstr; ReusableStringStream rss;
sstr << t; rss << t;
return sstr.str(); return rss.str();
} }
template <typename Fake = T> template <typename Fake = T>
@ -221,15 +221,15 @@ namespace Catch {
namespace Detail { namespace Detail {
template<typename InputIterator> template<typename InputIterator>
std::string rangeToString(InputIterator first, InputIterator last) { std::string rangeToString(InputIterator first, InputIterator last) {
std::ostringstream oss; ReusableStringStream rss;
oss << "{ "; rss << "{ ";
if (first != last) { if (first != last) {
oss << ::Catch::Detail::stringify(*first); rss << ::Catch::Detail::stringify(*first);
for (++first; first != last; ++first) for (++first; first != last; ++first)
oss << ", " << ::Catch::Detail::stringify(*first); rss << ", " << ::Catch::Detail::stringify(*first);
} }
oss << " }"; rss << " }";
return oss.str(); return rss.str();
} }
} }
@ -290,13 +290,13 @@ namespace Catch {
template<typename T1, typename T2> template<typename T1, typename T2>
struct StringMaker<std::pair<T1, T2> > { struct StringMaker<std::pair<T1, T2> > {
static std::string convert(const std::pair<T1, T2>& pair) { static std::string convert(const std::pair<T1, T2>& pair) {
std::ostringstream oss; ReusableStringStream rss;
oss << "{ " rss << "{ "
<< ::Catch::Detail::stringify(pair.first) << ::Catch::Detail::stringify(pair.first)
<< ", " << ", "
<< ::Catch::Detail::stringify(pair.second) << ::Catch::Detail::stringify(pair.second)
<< " }"; << " }";
return oss.str(); return rss.str();
} }
}; };
} }
@ -334,11 +334,11 @@ namespace Catch {
template<typename ...Types> template<typename ...Types>
struct StringMaker<std::tuple<Types...>> { struct StringMaker<std::tuple<Types...>> {
static std::string convert(const std::tuple<Types...>& tuple) { static std::string convert(const std::tuple<Types...>& tuple) {
std::ostringstream os; ReusableStringStream rss;
os << '{'; rss << '{';
Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, os); Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
os << " }"; rss << " }";
return os.str(); return rss.str();
} }
}; };
} }
@ -358,10 +358,10 @@ struct ratio_string {
template <class Ratio> template <class Ratio>
std::string ratio_string<Ratio>::symbol() { std::string ratio_string<Ratio>::symbol() {
std::ostringstream oss; Catch::ReusableStringStream rss;
oss << '[' << Ratio::num << '/' rss << '[' << Ratio::num << '/'
<< Ratio::den << ']'; << Ratio::den << ']';
return oss.str(); return rss.str();
} }
template <> template <>
struct ratio_string<std::atto> { struct ratio_string<std::atto> {
@ -394,33 +394,33 @@ namespace Catch {
template<typename Value, typename Ratio> template<typename Value, typename Ratio>
struct StringMaker<std::chrono::duration<Value, Ratio>> { struct StringMaker<std::chrono::duration<Value, Ratio>> {
static std::string convert(std::chrono::duration<Value, Ratio> const& duration) { static std::string convert(std::chrono::duration<Value, Ratio> const& duration) {
std::ostringstream oss; ReusableStringStream rss;
oss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's'; rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
return oss.str(); return rss.str();
} }
}; };
template<typename Value> template<typename Value>
struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> { struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {
static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) { static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) {
std::ostringstream oss; ReusableStringStream rss;
oss << duration.count() << " s"; rss << duration.count() << " s";
return oss.str(); return rss.str();
} }
}; };
template<typename Value> template<typename Value>
struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> { struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {
static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) { static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) {
std::ostringstream oss; ReusableStringStream rss;
oss << duration.count() << " m"; rss << duration.count() << " m";
return oss.str(); return rss.str();
} }
}; };
template<typename Value> template<typename Value>
struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> { struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {
static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) { static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) {
std::ostringstream oss; ReusableStringStream rss;
oss << duration.count() << " h"; rss << duration.count() << " h";
return oss.str(); return rss.str();
} }
}; };

View File

@ -9,6 +9,7 @@
#include "catch_enforce.h" #include "catch_enforce.h"
#include "catch_string_manip.h" #include "catch_string_manip.h"
#include <sstream>
namespace Catch { namespace Catch {

View File

@ -11,7 +11,6 @@
#include "catch_stream.h" #include "catch_stream.h"
#include "catch_compiler_capabilities.h" #include "catch_compiler_capabilities.h"
#include <sstream>
#include <vector> #include <vector>
namespace Catch { namespace Catch {
@ -73,10 +72,9 @@ namespace Catch {
template<typename T> template<typename T>
XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
m_oss.clear(); ReusableStringStream rss;
m_oss.str(std::string()); rss << attribute;
m_oss << attribute; return writeAttribute( name, rss.str() );
return writeAttribute( name, m_oss.str() );
} }
XmlWriter& writeText( std::string const& text, bool indent = true ); XmlWriter& writeText( std::string const& text, bool indent = true );
@ -100,7 +98,6 @@ namespace Catch {
std::vector<std::string> m_tags; std::vector<std::string> m_tags;
std::string m_indent; std::string m_indent;
std::ostream& m_os; std::ostream& m_os;
std::ostringstream m_oss;
}; };
} }

View File

@ -8,16 +8,15 @@
#ifndef TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #ifndef TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED #define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
#include "../internal/catch_enforce.h"
#include "../internal/catch_interfaces_reporter.h" #include "../internal/catch_interfaces_reporter.h"
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
#include <cfloat> #include <cfloat>
#include <cstdio> #include <cstdio>
#include <assert.h> #include <assert.h>
#include <memory> #include <memory>
#include <ostream>
namespace Catch { namespace Catch {
void prepareExpandedExpression(AssertionResult& result); void prepareExpandedExpression(AssertionResult& result);
@ -33,7 +32,8 @@ namespace Catch {
stream( _config.stream() ) stream( _config.stream() )
{ {
m_reporterPrefs.shouldRedirectStdOut = false; m_reporterPrefs.shouldRedirectStdOut = false;
CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) )
throw std::domain_error( "Verbosity level not supported by this reporter" );
} }
ReporterPreferences getPreferences() const override { ReporterPreferences getPreferences() const override {
@ -147,7 +147,8 @@ namespace Catch {
stream( _config.stream() ) stream( _config.stream() )
{ {
m_reporterPrefs.shouldRedirectStdOut = false; m_reporterPrefs.shouldRedirectStdOut = false;
CATCH_ENFORCE( DerivedT::getSupportedVerbosities().count( m_config->verbosity() ), "Verbosity level not supported by this reporter" ); if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) )
throw std::domain_error( "Verbosity level not supported by this reporter" );
} }
~CumulativeReporterBase() override = default; ~CumulativeReporterBase() override = default;

View File

@ -543,9 +543,9 @@ namespace Catch {
colour( _colour ) colour( _colour )
{} {}
SummaryColumn addRow( std::size_t count ) { SummaryColumn addRow( std::size_t count ) {
std::ostringstream oss; ReusableStringStream rss;
oss << count; rss << count;
std::string row = oss.str(); std::string row = rss.str();
for( auto& oldRow : rows ) { for( auto& oldRow : rows ) {
while( oldRow.size() < row.size() ) while( oldRow.size() < row.size() )
oldRow = ' ' + oldRow; oldRow = ' ' + oldRow;

View File

@ -14,7 +14,7 @@
#include "../internal/catch_timer.h" #include "../internal/catch_timer.h"
#include <assert.h> #include <assert.h>
#include <sstream>
#include <ctime> #include <ctime>
#include <algorithm> #include <algorithm>
@ -230,15 +230,15 @@ namespace Catch {
xml.writeAttribute( "message", result.getExpandedExpression() ); xml.writeAttribute( "message", result.getExpandedExpression() );
xml.writeAttribute( "type", result.getTestMacroName() ); xml.writeAttribute( "type", result.getTestMacroName() );
std::ostringstream oss; ReusableStringStream rss;
if( !result.getMessage().empty() ) if( !result.getMessage().empty() )
oss << result.getMessage() << '\n'; rss << result.getMessage() << '\n';
for( auto const& msg : stats.infoMessages ) for( auto const& msg : stats.infoMessages )
if( msg.type == ResultWas::Info ) if( msg.type == ResultWas::Info )
oss << msg.message << '\n'; rss << msg.message << '\n';
oss << "at " << result.getSourceInfo(); rss << "at " << result.getSourceInfo();
xml.writeText( oss.str(), false ); xml.writeText( rss.str(), false );
} }
} }

View File

@ -14,6 +14,8 @@
// file can be distributed as a single header that works with the main // file can be distributed as a single header that works with the main
// Catch single header. // Catch single header.
#include "../internal/catch_enforce.h"
#include <cstring> #include <cstring>
#ifdef __clang__ #ifdef __clang__
@ -69,9 +71,9 @@ namespace Catch {
AssertionResult const& result = assertionStats.assertionResult; AssertionResult const& result = assertionStats.assertionResult;
if( !result.isOk() ) { if( !result.isOk() ) {
std::ostringstream msg; ReusableStringStream msg;
if( !m_headerPrintedForThisSection ) if( !m_headerPrintedForThisSection )
printSectionHeader( msg ); printSectionHeader( msg.get() );
m_headerPrintedForThisSection = true; m_headerPrintedForThisSection = true;
msg << result.getSourceInfo() << "\n"; msg << result.getSourceInfo() << "\n";

View File

@ -8,6 +8,8 @@
#include "catch.hpp" #include "catch.hpp"
#include <sstream>
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wweak-vtables" #pragma clang diagnostic ignored "-Wweak-vtables"

View File

@ -18,6 +18,7 @@
#include <iostream> #include <iostream>
#include <cerrno> #include <cerrno>
#include <limits> #include <limits>
#include <sstream>
TEST_CASE( "random SECTION tests", "[.][sections][failing]" ) { TEST_CASE( "random SECTION tests", "[.][sections][failing]" ) {
int a = 1; int a = 1;

View File

@ -15,10 +15,11 @@
// that is triggered when compiling as Win32|Release // that is triggered when compiling as Win32|Release
#endif #endif
#include <stdio.h>
#include "catch.hpp" #include "catch.hpp"
#include <stdio.h>
#include <sstream>
namespace Catch { namespace Catch {
std::string toString( const std::pair<int, int>& value ) { std::string toString( const std::pair<int, int>& value ) {
std::ostringstream oss; std::ostringstream oss;