Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMartin Hořeňovský2026-05-12 13:16:46 +0200
committerMartin Hořeňovský2026-05-12 13:16:46 +0200
commit6ee0826dcae55ed1e06b2c5701981221e979e1e6 (patch)
tree349f3b34c528391896928c8cf8e8109123d35c26
parent47ea57d74f8968eae242ea1b068a9e5b257c36b1 (diff)
downloadcatch3-3.15.0.tar.gz
v3.15.0v3.15.0
-rw-r--r--CMakeLists.txt2
-rw-r--r--docs/matchers.md2
-rw-r--r--docs/other-macros.md2
-rw-r--r--docs/release-notes.md17
-rw-r--r--extras/catch_amalgamated.cpp175
-rw-r--r--extras/catch_amalgamated.hpp440
-rw-r--r--meson.build2
-rw-r--r--src/catch2/catch_version.cpp2
-rw-r--r--src/catch2/catch_version_macros.hpp2
9 files changed, 356 insertions, 288 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7946bc52..e0b782f0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -35,7 +35,7 @@ if(CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif()
project(Catch2
- VERSION 3.14.0 # CML version placeholder, don't delete
+ VERSION 3.15.0 # CML version placeholder, don't delete
LANGUAGES CXX
HOMEPAGE_URL "https://github.com/catchorg/Catch2"
DESCRIPTION "A modern, C++-native, unit test framework."
diff --git a/docs/matchers.md b/docs/matchers.md
index a57dc6d3..fa176ced 100644
--- a/docs/matchers.md
+++ b/docs/matchers.md
@@ -474,7 +474,7 @@ and new style matchers arbitrarily.
## Constexpr matchers
-> Support for constexpr matchers was introduced in Catch2 vX.Y.Z
+> Support for constexpr matchers was introduced in Catch2 3.15.0
When compiled for C++20, the new-style matchers (can) support `constexpr`
matching, albeit not `constexpr` stringification. The matcher combinators
diff --git a/docs/other-macros.md b/docs/other-macros.md
index de7e4bc6..9faf5da2 100644
--- a/docs/other-macros.md
+++ b/docs/other-macros.md
@@ -93,7 +93,7 @@ TEST_CASE("STATIC_CHECK showcase", "[traits]") {
* `STATIC_REQUIRE_THAT` and `STATIC_CHECK_THAT`
-> `STATIC_REQUIRE_THAT` and `STATIC_CHECK_THAT` was introduced in Catch2 X.Y.Z
+> `STATIC_REQUIRE_THAT` and `STATIC_CHECK_THAT` was introduced in Catch2 3.15.0
`STATIC_{REQUIRE,CHECK}_THAT` are analogous to `STATIC_{REQUIRE,CHECK}`,
but for matchers. They are always defined, even if the current compiler
diff --git a/docs/release-notes.md b/docs/release-notes.md
index d2c4ce63..3df9e690 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -2,6 +2,7 @@
# Release notes
**Contents**<br>
+[3.15.0](#3150)<br>
[3.14.0](#3140)<br>
[3.13.0](#3130)<br>
[3.12.0](#3120)<br>
@@ -74,6 +75,22 @@
[Even Older versions](#even-older-versions)<br>
+## 3.15.0
+
+### Fixes
+* Fixed ambiguous overload issue with `std::optional<T>` in C++26. (#3095)
+ * Since `std::optional` was turned into a range, the partial specialization of `StringMaker` for range-like types and for `std::optional<T>` were in conflict.
+
+### Improvements
+* Simplified `CATCH_REGISTER_ENUM` internals for faster compilation and retrieval.
+* Successful assertion are slightly (1-2%) faster.
+* Generic (new-style) matchers support constexpr matching in C++20.
+ * Combining matchers requires C++26.
+ * Catch2-provided generic matchers are all constexpr enabled.
+* Added `STATIC_REQUIRE_THAT` for compile-time matcher assertions.
+ * This requires the compiler to support enough `constexpr` for matchers, see above.
+
+
## 3.14.0
### Fixes
diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp
index b0c5c7ca..e851b09d 100644
--- a/extras/catch_amalgamated.cpp
+++ b/extras/catch_amalgamated.cpp
@@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
-// Catch v3.14.0
-// Generated: 2026-04-05 15:03:01.631668
+// Catch v3.15.0
+// Generated: 2026-05-12 13:08:21.086523
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -74,11 +74,11 @@ namespace Catch {
samples.data(), samples.data() + samples.size() );
auto wrap_estimate = [](Estimate<double> e) {
- return Estimate<FDuration> {
- FDuration(e.point),
- FDuration(e.lower_bound),
- FDuration(e.upper_bound),
- e.confidence_interval,
+ return Estimate<FDuration>{
+ FDuration( e.point ),
+ FDuration( e.lower_bound ),
+ FDuration( e.upper_bound ),
+ e.confidence_interval,
};
};
std::vector<FDuration> samples2;
@@ -147,7 +147,7 @@ namespace Catch {
namespace Catch {
namespace Benchmark {
namespace Detail {
- struct optimized_away_error : std::exception {
+ struct optimized_away_error final : std::exception {
const char* what() const noexcept override;
};
@@ -995,6 +995,7 @@ namespace Catch {
+
#include <cassert>
#include <stack>
@@ -1007,7 +1008,7 @@ namespace Catch {
m_messageId( builder.m_info.sequence ) {
MessageInfo info( CATCH_MOVE( builder.m_info ) );
info.message = builder.m_stream.str();
- IResultCapture::pushScopedMessage( CATCH_MOVE( info ) );
+ Detail::pushScopedMessage( CATCH_MOVE( info ) );
}
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
@@ -1016,7 +1017,7 @@ namespace Catch {
}
ScopedMessage::~ScopedMessage() {
- if ( !m_moved ) { IResultCapture::popScopedMessage( m_messageId ); }
+ if ( !m_moved ) { Detail::popScopedMessage( m_messageId ); }
}
@@ -1088,7 +1089,7 @@ namespace Catch {
assert( m_captured == m_messages.size() );
if ( m_isScoped ) {
for ( auto const& message : m_messages ) {
- IResultCapture::popScopedMessage( message.sequence );
+ Detail::popScopedMessage( message.sequence );
}
}
}
@@ -1097,9 +1098,9 @@ namespace Catch {
assert( index < m_messages.size() );
m_messages[index].message += value;
if ( m_isScoped ) {
- IResultCapture::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
+ Detail::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
} else {
- IResultCapture::addUnscopedMessage( CATCH_MOVE( m_messages[index] ) );
+ Detail::addUnscopedMessage( CATCH_MOVE( m_messages[index] ) );
}
m_captured++;
}
@@ -1160,9 +1161,6 @@ namespace Catch {
CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
#endif
}
- IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
- return m_enumValuesRegistry;
- }
private:
TestRegistry m_testCaseRegistry;
@@ -1170,7 +1168,6 @@ namespace Catch {
ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
TagAliasRegistry m_tagAliasRegistry;
StartupExceptionRegistry m_exceptionRegistry;
- Detail::EnumValuesRegistry m_enumValuesRegistry;
};
}
@@ -2397,7 +2394,7 @@ namespace Catch {
}
Version const& libraryVersion() {
- static Version version( 3, 14, 0, "", 0 );
+ static Version version( 3, 15, 0, "", 0 );
return version;
}
@@ -2686,13 +2683,17 @@ namespace Catch {
namespace Catch {
+ void AssertionHandler::finishIncomplete() {
+ m_resultCapture.handleIncomplete( m_assertionInfo );
+ }
+
AssertionHandler::AssertionHandler
( StringRef macroName,
SourceLineInfo const& lineInfo,
StringRef capturedExpression,
ResultDisposition::Flags resultDisposition )
: m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
- m_resultCapture( getResultCapture() )
+ m_resultCapture( static_cast<RunContext&>(getResultCapture()) )
{
m_resultCapture.notifyAssertionStarted( m_assertionInfo );
}
@@ -4088,12 +4089,11 @@ namespace Catch {
+
#include <cassert>
namespace Catch {
- IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default;
-
namespace Detail {
namespace {
@@ -4119,9 +4119,7 @@ namespace Catch {
return parsed;
}
- EnumInfo::~EnumInfo() = default;
-
- StringRef EnumInfo::lookup( int value ) const {
+ StringRef EnumInfo::lookup( int64_t value ) const {
for( auto const& valueToName : m_values ) {
if( valueToName.first == value )
return valueToName.second;
@@ -4129,25 +4127,20 @@ namespace Catch {
return "{** unexpected enum value **}"_sr;
}
- Catch::Detail::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
- auto enumInfo = Catch::Detail::make_unique<EnumInfo>();
- enumInfo->m_name = enumName;
- enumInfo->m_values.reserve( values.size() );
+ EnumInfo makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int64_t> const& values ) {
+ EnumInfo enumInfo;
+ enumInfo.m_name = enumName;
+ enumInfo.m_values.reserve( values.size() );
const auto valueNames = Catch::Detail::parseEnums( allValueNames );
assert( valueNames.size() == values.size() );
- std::size_t i = 0;
- for( auto value : values )
- enumInfo->m_values.emplace_back(value, valueNames[i++]);
+ for (size_t i = 0; i < values.size(); ++i) {
+ enumInfo.m_values.emplace_back( values[i], valueNames[i] );
+ }
return enumInfo;
}
- EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
- m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values));
- return *m_enumInfos.back();
- }
-
} // Detail
} // Catch
@@ -5072,7 +5065,7 @@ namespace Catch {
namespace {
//! A no-op implementation, used if no reporter wants output
//! redirection.
- class NoopRedirect : public OutputRedirect {
+ class NoopRedirect final : public OutputRedirect {
void activateImpl() override {}
void deactivateImpl() override {}
std::string getStdout() override { return {}; }
@@ -5109,7 +5102,7 @@ namespace Catch {
* Redirects the `std::cout`, `std::cerr`, `std::clog` streams,
* but does not touch the actual `stdout`/`stderr` file descriptors.
*/
- class StreamRedirect : public OutputRedirect {
+ class StreamRedirect final : public OutputRedirect {
ReusableStringStream m_redirectedOut, m_redirectedErr;
RedirectedStreamNew m_cout, m_cerr, m_clog;
@@ -5220,7 +5213,7 @@ namespace Catch {
* Works by replacing the file descriptors numbered 1 and 2
* with an open temporary file.
*/
- class FileRedirect : public OutputRedirect {
+ class FileRedirect final : public OutputRedirect {
TempFile m_outFile, m_errFile;
int m_originalOut = -1;
int m_originalErr = -1;
@@ -6136,6 +6129,25 @@ namespace Catch {
}
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+ void pushScopedMessage( MessageInfo&& message ) {
+ Detail::g_messageHolder().addScopedMessage( CATCH_MOVE( message ) );
+ }
+
+ void popScopedMessage( unsigned int messageId ) {
+ Detail::g_messageHolder().removeMessage( messageId );
+ }
+
+ void emplaceUnscopedMessage( MessageBuilder&& builder ) {
+ Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( builder ) );
+ }
+
+ void addUnscopedMessage( MessageInfo&& message ) {
+ Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( message ) );
+ }
+
+ bool lastAssertionPassed() { return Detail::g_lastAssertionPassed; }
+
} // namespace Detail
RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter)
@@ -6269,14 +6281,13 @@ namespace Catch {
Detail::g_lastAssertionPassed = true;
} else if (!result.succeeded()) {
Detail::g_lastAssertionPassed = false;
- if (result.isOk()) {
- }
- else if( m_activeTestCase->getTestCaseInfo().okToFail() ) // Read from a shared state established before the threads could start, this is fine
+ if (result.isOk()) {}
+ else if( m_activeTestCase->getTestCaseInfo().okToFail() ) { // Read from a shared state established before the threads could start, this is fine
m_atomicAssertionCount.failedButOk++;
- else
+ } else {
m_atomicAssertionCount.failed++;
- }
- else {
+ }
+ } else {
Detail::g_lastAssertionPassed = true;
}
@@ -6458,7 +6469,7 @@ namespace Catch {
// and since IResultCapture::getLastResult is deprecated,
// we will leave it as is, until it is finally removed.
Detail::LockGuard _( m_assertionMutex );
- return &(*m_lastResult);
+ return &*m_lastResult;
}
void RunContext::exceptionEarlyReported() {
@@ -6532,10 +6543,6 @@ namespace Catch {
m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false));
}
- bool RunContext::lastAssertionPassed() {
- return Detail::g_lastAssertionPassed;
- }
-
void RunContext::assertionPassedFastPath(SourceLineInfo lineInfo) {
// We want to save the line info for better experience with unexpected assertions
Detail::g_lastKnownLineInfo = lineInfo;
@@ -6714,7 +6721,7 @@ namespace Catch {
}
void RunContext::populateReaction( AssertionReaction& reaction,
- bool has_normal_disposition ) {
+ bool has_normal_disposition ) const {
reaction.shouldDebugBreak = m_shouldDebugBreak;
reaction.shouldThrow = aborting() || has_normal_disposition;
}
@@ -6765,22 +6772,6 @@ namespace Catch {
}
}
- void IResultCapture::pushScopedMessage( MessageInfo&& message ) {
- Detail::g_messageHolder().addScopedMessage( CATCH_MOVE( message ) );
- }
-
- void IResultCapture::popScopedMessage( unsigned int messageId ) {
- Detail::g_messageHolder().removeMessage( messageId );
- }
-
- void IResultCapture::emplaceUnscopedMessage( MessageBuilder&& builder ) {
- Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( builder ) );
- }
-
- void IResultCapture::addUnscopedMessage( MessageInfo&& message ) {
- Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( message ) );
- }
-
void seedRng(IConfig const& config) {
sharedRng().seed(config.rngSeed());
}
@@ -7112,10 +7103,11 @@ namespace Catch {
TagAlias const* TagAliasRegistry::find( std::string const& alias ) const {
auto it = m_registry.find( alias );
- if( it != m_registry.end() )
- return &(it->second);
- else
+ if ( it != m_registry.end() ) {
+ return &it->second;
+ } else {
return nullptr;
+ }
}
std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
@@ -7562,7 +7554,7 @@ namespace Catch {
void throw_test_failure_exception() {
#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS )
- throw TestFailureException{};
+ throw TestFailureException{}; //NOLINT(bugprone-std-exception-baseclass)
#else
CATCH_ERROR( "Test failure requires aborting test!" );
#endif
@@ -7570,7 +7562,7 @@ namespace Catch {
void throw_test_skip_exception() {
#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS )
- throw Catch::TestSkipException();
+ throw Catch::TestSkipException(); //NOLINT(bugprone-std-exception-baseclass)
#else
CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" );
#endif
@@ -8674,13 +8666,13 @@ namespace Catch {
namespace Matchers {
std::string MatcherUntypedBase::toString() const {
- if (m_cachedToString.empty()) {
- m_cachedToString = describe();
- }
- return m_cachedToString;
+ return describe();
}
- MatcherUntypedBase::~MatcherUntypedBase() = default;
+ std::string MatcherUntypedBase::describe() const {
+ using namespace std::string_literals;
+ return "Undescribed matcher"s;
+ }
} // namespace Matchers
} // namespace Catch
@@ -8701,14 +8693,6 @@ namespace Matchers {
return sstr.str();
}
- IsEmptyMatcher IsEmpty() {
- return {};
- }
-
- HasSizeMatcher SizeIs(std::size_t sz) {
- return HasSizeMatcher{ sz };
- }
-
} // end namespace Matchers
} // end namespace Catch
@@ -8962,17 +8946,13 @@ std::string Catch::Matchers::Detail::finalizeDescription(const std::string& desc
namespace Catch {
namespace Matchers {
- std::string AllTrueMatcher::describe() const { return "contains only true"; }
- AllTrueMatcher AllTrue() { return AllTrueMatcher{}; }
+ std::string AllTrueMatcher::describe() const { return "contains only true"; }
std::string NoneTrueMatcher::describe() const { return "contains no true"; }
- NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; }
-
std::string AnyTrueMatcher::describe() const { return "contains at least one true"; }
- AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; }
} // namespace Matchers
} // namespace Catch
@@ -9085,7 +9065,11 @@ namespace Matchers {
namespace Catch {
namespace Matchers {
- MatcherGenericBase::~MatcherGenericBase() = default;
+
+ std::string MatcherGenericBase::describe() const {
+ using namespace std::string_literals;
+ return "Undescribed generic matcher"s;
+ }
namespace Detail {
@@ -10108,9 +10092,8 @@ namespace Catch {
bool operator()(
Detail::unique_ptr<CumulativeReporterBase::SectionNode> const&
node ) const {
- return (
- ( node->stats.sectionInfo.name == m_other.name ) &&
- ( node->stats.sectionInfo.lineInfo == m_other.lineInfo ) );
+ return node->stats.sectionInfo.name == m_other.name
+ && node->stats.sectionInfo.lineInfo == m_other.lineInfo;
}
void operator=( BySectionInfo const& ) = delete;
@@ -10464,7 +10447,7 @@ namespace Catch {
}
// minimum whitespace to pad tag counts, possibly overwritten below
- size_t maxTagCountLen = 2;
+ int maxTagCountLen = 2;
// determine necessary padding for tag count column
if ( ! tags.empty() ) {
@@ -10479,7 +10462,7 @@ namespace Catch {
// more padding necessary for 3+ digits
if (maxTagCount >= 100) {
auto numDigits = 1 + std::floor( std::log10( maxTagCount ) );
- maxTagCountLen = static_cast<size_t>( numDigits );
+ maxTagCountLen = static_cast<int>( numDigits );
}
}
diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp
index ba6cf8ff..40fbf485 100644
--- a/extras/catch_amalgamated.hpp
+++ b/extras/catch_amalgamated.hpp
@@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
-// Catch v3.14.0
-// Generated: 2026-04-05 15:03:01.150393
+// Catch v3.15.0
+// Generated: 2026-05-12 13:08:20.543985
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -118,16 +118,24 @@
#endif // CATCH_PLATFORM_HPP_INCLUDED
-#ifdef __cplusplus
-
-# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
-# define CATCH_CPP17_OR_GREATER
-# endif
+#if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+# define CATCH_CPP17_OR_GREATER
+#endif
-# if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
-# define CATCH_CPP20_OR_GREATER
-# endif
+#if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
+# define CATCH_CPP20_OR_GREATER
+#endif
+// Matchers are only constexpr-able in C++20
+#if defined( CATCH_CPP20_OR_GREATER ) && \
+ defined( __cpp_constexpr_dynamic_alloc ) && \
+ __cpp_constexpr_dynamic_alloc >= 201907L && \
+ /* GCC < 13 define the feature macro, but compiler bugs stop us from using it */ \
+ ( !defined( __GNUC__ ) || __GNUC__ >= 13 || defined(__clang__) )
+# define CATCH_INTERNAL_CONSTEXPR_MATCHERS_ENABLED
+# define CATCH_DESTRUCTOR_CONSTEXPR constexpr
+#else
+# define CATCH_DESTRUCTOR_CONSTEXPR
#endif
// Only GCC compiler should be used in this block, so other compilers trying to
@@ -1053,8 +1061,6 @@ namespace Catch {
struct AssertionInfo;
struct SectionInfo;
struct SectionEndInfo;
- struct MessageInfo;
- struct MessageBuilder;
struct Counts;
struct AssertionReaction;
struct SourceLineInfo;
@@ -1094,11 +1100,6 @@ namespace Catch {
virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
virtual void benchmarkFailed( StringRef error ) = 0;
- static void pushScopedMessage( MessageInfo&& message );
- static void popScopedMessage( unsigned int messageId );
- static void addUnscopedMessage( MessageInfo&& message );
- static void emplaceUnscopedMessage( MessageBuilder&& builder );
-
virtual void handleFatalErrorCondition( StringRef message ) = 0;
virtual void handleExpr
@@ -1124,9 +1125,6 @@ namespace Catch {
ResultWas::OfType resultType,
AssertionReaction &reaction ) = 0;
-
- virtual bool lastAssertionPassed() = 0;
-
// Deprecated, do not use:
virtual std::string getCurrentTestName() const = 0;
virtual const AssertionResult* getLastResult() const = 0;
@@ -1231,7 +1229,7 @@ namespace Catch {
class IStream;
struct PathFilter;
- class IConfig : public Detail::NonCopyable {
+ class IConfig : Detail::NonCopyable {
public:
virtual ~IConfig();
@@ -1319,7 +1317,6 @@ namespace Catch {
virtual void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) = 0;
virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
virtual void registerStartupException() noexcept = 0;
- virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;
};
IRegistryHub const& getRegistryHub();
@@ -1681,7 +1678,7 @@ namespace Catch {
callable& operator=(callable&&) = default;
};
template <typename Fun>
- struct model : public callable {
+ struct model final : public callable {
model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {}
model(Fun const& fun_) : fun(fun_) {}
@@ -2469,45 +2466,46 @@ namespace Catch {
#endif // CATCH_VOID_TYPE_HPP_INCLUDED
-#ifndef CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
-#define CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
+#ifndef CATCH_ENUM_INFO_HPP_INCLUDED
+#define CATCH_ENUM_INFO_HPP_INCLUDED
+#include <cstdint>
+#include <utility>
#include <vector>
namespace Catch {
-
namespace Detail {
+
struct EnumInfo {
StringRef m_name;
- std::vector<std::pair<int, StringRef>> m_values;
+ std::vector<std::pair<int64_t, StringRef>> m_values;
- ~EnumInfo();
-
- StringRef lookup( int value ) const;
+ StringRef lookup( int64_t value ) const;
};
- } // namespace Detail
-
- class IMutableEnumValuesRegistry {
- public:
- virtual ~IMutableEnumValuesRegistry(); // = default;
-
- virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
- template<typename E>
- Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
- static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
- std::vector<int> intValues;
+ EnumInfo makeEnumInfo( StringRef enumName,
+ StringRef allValueNames,
+ std::vector<int64_t> const& values );
+ template <typename E>
+ Detail::EnumInfo makeEnumInfo( StringRef enumName,
+ StringRef allEnums,
+ std::initializer_list<E> values ) {
+ static_assert( sizeof( int64_t ) >= sizeof( E ),
+ "Cannot serialize enum to int64_t" );
+ std::vector<int64_t> intValues;
intValues.reserve( values.size() );
- for( auto enumValue : values )
- intValues.push_back( static_cast<int>( enumValue ) );
- return registerEnum( enumName, allEnums, intValues );
+ for ( auto enumValue : values )
+ intValues.push_back( static_cast<int64_t>( enumValue ) );
+ return makeEnumInfo( enumName, allEnums, intValues );
}
- };
-} // Catch
+ std::vector<StringRef> parseEnums( StringRef enums );
+
+ } // namespace Detail
+} // namespace Catch
-#endif // CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
+#endif // CATCH_ENUM_INFO_HPP_INCLUDED
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
#include <string_view>
@@ -2872,7 +2870,10 @@ namespace Catch {
}
#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
-#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL)
+#if defined( CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER ) && \
+ defined( CATCH_CONFIG_CPP17_OPTIONAL ) && \
+ /* P3168 turned optional into a range, making this ambigous with the range support */ \
+ !defined( __cpp_lib_optional_range_support )
#include <optional>
namespace Catch {
template<typename T>
@@ -3119,16 +3120,21 @@ struct ratio_string<std::milli> {
};
}
-
-#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
-namespace Catch { \
- template<> struct StringMaker<enumName> { \
- static std::string convert( enumName value ) { \
- static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
- return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
- } \
- }; \
-}
+#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
+ namespace Catch { \
+ template <> \
+ struct StringMaker<enumName> { \
+ static std::string convert( enumName value ) { \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ static const auto enumInfo = ::Catch::Detail::makeEnumInfo( \
+ #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ return static_cast<std::string>( \
+ enumInfo.lookup( static_cast<int64_t>( value ) ) ); \
+ } \
+ }; \
+ }
#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
@@ -4049,9 +4055,21 @@ namespace Catch {
#include <vector>
namespace Catch {
+ struct MessageInfo;
+ struct MessageBuilder;
+
+ namespace Detail {
+ // The message state affecting functions have to be defined in
+ // the TU where the thread-local message holders are defined.
+ // Currently this is catch_run_context.cpp
+
+ void pushScopedMessage( MessageInfo&& message );
+ void popScopedMessage( unsigned int messageId );
+ void addUnscopedMessage( MessageInfo&& message );
+ void emplaceUnscopedMessage( MessageBuilder&& builder );
+ } // namespace Detail
struct SourceLineInfo;
- class IResultCapture;
struct MessageStream {
@@ -4141,7 +4159,7 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
- Catch::IResultCapture::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
+ Catch::Detail::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
@@ -5430,7 +5448,7 @@ namespace Catch {
void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
template<typename LhsT, typename RhsT>
- class BinaryExpr : public ITransientExpression {
+ class BinaryExpr final : public ITransientExpression {
LhsT m_lhs;
StringRef m_op;
RhsT m_rhs;
@@ -5506,7 +5524,7 @@ namespace Catch {
};
template<typename LhsT>
- class UnaryExpr : public ITransientExpression {
+ class UnaryExpr final : public ITransientExpression {
LhsT m_lhs;
void streamReconstructedExpression( std::ostream &os ) const override {
@@ -5707,6 +5725,8 @@ namespace Catch {
namespace Catch {
+ class RunContext;
+
struct AssertionReaction {
bool shouldDebugBreak = false;
bool shouldThrow = false;
@@ -5717,7 +5737,12 @@ namespace Catch {
AssertionInfo m_assertionInfo;
AssertionReaction m_reaction;
bool m_completed = false;
- IResultCapture& m_resultCapture;
+ // Since all uses are hidden in the .cpp file, we can directly use
+ // the final type and avoid going through virtual dispatch, without
+ // massive compilation time overhead.
+ RunContext& m_resultCapture;
+
+ void finishIncomplete();
public:
AssertionHandler
@@ -5726,9 +5751,9 @@ namespace Catch {
StringRef capturedExpression,
ResultDisposition::Flags resultDisposition );
~AssertionHandler() {
- if ( !m_completed ) {
- m_resultCapture.handleIncomplete( m_assertionInfo );
- }
+ // We want the common fast path inlinable, and the virtual
+ // dispatch in a function in single TU.
+ if ( !m_completed ) { finishIncomplete(); }
}
@@ -5771,6 +5796,13 @@ namespace Catch {
#endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED
+namespace Catch {
+ namespace Detail {
+ // Defined in catch_run_context.cpp, where the thread-local data lives.
+ bool lastAssertionPassed();
+ }
+}
+
// We need this suppression to leak, because it took until GCC 10
// for the front end to handle local suppression via _Pragma properly
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ <= 9
@@ -5813,12 +5845,12 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \
INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
- if( Catch::getResultCapture().lastAssertionPassed() )
+ if( Catch::Detail::lastAssertionPassed() )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \
INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
- if( !Catch::getResultCapture().lastAssertionPassed() )
+ if( !Catch::Detail::lastAssertionPassed() )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \
@@ -6101,7 +6133,7 @@ namespace Catch {
namespace Catch {
template<typename C>
-class TestInvokerAsMethod : public ITestInvoker {
+class TestInvokerAsMethod final : public ITestInvoker {
void (C::*m_testAsMethod)();
public:
constexpr TestInvokerAsMethod( void ( C::*testAsMethod )() ) noexcept:
@@ -6121,7 +6153,7 @@ Detail::unique_ptr<ITestInvoker> makeTestInvoker( void (C::*testAsMethod)() ) {
}
template <typename C>
-class TestInvokerFixture : public ITestInvoker {
+class TestInvokerFixture final : public