diff options
| author | Leitwolf11 | 2026-10-31 13:00:24 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-10-31 13:00:24 +0300 |
| commit | 8814bd0b796b2e291fa5b4a1033f0313c8a26065 (patch) | |
| tree | e87e5f5150f3712bb05ed53911425600aa871f45 | |
| parent | 6a42cdf6461b2577106ef094d6f98025554b6077 (diff) | |
| download | catch3-8814bd0b796b2e291fa5b4a1033f0313c8a26065.tar.gz | |
Build Catch3 as C++17
C++17 is still the widest common target, and a library built on it stays
usable from C++17 and every later standard, while C++20 would have left
out many users.
Concepts become enable_if on named traits in Detail/TypeTraits.hpp, which
also replaces std::remove_cvref_t and std::identity. std::span becomes
std::vector references, std::bit_cast becomes static_cast and memcpy,
template lambdas become helper templates, and std::erase_if, contains,
starts_with and ends_with have their C++17 forms. Random::Between no
longer uses a lambda over a constexpr local, which MSVC rejects outside
/permissive-, so the headers work under its default mode too.
Tests give up designated initializers, which MSVC refuses in C++17. CI
builds the matrix in C++17 and C++20, since the headers promise both.
41 files changed, 408 insertions, 240 deletions
@@ -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/.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} @@ -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/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 Progmasoft::Catch3 using First = std::tuple_element_t<0, std::tuple<Arguments...>>; if constexpr (Detail::kIsPropertyOptions<First>) { - return [&]<std::size_t... Index>(std::index_sequence<Index...>) { - return Detail::ForAllWith(std::get<0>(all), - std::get<kCount - 1U>(all), - std::get<Index + 1U>(all)...); - }(std::make_index_sequence<kCount - 2U>{}); + return Detail::ForAllWithOptions( + all, + std::make_index_sequence<kCount - 2U>{}); } else { - return [&]<std::size_t... Index>(std::index_sequence<Index...>) { - return Detail::ForAllWith(PropertyOptions{}, - std::get<kCount - 1U>(all), - std::get<Index>(all)...); - }(std::make_index_sequence<kCount - 1U>{}); + return Detail::ForAllWithDefaults( + all, + std::make_index_sequence<kCount - 1U>{}); } } diff --git a/src/Progmasoft/Catch3/Generators/Element.hpp b/src/Progmasoft/Catch3/Generators/Element.hpp index 087b6355..2c270d18 100644 --- a/src/Progmasoft/Catch3/Generators/Element.hpp +++ b/src/Progmasoft/Catch3/Generators/Element.hpp @@ -2,9 +2,9 @@ // SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-1.1 #pragma once +#include <Progmasoft/Catch3/Detail/TypeTraits.hpp> #include <Progmasoft/Catch3/Generator.hpp> #include <algorithm> -#include <concepts> #include <cstddef> #include <initializer_list> #include <iterator> @@ -20,8 +20,9 @@ namespace Progmasoft::Catch3 /// The list order is the simplicity order: a value shrinks to the values /// listed before it, earliest first, so place the simplest case first. /// A value that is not in the list has no candidates. - template<typename ValueType> - requires std::equality_comparable<ValueType> + template<typename ValueType, + std::enable_if_t<Detail::kIsEqualityComparable<ValueType>, int> + = 0> [[nodiscard]] Generator<ValueType> Element(std::vector<ValueType> values) { @@ -52,8 +53,9 @@ namespace Progmasoft::Catch3 } /// Convenience overload for a literal list of values. - template<typename ValueType> - requires std::equality_comparable<ValueType> + template<typename ValueType, + std::enable_if_t<Detail::kIsEqualityComparable<ValueType>, int> + = 0> [[nodiscard]] Generator<ValueType> Element(std::initializer_list<ValueType> values) { diff --git a/src/Progmasoft/Catch3/Generators/Integer.hpp b/src/Progmasoft/Catch3/Generators/Integer.hpp index 45192b3d..3659aa4f 100644 --- a/src/Progmasoft/Catch3/Generators/Integer.hpp +++ b/src/Progmasoft/Catch3/Generators/Integer.hpp @@ -2,9 +2,9 @@ // SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-1.1 #pragma once +#include <Progmasoft/Catch3/Detail/TypeTraits.hpp> #include <Progmasoft/Catch3/Generator.hpp> #include <algorithm> -#include <concepts> #include <limits> #include <type_traits> @@ -13,8 +13,7 @@ namespace Progmasoft::Catch3 namespace Detail { - template<std::integral IntegerType> - requires(!std::same_as<IntegerType, bool>) + template<typename IntegerType, Detail::EnableIfInteger<IntegerType> = 0> [[nodiscard]] std::vector<IntegerType> ShrinkInteger(IntegerType value, IntegerType minimum, @@ -24,7 +23,9 @@ namespace Progmasoft::Catch3 const auto addCandidate = [&](IntegerType candidate) { if (candidate < minimum || candidate > maximum || candidate == value - || std::ranges::find(candidates, candidate) + || std::find(candidates.begin(), + candidates.end(), + candidate) != candidates.end()) { return; @@ -86,8 +87,7 @@ namespace Progmasoft::Catch3 /// Generates integers over the inclusive range and shrinks toward simple /// values. - template<std::integral IntegerType> - requires(!std::same_as<IntegerType, bool>) + template<typename IntegerType, Detail::EnableIfInteger<IntegerType> = 0> [[nodiscard]] Generator<IntegerType> Integer(IntegerType minimum = std::numeric_limits<IntegerType>::lowest(), IntegerType maximum = std::numeric_limits<IntegerType>::max()) diff --git a/src/Progmasoft/Catch3/Generators/Tuple.hpp b/src/Progmasoft/Catch3/Generators/Tuple.hpp index df58da05..3e889307 100644 --- a/src/Progmasoft/Catch3/Generators/Tuple.hpp +++ b/src/Progmasoft/Catch3/Generators/Tuple.hpp @@ -54,10 +54,11 @@ namespace Progmasoft::Catch3 /// runner's greedy restart this minimizes every component |