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

summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLeitwolf2026-10-31 17:18:21 +0300
committerGitHub2026-10-31 17:18:21 +0300
commit326ea0c0213aeecb6b984bba3dabb0ebad97679e (patch)
tree5cec698f0bc0b8a60f45e866fb40f2fd8a358bd1
parent6a42cdf6461b2577106ef094d6f98025554b6077 (diff)
parentafe91797e9d50cd4712d3694a2e5fedfc0b966b1 (diff)
downloadcatch3-326ea0c0213aeecb6b984bba3dabb0ebad97679e.tar.gz
Merge pull request #22 from Progmasoft/build/cpp17
Build Catch3 as C++17
-rw-r--r--.bazelrc8
-rw-r--r--.clang-format2
-rw-r--r--.github/workflows/linux-builds.yml4
-rw-r--r--.github/workflows/windows-simple-builds.yml2
-rw-r--r--CMake/catch3.pc.in2
-rw-r--r--README.md8
-rw-r--r--docs/Readme.md2
-rw-r--r--docs/cmake-integration.md4
-rw-r--r--docs/contributing.md4
-rw-r--r--docs/faq.md2
-rw-r--r--docs/progmasoft-catch3.md6
-rw-r--r--docs/progmasoft-properties.md2
-rw-r--r--docs/why-catch3.md4
-rw-r--r--extras/catch_amalgamated.cpp48
-rw-r--r--extras/catch_amalgamated.hpp4
-rw-r--r--extras/catch_amalgamated_third_party.hpp2
-rw-r--r--fuzzing/CMakeLists.txt2
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/Progmasoft/BUILD.bazel15
-rw-r--r--src/Progmasoft/Catch3/Detail/TypeTraits.hpp47
-rw-r--r--src/Progmasoft/Catch3/ForAll.hpp38
-rw-r--r--src/Progmasoft/Catch3/Generators/Element.hpp12
-rw-r--r--src/Progmasoft/Catch3/Generators/Integer.hpp12
-rw-r--r--src/Progmasoft/Catch3/Generators/Tuple.hpp3
-rw-r--r--src/Progmasoft/Catch3/Property.hpp15
-rw-r--r--src/Progmasoft/Catch3/Random.hpp43
-rw-r--r--src/Progmasoft/Catch3/Strategy/ChoiceSource.hpp14
-rw-r--r--src/Progmasoft/Catch3/Strategy/Containers.hpp4
-rw-r--r--src/Progmasoft/Catch3/Strategy/Engine.hpp7
-rw-r--r--src/Progmasoft/Catch3/Strategy/Numeric.hpp14
-rw-r--r--src/Progmasoft/Catch3/Strategy/Strategy.hpp27
-rw-r--r--src/Progmasoft/Catch3/meson.build2
-rw-r--r--src/Progmasoft/ChoiceShrinker.cpp8
-rw-r--r--src/Progmasoft/PropertyEngine.cpp16
-rw-r--r--src/Progmasoft/Runner/Process.cpp44
-rw-r--r--src/Progmasoft/meson.build1
-rw-r--r--tests/CMakeLists.txt4
-rw-r--r--tests/ExtraTests/CMakeLists.txt2
-rw-r--r--tests/Progmasoft/BUILD.bazel8
-rw-r--r--tests/Progmasoft/Generators.tests.cpp76
-rw-r--r--tests/Progmasoft/Property.tests.cpp66
-rw-r--r--tests/Progmasoft/Results.tests.cpp2
-rw-r--r--tests/Progmasoft/Snapshot.tests.cpp77
-rw-r--r--tests/Progmasoft/Strategy.tests.cpp34
-rw-r--r--tests/meson.build4
-rwxr-xr-xtools/scripts/generateAmalgamatedFiles.py2
46 files changed, 444 insertions, 264 deletions
diff --git a/.bazelrc b/.bazelrc
index b10a64f5..30415bed 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -1,12 +1,12 @@
build --enable_platform_specific_config
-build:gcc9 --cxxopt=-std=c++2a
-build:gcc11 --cxxopt=-std=c++2a
-build:gcc13 --cxxopt=-std=c++2a
+build:gcc9 --cxxopt=-std=c++17
+build:gcc11 --cxxopt=-std=c++17
+build:gcc13 --cxxopt=-std=c++17
build:clang13 --cxxopt=-std=c++17
build:vs2019 --cxxopt=/std:c++17
build:vs2022 --cxxopt=/std:c++17
build:windows --config=vs2022
build:linux --config=gcc11
-build:macos --cxxopt=-std=c++2b
+build:macos --cxxopt=-std=c++17
diff --git a/.clang-format b/.clang-format
index 05a21827..d6dcd25c 100644
--- a/.clang-format
+++ b/.clang-format
@@ -7,7 +7,7 @@
# PascalCase/camelCase conventions of Progmasoft C++ Style.
Language: Cpp
BasedOnStyle: LLVM
-Standard: c++20
+Standard: c++17
UseTab: Never
IndentWidth: 4
diff --git a/.github/workflows/linux-builds.yml b/.github/workflows/linux-builds.yml
index 8597f658..5290574b 100644
--- a/.github/workflows/linux-builds.yml
+++ b/.github/workflows/linux-builds.yml
@@ -25,7 +25,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Debug, Release]
- std: [14, 17]
+ std: [17, 20]
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
@@ -93,7 +93,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Debug, Release]
- std: [14, 17]
+ std: [17, 20]
steps:
# Git comes first: without it the checkout falls back to an archive of
diff --git a/.github/workflows/windows-simple-builds.yml b/.github/workflows/windows-simple-builds.yml
index 164ff839..3de156c6 100644
--- a/.github/workflows/windows-simple-builds.yml
+++ b/.github/workflows/windows-simple-builds.yml
@@ -22,7 +22,7 @@ jobs:
os: [windows-2025-vs2026]
platform: [Win32, x64]
build_type: [Debug, Release]
- std: [14, 17]
+ std: [17, 20]
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
diff --git a/CMake/catch3.pc.in b/CMake/catch3.pc.in
index 60f81e7f..0e63b475 100644
--- a/CMake/catch3.pc.in
+++ b/CMake/catch3.pc.in
@@ -4,7 +4,7 @@ includedir=${prefix}/@include_dir@
libdir=${prefix}/@lib_dir@
Name: Catch3
-Description: A C++ test framework with a Catch2-compatible API; headers need C++14 and above
+Description: A C++ test framework with a Catch2-compatible API; headers need C++17 and above
URL: https://github.com/Progmasoft/catch3
Version: @Catch3_VERSION@
Cflags: -I${includedir}
diff --git a/README.md b/README.md
index bacde00d..14ee4bdf 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
An independently maintained C++ test framework: Catch3 keeps the practical,
expressive test-writing model that developers expect, while adding a separately
-owned C++20 library for property checks, snapshots, structured results, and XML
+owned C++17 library for property checks, snapshots, structured results, and XML
serialization.
> Catch3 is not an official Catch2 release and is not endorsed by Catch2's
@@ -33,7 +33,7 @@ serialization.
- **Structured results and XML output.** The runner's result model, with
every attempt, and its deterministic JUnit XML writer are public.
- **A compiled library, not a header-only package.** Snapshot I/O and result/XML
- serialization are compiled C++20 implementations; templates and assertion
+ serialization are compiled C++17 implementations; templates and assertion
macros remain in headers where needed.
Catch3 is in an early development stage. Catch2's command line and reporters
@@ -87,8 +87,8 @@ target_link_libraries(MyTests PRIVATE Catch3::Catch3WithMain)
`Catch3::Catch3` is the library: the engine, the runner, property testing and
snapshots; `Catch3::Catch3WithMain` adds the default `main`. Tests include
`<Progmasoft/Catch3/TestMacros.hpp>` and the other `Progmasoft/Catch3/`
-headers. The library is built as C++20; code that includes only the engine's
-headers needs C++14. Catch3 and Catch2 can be installed side by side, as
+headers. The library is C++17, and its headers work in C++17
+and later. Catch3 and Catch2 can be installed side by side, as
their package, target, header and pkg-config names differ.
## Documentation
diff --git a/docs/Readme.md b/docs/Readme.md
index 18bdf927..3da1a8ca 100644
--- a/docs/Readme.md
+++ b/docs/Readme.md
@@ -2,7 +2,7 @@
# Catch3 documentation
Catch3 keeps Catch2's API for writing tests, runs them with
-[its own runner](progmasoft-runner.md#top) and adds Progmasoft's C++20
+[its own runner](progmasoft-runner.md#top) and adds Progmasoft's C++17
extensions. Start with the [tutorial](tutorial.md#top), then read
[the runner](progmasoft-runner.md#top), [what Catch3 adds](progmasoft-catch3.md),
[native property testing](progmasoft-properties.md), [the roadmap](progmasoft-roadmap.md) and
diff --git a/docs/cmake-integration.md b/docs/cmake-integration.md
index 01c9905a..c6e3771c 100644
--- a/docs/cmake-integration.md
+++ b/docs/cmake-integration.md
@@ -16,8 +16,8 @@
Catch3's CMake package is `Catch3`. It exports two targets:
1. `Catch3::Catch3`, the library: the Catch2-compatible engine, the Catch3
- runner, property testing and snapshots. It is built as C++20; code that
- includes only the engine's headers needs C++14.
+ runner, property testing and snapshots. It is C++17, and its headers
+ work in C++17 and later.
2. `Catch3::Catch3WithMain`, the library with the default `main`.
`Catch3.cmake` registers each `TEST_CASE` with CTest.
diff --git a/docs/contributing.md b/docs/contributing.md
index 05c1f415..82e82675 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -62,13 +62,13 @@ distribution with `python tools/scripts/generateAmalgamatedFiles.py`.
what Catch3 promises to keep: code that compiles against Catch2's headers
must keep compiling. It stays under the Boost Software License 1.0, keeps the
`Catch2 Authors` header and follows the style of the surrounding Catch2 code.
-Code that includes only its headers needs C++14.
+Code that includes its headers needs C++17 or later.
`src/Progmasoft` holds what Progmasoft adds: the runner, the results model,
property testing and snapshots. It is licensed `MPL-2.0 WITH
AdditionRef-Progmasoft-Exception-1.1`, follows the
[Progmasoft C++ Style](https://github.com/Progmasoft/progmasoft-cpp), is
-C++20, reports recoverable errors with `Expected` rather than exceptions, and
+C++17, reports recoverable errors with `Expected` rather than exceptions, and
is formatted with the `.clang-format` beside it.
`tools/scripts/checkLicense.py` checks that every source file carries the
diff --git a/docs/faq.md b/docs/faq.md
index 1ae93bca..63ada4e2 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -41,7 +41,7 @@ as needed later.
The bundled Catch2-compatible engine provides no ABI stability guarantee.
Its C++ interfaces should be compiled with a consistent compiler and build
-configuration. The Progmasoft C++20 extension library does not currently
+configuration. The Progmasoft C++17 extension library does not currently
promise a stable binary ABI either.
Catch2 is not designed to be distributed as dynamic library, and you
diff --git a/docs/progmasoft-catch3.md b/docs/progmasoft-catch3.md
index 7dc09822..49754af1 100644
--- a/docs/progmasoft-catch3.md
+++ b/docs/progmasoft-catch3.md
@@ -170,8 +170,8 @@ call-site expansion require it.
## Build and integration
The CMake package is `Catch3`, with the targets `Catch3::Catch3` and
-`Catch3::Catch3WithMain`. The library is built as C++20; code that includes
-only the engine's headers still needs only C++14. After installation,
+`Catch3::Catch3WithMain`. The library is C++17, and its headers work in C++17
+and later. After installation,
consumers can use:
```cmake
@@ -186,7 +186,7 @@ ways to build the same engine and property tests. Their root files are
`CMakeLists.txt`, `meson.build`, `xmake.lua`, and `premake5.lua`. Xmake and
Premake materialize the default generated configuration header,
`Progmasoft/Catch3/UserConfig.hpp`, from the checked-in template. Meson 1.1.0 or newer is required for the Clang-CL
-C++20 test target.
+C++17 test target.
For project history, compatibility intent, license split, and current feature
limits, read [Why Progmasoft Catch3?](why-catch3.md).
diff --git a/docs/progmasoft-properties.md b/docs/progmasoft-properties.md
index e4302383..0b13ad60 100644
--- a/docs/progmasoft-properties.md
+++ b/docs/progmasoft-properties.md
@@ -224,6 +224,6 @@ A mismatch reports both results as notes.
## Compatibility
-The engine is C++20 and part of the `Catch3::Catch3` library. It does not
+The engine is C++17 and part of the `Catch3::Catch3` library. It does not
change the `Catch` namespace, and it needs no exceptions: a client built without them gets the
same results, except that an exception thrown by a body cannot be caught.
diff --git a/docs/why-catch3.md b/docs/why-catch3.md
index 4d29c4ff..120295f0 100644
--- a/docs/why-catch3.md
+++ b/docs/why-catch3.md
@@ -17,7 +17,7 @@ Catch2's runner with [its own](progmasoft-runner.md#top).
| --- | --- | --- |
| Test execution | Catch2's test registration, `RunContext` and listeners | The Catch3 runner: parallel worker processes, retries, flaky-test detection, quarantine, history, selection, sharding and its own command line and reports; `CATCH3_CHECK_FOR_ALL` and `CATCH3_CHECK_PROPERTY` report property outcomes through Catch2's assertion path |
| Public include path | The engine's headers moved to `Progmasoft/Catch3/...` with Progmasoft names, such as `TestMacros.hpp` | `Progmasoft/Catch3.hpp` and the other `Progmasoft/Catch3/...` headers |
-| C++ baseline | Code that includes the engine's headers needs C++14; the library, which holds the runner, is built as C++20 | The added property API requires C++20 |
+| C++ baseline | C++17; the headers work in C++17 and later | The same |
| Randomness | Catch2's existing behavior is unchanged | A specified SplitMix64 sequence provides deterministic trial seeds and unbiased bounded integer sampling |
| Generated values | No change to existing Catch2 generators | Strategies for numbers, text, collections and structures with `Map`, `Filter` and `FlatMap`; shrinking of recorded choices, so every combination shrinks; replay tokens and an example database; state machines, `Classify`/`Cover` statistics, targeted search, and differential and metamorphic oracles |
| Build identity | One library: the `Catch3` CMake package with `Catch3::Catch3` and `Catch3::Catch3WithMain`, `//:catch3` in Bazel, `catch3` in Meson and pkg-config | The same library |
@@ -41,7 +41,7 @@ continuity with the included Catch2 3.16.1 base; this is not a promise that ever
future Catch2 upstream change will be adopted unchanged or that all downstream
projects have already been validated.
-The engine, the runner and the Progmasoft additions are one compiled C++20
+The engine, the runner and the Progmasoft additions are one compiled C++17
library. Template-based generators and call-site assertion macros remain in
headers; snapshot I/O, result aggregation, and XML serialization are compiled
implementations.
diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp
index fc87bf40..3b15cc2d 100644
--- a/extras/catch_amalgamated.cpp
+++ b/extras/catch_amalgamated.cpp
@@ -7,14 +7,14 @@
// SPDX-License-Identifier: BSL-1.0
// Catch3 v0.3.0, on the Catch2 v3.16.1 engine
-// Generated: 2026-10-29 19:30:41.305371
+// Generated: 2026-10-31 13:33:37.309963
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
//
// The parts that come from src/Progmasoft, the Catch3 runner, keep
// their own copyright and license lines (MPL-2.0 WITH
-// AdditionRef-Progmasoft-Exception-1.1). The runner needs C++20.
+// AdditionRef-Progmasoft-Exception-1.1). The runner needs C++17.
// ----------------------------------------------------------
#include "catch_amalgamated.hpp"
@@ -14663,18 +14663,22 @@ namespace Progmasoft::Catch3::Runner
for (const auto &[name, value] : additions)
{
const std::wstring wideName = Widen(name);
- std::erase_if(entries, [&](const std::wstring &entry) {
- // A leading '=' marks the hidden per-drive variables.
- const auto equals = entry.find(L'=', 1);
- return equals == wideName.size()
- && CompareStringOrdinal(
- entry.data(),
- static_cast<int>(equals),
- wideName.data(),
- static_cast<int>(wideName.size()),
- TRUE)
- == CSTR_EQUAL;
- });
+ const auto removed = std::remove_if(
+ entries.begin(),
+ entries.end(),
+ [&](const std::wstring &entry) {
+ // A leading '=' marks the hidden per-drive variables.
+ const auto equals = entry.find(L'=', 1);
+ return equals == wideName.size()
+ && CompareStringOrdinal(
+ entry.data(),
+ static_cast<int>(equals),
+ wideName.data(),
+ static_cast<int>(wideName.size()),
+ TRUE)
+ == CSTR_EQUAL;
+ });
+ entries.erase(removed, entries.end());
entries.push_back(wideName + L"=" + Widen(value));
}
std::wstring block;
@@ -15429,11 +15433,17 @@ namespace Progmasoft::Catch3::Runner
}
for (const auto &[name, value] : options.environment)
{
- std::erase_if(environmentStorage, [&](const std::string &entry) {
- return entry.size() > name.size()
- && entry.compare(0, name.size(), name) == 0
- && entry[name.size()] == '=';
- });
+ // A lambda cannot capture a structured binding in C++17.
+ const std::string &variable = name;
+ const auto removed = std::remove_if(
+ environmentStorage.begin(),
+ environmentStorage.end(),
+ [&](const std::string &entry) {
+ return entry.size() > variable.size()
+ && entry.compare(0, variable.size(), variable) == 0
+ && entry[variable.size()] == '=';
+ });
+ environmentStorage.erase(removed, environmentStorage.end());
environmentStorage.push_back(name + "=" + value);
}
std::vector<char *> environment;
diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp
index 09e99fb2..18d52356 100644
--- a/extras/catch_amalgamated.hpp
+++ b/extras/catch_amalgamated.hpp
@@ -7,14 +7,14 @@
// SPDX-License-Identifier: BSL-1.0
// Catch3 v0.3.0, on the Catch2 v3.16.1 engine
-// Generated: 2026-10-29 19:30:41.164140
+// Generated: 2026-10-31 13:33:37.220554
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
//
// The parts that come from src/Progmasoft, the Catch3 runner, keep
// their own copyright and license lines (MPL-2.0 WITH
-// AdditionRef-Progmasoft-Exception-1.1). The runner needs C++20.
+// AdditionRef-Progmasoft-Exception-1.1). The runner needs C++17.
// ----------------------------------------------------------
#ifndef CATCH_AMALGAMATED_HPP_INCLUDED
#define CATCH_AMALGAMATED_HPP_INCLUDED
diff --git a/extras/catch_amalgamated_third_party.hpp b/extras/catch_amalgamated_third_party.hpp
index f5961365..3462ff20 100644
--- a/extras/catch_amalgamated_third_party.hpp
+++ b/extras/catch_amalgamated_third_party.hpp
@@ -1,5 +1,5 @@
// Catch3 v0.3.0: yyjson and pugixml for catch_amalgamated.cpp
-// Generated: 2026-10-29 19:10:35.543593
+// Generated: 2026-10-31 13:33:37.396913
// ----------------------------------------------------------
// This file is an amalgamation of src/Progmasoft/Runner/Yyjson.inl,
// src/Progmasoft/PugiXml.inl and the libraries they include from
diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt
index c5b71e9c..03e205ce 100644
--- a/fuzzing/CMakeLists.txt
+++ b/fuzzing/CMakeLists.txt
@@ -8,7 +8,7 @@ add_library(fuzzhelper NullOStream.h NullOStream.cpp)
target_link_libraries(fuzzhelper PUBLIC Catch3::Catch3)
# The runner fuzzers include its C++20 headers.
-target_compile_features(fuzzhelper PUBLIC cxx_std_20)
+target_compile_features(fuzzhelper PUBLIC cxx_std_17)
# This should be possible to set from the outside to be oss-fuzz compatible,
# fix later. For now, target libFuzzer only.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f3b7812d..398260fd 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -398,8 +398,7 @@ set_target_properties(Catch3 PROPERTIES
# Code that includes the engine's headers needs C++14, and the other
# Progmasoft/ headers C++20; the library itself, with the runner, is built as C++20.
-target_compile_features(Catch3 PUBLIC cxx_std_14)
-set_target_properties(Catch3 PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON)
+target_compile_features(Catch3 PUBLIC cxx_std_17)
configure_file(
"${SOURCES_DIR}/UserConfig.hpp.in"
@@ -491,7 +490,7 @@ if(CATCH3_BUILD_EXAMPLES OR CATCH3_BUILD_EXTRA_TESTS)
)
# These targets compile the library's sources themselves, the runner's
# among them, which need C++20.
- target_compile_features(Catch3_buildall_interface INTERFACE cxx_std_20)
+ target_compile_features(Catch3_buildall_interface INTERFACE cxx_std_17)
endif()
list(APPEND CATCH_IMPL_TARGETS Catch3 Catch3WithMain)
diff --git a/src/Progmasoft/BUILD.bazel b/src/Progmasoft/BUILD.bazel
index 5824d1b6..a3b4d68e 100644
--- a/src/Progmasoft/BUILD.bazel
+++ b/src/Progmasoft/BUILD.bazel
@@ -4,12 +4,11 @@ load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
load("@rules_cc//cc:defs.bzl", "cc_library")
# Catch3: the Catch2-compatible engine in Catch3/, the runner in Runner/,
-# property testing and snapshots. It is built as C++20; code that includes
-# only the engine's headers needs C++14. //:catch3 and //:catch3_main are the
-# public names.
-CPP20 = select({
- "@platforms//os:windows": ["/std:c++20"],
- "//conditions:default": ["-std=c++20"],
+# property testing and snapshots. It is C++17 and its headers work in C++17
+# and later. //:catch3 and //:catch3_main are the public names.
+CPP17 = select({
+ "@platforms//os:windows": ["/std:c++17"],
+ "//conditions:default": ["-std=c++17"],
})
# The engine's configuration header, with Bazel support switched on.
@@ -89,7 +88,7 @@ cc_library(
exclude = ["Catch3/Internal/Main.cpp"],
),
hdrs = glob(["**/*.hpp"]) + [":Catch3/UserConfig.hpp"],
- copts = CPP20,
+ copts = CPP17,
linkstatic = True,
strip_include_prefix = "/src",
visibility = ["//:__pkg__"],
@@ -102,7 +101,7 @@ cc_library(
cc_library(
name = "catch3_main",
srcs = ["Catch3/Internal/Main.cpp"],
- copts = CPP20,
+ copts = CPP17,
linkstatic = True,
visibility = ["//:__pkg__"],
deps = [":catch3"],
diff --git a/src/Progmasoft/Catch3/Detail/TypeTraits.hpp b/src/Progmasoft/Catch3/Detail/TypeTraits.hpp
new file mode 100644
index 00000000..56f06665
--- /dev/null
+++ b/src/Progmasoft/Catch3/Detail/TypeTraits.hpp
@@ -0,0 +1,47 @@
+// Copyright (c) 2026 Progmasoft.
+// SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-1.1
+#pragma once
+
+#include <type_traits>
+#include <utility>
+
+namespace Progmasoft::Catch3::Detail
+{
+
+ /// std::remove_cvref_t, which C++17 does not have.
+ template<typename Type>
+ using RemoveCvRef = std::remove_cv_t<std::remove_reference_t<Type>>;
+
+ /// std::identity, which C++17 does not have.
+ struct Identity final
+ {
+ template<typename Type>
+ [[nodiscard]] constexpr Type &&
+ operator()(Type &&value) const noexcept
+ {
+ return std::forward<Type>(value);
+ }
+ };
+
+ /// An integral type that holds numbers, so anything but bool.
+ template<typename Type>
+ inline constexpr bool kIsInteger
+ = std::is_integral_v<Type>
+ && !std::is_same_v<std::remove_cv_t<Type>, bool>;
+
+ /// Limits a function template to the types of kIsInteger.
+ template<typename Type>
+ using EnableIfInteger = std::enable_if_t<kIsInteger<Type>, int>;
+
+ template<typename Type, typename = void>
+ inline constexpr bool kIsEqualityComparable = false;
+
+ template<typename Type>
+ inline constexpr bool kIsEqualityComparable<
+ Type,
+ std::enable_if_t<
+ std::is_convertible_v<decltype(std::declval<const Type &>()
+ == std::declval<const Type &>()),
+ bool>>> = true;
+
+} // namespace Progmasoft::Catch3::Detail
diff --git a/src/Progmasoft/Catch3/ForAll.hpp b/src/Progmasoft/Catch3/ForAll.hpp
index e41bfdb6..748b89c6 100644
--- a/src/Progmasoft/Catch3/ForAll.hpp
+++ b/src/Progmasoft/Catch3/ForAll.hpp
@@ -148,7 +148,27 @@ namespace Progmasoft::Catch3
template<typename Type>
inline constexpr bool kIsPropertyOptions
- = std::is_same_v<std::remove_cvref_t<Type>, PropertyOptions>;
+ = std::is_same_v<RemoveCvRef<Type>, PropertyOptions>;
+
+ template<typename Arguments, std::size_t... Index>
+ [[nodiscard]] PropertyResult
+ ForAllWithOptions(Arguments &all, std::index_sequence<Index...>)
+ {
+ constexpr std::size_t kCount = std::tuple_size_v<Arguments>;
+ return ForAllWith(std::get<0>(all),
+ std::get<kCount - 1U>(all),
+ std::get<Index + 1U>(all)...);
+ }
+
+ template<typename Arguments, std::size_t... Index>
+ [[nodiscard]] PropertyResult
+ ForAllWithDefaults(Arguments &all, std::index_sequence<Index...>)
+ {
+ constexpr std::size_t kCount = std::tuple_size_v<Arguments>;
+ return ForAllWith(PropertyOptions{},
+ std::get<kCount - 1U>(all),
+ std::get<Index>(all)...);
+ }
} // namespace Detail
@@ -172,19 +192,15 @@ namespace Pr