diff --git a/include/catch_session.hpp b/include/catch_session.hpp index d0dc9e9d..dc356614 100644 --- a/include/catch_session.hpp +++ b/include/catch_session.hpp @@ -23,12 +23,10 @@ namespace Catch { IStreamingReporterPtr createReporter( std::string const& reporterName, IConfigPtr const& config ) { - if( auto reporter = getRegistryHub().getReporterRegistry().create( reporterName, config ) ) - return reporter; + auto reporter = getRegistryHub().getReporterRegistry().create( reporterName, config ); + CATCH_ENFORCE( reporter, "No reporter registered with name: '" << reporterName << "'" ); - std::ostringstream oss; - oss << "No reporter registered with name: '" << reporterName << "'"; - throw std::domain_error( oss.str() ); + return reporter; } IStreamingReporterPtr makeReporter( std::shared_ptr const& config ) { @@ -105,11 +103,8 @@ namespace Catch { Session() : m_cli( makeCommandLineParser() ) { - if( alreadyInstantiated ) { - std::string msg = "Only one instance of Catch::Session can ever be used"; - Catch::cerr() << msg << std::endl; - throw std::logic_error( msg ); - } + if( alreadyInstantiated ) + CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); alreadyInstantiated = true; } ~Session() { diff --git a/include/internal/catch_commandline.hpp b/include/internal/catch_commandline.hpp index 742f01a4..89c55695 100644 --- a/include/internal/catch_commandline.hpp +++ b/include/internal/catch_commandline.hpp @@ -19,19 +19,16 @@ namespace Catch { inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } inline void abortAfterX( ConfigData& config, int x ) { - if( x < 1 ) - throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); + CATCH_ENFORCE( x >=1, "Value after -x or --abortAfter must be greater than zero" ); config.abortAfter = x; } - inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } + inline void addTestOrTags( ConfigData& config, std::string const& testSpec ) { config.testsOrTags.push_back( testSpec ); } inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } - inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } + inline void addReporterName( ConfigData& config, std::string const& reporterName ) { config.reporterNames.push_back( reporterName ); } - inline void addWarning( ConfigData& config, std::string const& _warning ) { - if( _warning == "NoAssertions" ) - config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); - else - throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' ); + inline void addWarning( ConfigData& config, std::string const& warning ) { + CATCH_ENFORCE( warning == "NoAssertions", "Unrecognised warning: '" << warning << "'" ); + config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); } inline void setOrder( ConfigData& config, std::string const& order ) { if( startsWith( "declared", order ) ) @@ -41,7 +38,7 @@ namespace Catch { else if( startsWith( "random", order ) ) config.runOrder = RunTests::InRandomOrder; else - throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); + CATCH_ENFORCE( false, "Unrecognised ordering: '" << order << '\'' ); } inline void setRngSeed( ConfigData& config, std::string const& seed ) { if( seed == "time" ) { @@ -51,8 +48,7 @@ namespace Catch { std::stringstream ss; ss << seed; ss >> config.rngSeed; - if( ss.fail() ) - throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" ); + CATCH_ENFORCE( !ss.fail(), "Argument to --rng-seed should be the word 'time' or a number" ); } } inline void setVerbosity( ConfigData& config, int level ) { @@ -74,15 +70,14 @@ namespace Catch { else if( mode == "auto" ) config.useColour = UseColour::Auto; else - throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); + CATCH_ENFORCE( false, "colour mode must be one of: auto, yes or no" ); } inline void forceColour( ConfigData& config ) { config.useColour = UseColour::Yes; } inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { std::ifstream f( _filename.c_str() ); - if( !f.is_open() ) - throw std::domain_error( "Unable to load input file: " + _filename ); + CATCH_ENFORCE( f.is_open(), "Unable to load input file: '" << _filename << "'" ); std::string line; while( std::getline( f, line ) ) { diff --git a/include/internal/catch_common.h b/include/internal/catch_common.h index bcb6bd51..c4ac2967 100644 --- a/include/internal/catch_common.h +++ b/include/internal/catch_common.h @@ -23,6 +23,7 @@ #include #include +#include namespace Catch { @@ -99,8 +100,6 @@ namespace Catch { inline bool alwaysTrue() { return true; } inline bool alwaysFalse() { return false; } - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); - void seedRng( IConfig const& config ); unsigned int rngSeed(); @@ -117,10 +116,29 @@ namespace Catch { T const& operator + ( T const& value, StreamEndStop ) { return value; } + + template + class Error { + std::ostringstream m_oss; + + public: + template + auto operator <<( T const& value ) -> Error& { + m_oss << value; + return *this; + } + + [[noreturn]] + void raise() { + throw ExceptionT( m_oss.str() ); + } + }; } #define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) -#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); +#define CATCH_INTERNAL_ERROR( msg ) do{ ( ::Catch::Error() << CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg ).raise(); } while(false) +#define CATCH_ERROR( msg ) ( ::Catch::Error<>() << msg ).raise() +#define CATCH_ENFORCE( condition, msg ) do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) #endif // TWOBLUECUBES_CATCH_COMMON_H_INCLUDED diff --git a/include/internal/catch_common.hpp b/include/internal/catch_common.hpp index 9f943777..fc82351e 100644 --- a/include/internal/catch_common.hpp +++ b/include/internal/catch_common.hpp @@ -106,13 +106,6 @@ namespace Catch { #endif return os; } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { - std::ostringstream oss; - oss << locationInfo << ": Internal Catch error: '" << message << '\''; - if( alwaysTrue() ) - throw std::logic_error( oss.str() ); - } } #endif // TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED diff --git a/include/internal/catch_config.hpp b/include/internal/catch_config.hpp index 8be68a81..869e4c70 100644 --- a/include/internal/catch_config.hpp +++ b/include/internal/catch_config.hpp @@ -118,7 +118,7 @@ namespace Catch { if( m_data.outputFilename == "%debug" ) return new DebugOutStream(); else - throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); + CATCH_ERROR( "Unrecognised stream: '" << m_data.outputFilename << "'" ); } else return new FileStream( m_data.outputFilename ); diff --git a/include/internal/catch_console_colour_impl.hpp b/include/internal/catch_console_colour_impl.hpp index f15494ab..07cb07b4 100644 --- a/include/internal/catch_console_colour_impl.hpp +++ b/include/internal/catch_console_colour_impl.hpp @@ -73,7 +73,7 @@ namespace { case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - case Colour::Bright: throw std::logic_error( "not a colour" ); + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); } } @@ -134,7 +134,7 @@ namespace { case Colour::BrightGreen: return setColour( "[1;32m" ); case Colour::BrightWhite: return setColour( "[1;37m" ); - case Colour::Bright: throw std::logic_error( "not a colour" ); + case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); } } static IColourImpl* instance() { diff --git a/include/internal/catch_run_context.hpp b/include/internal/catch_run_context.hpp index feb192ed..3d715c46 100644 --- a/include/internal/catch_run_context.hpp +++ b/include/internal/catch_run_context.hpp @@ -371,7 +371,7 @@ namespace Catch { if( IResultCapture* capture = getCurrentContext().getResultCapture() ) return *capture; else - throw std::logic_error( "No result capture instance" ); + CATCH_INTERNAL_ERROR( "No result capture instance" ); } } // end namespace Catch diff --git a/include/internal/catch_stream.hpp b/include/internal/catch_stream.hpp index cc6c38a1..b287548b 100644 --- a/include/internal/catch_stream.hpp +++ b/include/internal/catch_stream.hpp @@ -59,11 +59,7 @@ namespace Catch { FileStream::FileStream( std::string const& filename ) { m_ofs.open( filename.c_str() ); - if( m_ofs.fail() ) { - std::ostringstream oss; - oss << "Unable to open file: '" << filename << '\''; - throw std::domain_error( oss.str() ); - } + CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); } std::ostream& FileStream::stream() const { diff --git a/include/internal/catch_tag_alias_registry.hpp b/include/internal/catch_tag_alias_registry.hpp index f7928a64..fef96c35 100644 --- a/include/internal/catch_tag_alias_registry.hpp +++ b/include/internal/catch_tag_alias_registry.hpp @@ -40,24 +40,13 @@ namespace Catch { void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { - if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { - std::ostringstream oss; - oss << Colour( Colour::Red ) - << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" - << Colour( Colour::FileName ) - << lineInfo << '\n'; - throw std::domain_error( oss.str().c_str() ); - } - if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { - std::ostringstream oss; - oss << Colour( Colour::Red ) - << "error: tag alias, \"" << alias << "\" already registered.\n" - << "\tFirst seen at " - << Colour( Colour::Red ) << find(alias)->lineInfo << '\n' - << Colour( Colour::Red ) << "\tRedefined at " - << Colour( Colour::FileName) << lineInfo << '\n'; - throw std::domain_error( oss.str().c_str() ); - } + CATCH_ENFORCE( startsWith( alias, "[@" ) && endsWith( alias, ']' ), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); + + CATCH_ENFORCE( m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo ); } ITagAliasRegistry::~ITagAliasRegistry() {} diff --git a/include/internal/catch_test_case_info.hpp b/include/internal/catch_test_case_info.hpp index 0ee14c82..32c0e6e9 100644 --- a/include/internal/catch_test_case_info.hpp +++ b/include/internal/catch_test_case_info.hpp @@ -37,15 +37,10 @@ namespace Catch { return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); } inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { - if( isReservedTag( tag ) ) { - std::ostringstream ss; - ss << Colour(Colour::Red) - << "Tag name [" << tag << "] not allowed.\n" + CATCH_ENFORCE( !isReservedTag( tag ), + "Tag name: [" << tag << "] is not allowed.\n" << "Tag names starting with non alpha-numeric characters are reserved\n" - << Colour(Colour::FileName) - << _lineInfo << '\n'; - throw std::runtime_error(ss.str()); - } + << _lineInfo ); } TestCase makeTestCase( ITestCase* _testCase, diff --git a/include/internal/catch_test_case_registry_impl.hpp b/include/internal/catch_test_case_registry_impl.hpp index cecbc253..5406c543 100644 --- a/include/internal/catch_test_case_registry_impl.hpp +++ b/include/internal/catch_test_case_registry_impl.hpp @@ -62,18 +62,12 @@ namespace Catch { void enforceNoDuplicateTestCases( std::vector const& functions ) { std::set seenFunctions; - for( auto const function : functions ) { - std::pair::const_iterator, bool> prev = seenFunctions.insert( function ); - if( !prev.second ) { - std::ostringstream ss; - - ss << Colour( Colour::Red ) - << "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n' - << "\tRedefined at " << function.getTestCaseInfo().lineInfo << std::endl; - - throw std::runtime_error(ss.str()); - } + for( auto const& function : functions ) { + auto prev = seenFunctions.insert( function ); + CATCH_ENFORCE( prev.second, + "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); } } diff --git a/include/internal/catch_test_case_tracker.hpp b/include/internal/catch_test_case_tracker.hpp index beba96bd..d03fc936 100644 --- a/include/internal/catch_test_case_tracker.hpp +++ b/include/internal/catch_test_case_tracker.hpp @@ -202,11 +202,6 @@ namespace TestCaseTracking { m_ctx.currentTracker().close(); switch( m_runState ) { - case NotStarted: - case CompletedSuccessfully: - case Failed: - throw std::logic_error( "Illogical state" ); - case NeedsAnotherRun: break;; @@ -218,8 +213,13 @@ namespace TestCaseTracking { m_runState = CompletedSuccessfully; break; + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); + default: - throw std::logic_error( "Unexpected state" ); + CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); } moveToParent(); m_ctx.completeCycle(); diff --git a/include/internal/catch_wildcard_pattern.hpp b/include/internal/catch_wildcard_pattern.hpp index b9827b96..04d70f12 100644 --- a/include/internal/catch_wildcard_pattern.hpp +++ b/include/internal/catch_wildcard_pattern.hpp @@ -49,16 +49,9 @@ namespace Catch return startsWith( adjustCase( str ), m_pattern ); case WildcardAtBothEnds: return contains( adjustCase( str ), m_pattern ); + default: + CATCH_INTERNAL_ERROR( "Unknown enum" ); } - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif - throw std::logic_error( "Unknown enum" ); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif } private: std::string adjustCase( std::string const& str ) const {