Add support for -fno-exceptions (or equivalent)

This means

* Adding new configuration toggle `CATCH_CONFIG_DISABLE_EXCEPTIONS`
and a best-guess configuration auto-checking for it.
* Adding new set of internal macros, `CATCH_TRY`, `CATCH_CATCH_ALL`
and `CATCH_CATCH_ANON` that can be used in place of regular `try`,
`catch(...)` and `catch(T const&)` respectively, while disappearing
when `CATCH_CONFIG_DISABLE_EXCEPTIONS` is enabled.
* Replacing all uses of `throw` with calls to `Catch::throw_exception`
customization point.
* Providing a default implementation for the above customization point
when `CATCH_CONFIG_DISABLE_EXCEPTIONS` is set.
* Letting users override this implementation with their own.
* Some minor changes and ifdefs all around to support the above
This commit is contained in:
Martin Hořeňovský 2018-09-03 17:52:48 +02:00
parent 86da2846af
commit 8b01883854
12 changed files with 102 additions and 23 deletions

View File

@ -81,8 +81,13 @@ namespace Catch {
// (To go back to the test and change execution, jump over the throw, next)
CATCH_BREAK_INTO_DEBUGGER();
}
if( m_reaction.shouldThrow )
if (m_reaction.shouldThrow) {
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
throw Catch::TestFailureException();
#else
CATCH_ERROR( "Test failure requires aborting test!" );
#endif
}
}
void AssertionHandler::setCompleted() {
m_completed = true;

View File

@ -21,7 +21,7 @@
#define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"
#endif
#if defined(CATCH_CONFIG_FAST_COMPILE)
#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
///////////////////////////////////////////////////////////////////////////////
// Another way to speed-up compilation is to omit local try-catch for REQUIRE*

View File

@ -14,6 +14,7 @@
// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled?
// ****************
// Note to maintainers: if new toggles are added please document them
// in configuration.md, too
@ -131,7 +132,12 @@
#endif // _MSC_VER
////////////////////////////////////////////////////////////////////////////////
// Check if we are compiled with -fno-exceptions or equivalent
#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)
# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED
#endif
////////////////////////////////////////////////////////////////////////////////
// DJGPP
#ifdef __DJGPP__
# define CATCH_INTERNAL_CONFIG_NO_WCHAR
@ -179,6 +185,10 @@
# define CATCH_CONFIG_NEW_CAPTURE
#endif
#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
# define CATCH_CONFIG_DISABLE_EXCEPTIONS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
@ -192,6 +202,16 @@
# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
#endif
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
#define CATCH_TRY if ((true))
#define CATCH_CATCH_ALL if ((false))
#define CATCH_CATCH_ANON(type) if ((false))
#else
#define CATCH_TRY try
#define CATCH_CATCH_ALL catch (...)
#define CATCH_CATCH_ANON(type) catch (type)
#endif
#endif // TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED

View File

@ -0,0 +1,19 @@
/*
* Created by Martin on 03/09/2018.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_enforce.h"
namespace Catch {
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER)
[[noreturn]]
void throw_exception(std::exception const& e) {
Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n"
<< "The message was: " << e.what() << '\n';
std::terminate();
}
#endif
} // namespace Catch;

View File

@ -8,19 +8,35 @@
#define TWOBLUECUBES_CATCH_ENFORCE_H_INCLUDED
#include "catch_common.h"
#include "catch_compiler_capabilities.h"
#include "catch_stream.h"
#include <stdexcept>
namespace Catch {
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
template <typename Ex>
[[noreturn]]
void throw_exception(Ex const& e) {
throw e;
}
#else // ^^ Exceptions are enabled // Exceptions are disabled vv
[[noreturn]]
void throw_exception(std::exception const& e);
#endif
} // namespace Catch;
#define CATCH_PREPARE_EXCEPTION( type, msg ) \
type( ( Catch::ReusableStringStream() << msg ).str() )
#define CATCH_INTERNAL_ERROR( msg ) \
throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg);
Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg))
#define CATCH_ERROR( msg ) \
throw CATCH_PREPARE_EXCEPTION( std::domain_error, msg )
Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::domain_error, msg ))
#define CATCH_RUNTIME_ERROR( msg ) \
throw CATCH_PREPARE_EXCEPTION( std::runtime_error, msg )
Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::runtime_error, msg ))
#define CATCH_ENFORCE( condition, msg ) \
do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false)
#endif // TWOBLUECUBES_CATCH_ENFORCE_H_INCLUDED

View File

@ -6,8 +6,10 @@
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include "catch_assertionhandler.h"
#include "catch_exception_translator_registry.h"
#include "catch_assertionhandler.h"
#include "catch_compiler_capabilities.h"
#include "catch_enforce.h"
#ifdef __OBJC__
#import "Foundation/Foundation.h"
@ -22,6 +24,7 @@ namespace Catch {
m_translators.push_back( std::unique_ptr<const IExceptionTranslator>( translator ) );
}
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
std::string ExceptionTranslatorRegistry::translateActiveException() const {
try {
#ifdef __OBJC__
@ -64,6 +67,13 @@ namespace Catch {
}
}
#else // ^^ Exceptions are enabled // Exceptions are disabled vv
std::string ExceptionTranslatorRegistry::translateActiveException() const {
CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
}
#endif
std::string ExceptionTranslatorRegistry::tryTranslators() const {
if( m_translators.empty() )
std::rethrow_exception(std::current_exception());

View File

@ -1,4 +1,5 @@
#include "catch_run_context.h"
#include "catch_compiler_capabilities.h"
#include "catch_context.h"
#include "catch_enforce.h"
#include "catch_random_number_generator.h"
@ -327,7 +328,7 @@ namespace Catch {
seedRng(*m_config);
Timer timer;
try {
CATCH_TRY {
if (m_reporter->getPreferences().shouldRedirectStdOut) {
#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
RedirectedStdOut redirectedStdOut;
@ -347,9 +348,9 @@ namespace Catch {
invokeActiveTestCase();
}
duration = timer.getElapsedSeconds();
} catch (TestFailureException&) {
} CATCH_CATCH_ANON (TestFailureException&) {
// This just means the test was aborted due to failure
} catch (...) {
} CATCH_CATCH_ALL {
// Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
// are reported without translation at the point of origin.
if( m_shouldReportUnexpected ) {

View File

@ -119,10 +119,12 @@ namespace Catch {
Session::Session() {
static bool alreadyInstantiated = false;
if( alreadyInstantiated ) {
try { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); }
catch(...) { getMutableRegistryHub().registerStartupException(); }
CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); }
CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); }
}
// There cannot be exceptions at startup in no-exception mode.
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
if ( !exceptions.empty() ) {
m_startupExceptions = true;
@ -137,6 +139,7 @@ namespace Catch {
}
}
}
#endif
alreadyInstantiated = true;
m_cli = makeCommandLineParser( m_configData );
@ -251,11 +254,11 @@ namespace Catch {
if( m_startupExceptions )
return 1;
if( m_configData.showHelp || m_configData.libIdentify )
if (m_configData.showHelp || m_configData.libIdentify) {
return 0;
}
try
{
CATCH_TRY {
config(); // Force config to be constructed
seedRng( *m_config );
@ -273,10 +276,12 @@ namespace Catch {
// of 256 tests has failed
return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast<int>(totals.assertions.failed)));
}
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
catch( std::exception& ex ) {
Catch::cerr() << ex.what() << std::endl;
return MaxExitCode;
}
#endif
}
} // end namespace Catch

View File

@ -7,13 +7,13 @@
*/
#include "catch_startup_exception_registry.h"
#include "catch_compiler_capabilities.h"
namespace Catch {
void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept {
try {
CATCH_TRY {
m_exceptions.push_back(exception);
}
catch(...) {
} CATCH_CATCH_ALL {
// If we run out of memory during start-up there's really not a lot more we can do about it
std::terminate();
}

View File

@ -1,12 +1,13 @@
#include "catch_tag_alias_autoregistrar.h"
#include "catch_compiler_capabilities.h"
#include "catch_interfaces_registry_hub.h"
namespace Catch {
RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) {
try {
CATCH_TRY {
getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo);
} catch (...) {
} CATCH_CATCH_ALL {
// Do not throw when constructing global objects, instead register the exception to be processed later
getMutableRegistryHub().registerStartupException();
}

View File

@ -6,6 +6,7 @@
*/
#include "catch_test_registry.h"
#include "catch_compiler_capabilities.h"
#include "catch_test_case_registry_impl.h"
#include "catch_interfaces_registry_hub.h"
@ -18,7 +19,7 @@ namespace Catch {
NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {}
AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept {
try {
CATCH_TRY {
getMutableRegistryHub()
.registerTest(
makeTestCase(
@ -26,7 +27,7 @@ namespace Catch {
extractClassName( classOrMethod ),
nameAndTags,
lineInfo));
} catch (...) {
} CATCH_CATCH_ALL {
// Do not throw when constructing global objects, instead register the exception to be processed later
getMutableRegistryHub().registerStartupException();
}

View File

@ -173,6 +173,7 @@ set(IMPL_SOURCES
${HEADER_DIR}/internal/catch_debug_console.cpp
${HEADER_DIR}/internal/catch_debugger.cpp
${HEADER_DIR}/internal/catch_decomposer.cpp
${HEADER_DIR}/internal/catch_enforce.cpp
${HEADER_DIR}/internal/catch_errno_guard.cpp
${HEADER_DIR}/internal/catch_exception_translator_registry.cpp
${HEADER_DIR}/internal/catch_fatal_condition.cpp