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

summaryrefslogtreecommitdiffhomepage
path: root/src/Progmasoft/Catch3/Generators
diff options
context:
space:
mode:
authorLeitwolf2026-10-25 10:31:36 +0300
committerGitHub2026-10-25 10:31:36 +0300
commitd2e3e074bb2e1b8ffd9fc990d0b87667ea602f58 (patch)
tree0bce4462b2e190c39b3e1c5f697e3e4abd1a5e2a /src/Progmasoft/Catch3/Generators
parent13f81a480a93807c14dddb5abdb97355ced4d4c5 (diff)
parent126dc333564cb72b5115d9a9242d66694694b6d0 (diff)
downloadcatch3-d2e3e074bb2e1b8ffd9fc990d0b87667ea602f58.tar.gz
Merge pull request #18 from Progmasoft/build/catch3-identity
Give Catch3 its own build names and headers
Diffstat (limited to 'src/Progmasoft/Catch3/Generators')
-rw-r--r--src/Progmasoft/Catch3/Generators/Adapters.hpp317
-rw-r--r--src/Progmasoft/Catch3/Generators/All.hpp29
-rw-r--r--src/Progmasoft/Catch3/Generators/GeneratorException.cpp17
-rw-r--r--src/Progmasoft/Catch3/Generators/GeneratorException.hpp29
-rw-r--r--src/Progmasoft/Catch3/Generators/Generators.cpp32
-rw-r--r--src/Progmasoft/Catch3/Generators/Generators.hpp258
-rw-r--r--src/Progmasoft/Catch3/Generators/Random.cpp46
-rw-r--r--src/Progmasoft/Catch3/Generators/Random.hpp108
-rw-r--r--src/Progmasoft/Catch3/Generators/Range.hpp114
-rw-r--r--src/Progmasoft/Catch3/Generators/Throw.cpp24
-rw-r--r--src/Progmasoft/Catch3/Generators/Throw.hpp21
11 files changed, 995 insertions, 0 deletions
diff --git a/src/Progmasoft/Catch3/Generators/Adapters.hpp b/src/Progmasoft/Catch3/Generators/Adapters.hpp
new file mode 100644
index 00000000..3b6f13c3
--- /dev/null
+++ b/src/Progmasoft/Catch3/Generators/Adapters.hpp
@@ -0,0 +1,317 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#pragma once
+
+#include <Progmasoft/Catch3/Generators/Generators.hpp>
+#include <Progmasoft/Catch3/Internal/Meta.hpp>
+#include <Progmasoft/Catch3/Internal/MoveAndForward.hpp>
+#include <Progmasoft/Catch3/Internal/Optional.hpp>
+
+#include <cassert>
+
+namespace Catch {
+namespace Generators {
+
+ template <typename T>
+ class TakeGenerator final : public IGenerator<T> {
+ GeneratorWrapper<T> m_generator;
+ size_t m_returned = 0;
+ size_t m_target;
+
+ void skipToNthElementImpl( std::size_t n ) override {
+ if ( n >= m_target ) {
+ Detail::throw_generator_exception(
+ "Could not jump to Nth element: not enough elements" );
+ }
+
+ m_generator.skipToNthElement( n );
+ m_returned = n;
+ }
+
+ public:
+ TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
+ m_generator(CATCH_MOVE(generator)),
+ m_target(target)
+ {
+ assert(target != 0 && "Empty generators are not allowed");
+ }
+ T const& get() const override {
+ return m_generator.get();
+ }
+ bool next() override {
+ ++m_returned;
+ if (m_returned >= m_target) {
+ return false;
+ }
+
+ const auto success = m_generator.next();
+ // If the underlying generator does not contain enough values
+ // then we cut short as well
+ if (!success) {
+ m_returned = m_target;
+ }
+ return success;
+ }
+
+ bool isFinite() const override { return true; }
+ };
+
+ template <typename T>
+ GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
+ return GeneratorWrapper<T>(Catch::Detail::make_unique<TakeGenerator<T>>(target, CATCH_MOVE(generator)));
+ }
+
+
+ template <typename T, typename Predicate>
+ class FilterGenerator final : public IGenerator<T> {
+ GeneratorWrapper<T> m_generator;
+ Predicate m_predicate;
+ static_assert(!std::is_reference<Predicate>::value, "This would most likely result in a dangling reference");
+ public:
+ template <typename P>
+ FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
+ m_generator(CATCH_MOVE(generator)),
+ m_predicate(CATCH_FORWARD(pred))
+ {
+ if (!m_predicate(m_generator.get())) {
+ // It might happen that there are no values that pass the
+ // filter. In that case we throw an exception.
+ auto has_initial_value = next();
+ if (!has_initial_value) {
+ Detail::throw_generator_exception("No valid value found in filtered generator");
+ }
+ }
+ }
+
+ T const& get() const override {
+ return m_generator.get();
+ }
+
+ bool next() override {
+ bool success = m_generator.next();
+ if (!success) {
+ return false;
+ }
+ while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
+ return success;
+ }
+
+ bool isFinite() const override { return m_generator.isFinite(); }
+ };
+
+
+ template <typename T, typename Predicate>
+ GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
+ return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, typename std::remove_reference<Predicate>::type>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
+ }
+
+ template <typename T>
+ class RepeatGenerator final : public IGenerator<T> {
+ static_assert(!std::is_same<T, bool>::value,
+ "RepeatGenerator currently does not support bools"
+ "because of std::vector<bool> specialization");
+ GeneratorWrapper<T> m_generator;
+ mutable std::vector<T> m_returned;
+ size_t m_target_repeats;
+ size_t m_current_repeat = 0;
+ size_t m_repeat_index = 0;
+ public:
+ RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator):
+ m_generator(CATCH_MOVE(generator)),
+ m_target_repeats(repeats)
+ {
+ assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
+ if (!m_generator.isFinite()) {
+ Detail::throw_generator_exception( "Cannot repeat infinite generator" );
+ }
+ }
+
+ T const& get() const override {
+ if (m_current_repeat == 0) {
+ m_returned.push_back(m_generator.get());
+ return m_returned.back();
+ }
+ return m_returned[m_repeat_index];
+ }
+
+ bool next() override {
+ // There are 2 basic cases:
+ // 1) We are still reading the generator
+ // 2) We are reading our own cache
+
+ // In the first case, we need to poke the underlying generator.
+ // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
+ if (m_current_repeat == 0) {
+ const auto success = m_generator.next();
+ if (!success) {
+ ++m_current_repeat;
+ }
+ return m_current_repeat < m_target_repeats;
+ }
+
+ // In the second case, we need to move indices forward and check that we haven't run up against the end
+ ++m_repeat_index;
+ if (m_repeat_index == m_returned.size()) {
+ m_repeat_index = 0;
+ ++m_current_repeat;
+ }
+ return m_current_repeat < m_target_repeats;
+ }
+
+ bool isFinite() const override { return m_generator.isFinite(); }
+ };
+
+ template <typename T>
+ GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
+ return GeneratorWrapper<T>(Catch::Detail::make_unique<RepeatGenerator<T>>(repeats, CATCH_MOVE(generator)));
+ }
+
+ template <typename T, typename U, typename Func>
+ class MapGenerator final : public IGenerator<T> {
+ // TBD: provide static assert for mapping function, for friendly error message
+ GeneratorWrapper<U> m_generator;
+ Func m_function;
+ // To avoid returning dangling reference, we have to save the values
+ mutable Optional<T> m_cache;
+
+ void skipToNthElementImpl( std::size_t n ) override {
+ m_generator.skipToNthElement( n );
+ m_cache.reset();
+ }
+
+ public:
+ template <typename F2 = Func>
+ MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
+ m_generator(CATCH_MOVE(generator)),
+ m_function(CATCH_FORWARD(function))
+ {}
+
+ T const& get() const override {
+ if ( !m_cache ) { m_cache = m_function( m_generator.get() ); }
+ return *m_cache;
+ }
+ bool next() override {
+ m_cache.reset();
+ return m_generator.next();
+ }
+
+ bool isFinite() const override { return m_generator.isFinite(); }
+ };
+
+ template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
+ GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
+ return GeneratorWrapper<T>(
+ Catch::Detail::make_unique<MapGenerator<T, U, Func>>(CATCH_FORWARD(function), CATCH_MOVE(generator))
+ );
+ }
+
+ template <typename T, typename U, typename Func>
+ GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
+ return GeneratorWrapper<T>(
+ Catch::Detail::make_unique<MapGenerator<T, U, Func>>(CATCH_FORWARD(function), CATCH_MOVE(generator))
+ );
+ }
+
+ template <typename T>
+ class ChunkGenerator final : public IGenerator<std::vector<T>> {
+ std::vector<T> m_chunk;
+ size_t m_chunk_size;
+ GeneratorWrapper<T> m_generator;
+ public:
+ ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
+ m_chunk_size(size), m_generator(CATCH_MOVE(generator))
+ {
+ m_chunk.reserve(m_chunk_size);
+ if (m_chunk_size != 0) {
+ m_chunk.push_back(m_generator.get());
+ for (size_t i = 1; i < m_chunk_size; ++i) {
+ if (!m_generator.next()) {
+ Detail::throw_generator_exception("Not enough values to initialize the first chunk");
+ }
+ m_chunk.push_back(m_generator.get());
+ }
+ }
+ }
+ std::vector<T> const& get() const override {
+ return m_chunk;
+ }
+ bool next() override {
+ m_chunk.clear();
+ for (size_t idx = 0; idx < m_chunk_size; ++idx) {
+ if (!m_generator.next()) {
+ return false;
+ }
+ m_chunk.push_back(m_generator.get());
+ }
+ return true;
+ }
+
+ bool isFinite() const override { return m_generator.isFinite(); }
+ };
+
+ template <typename T>
+ GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) {
+ return GeneratorWrapper<std::vector<T>>(
+ Catch::Detail::make_unique<ChunkGenerator<T>>(size, CATCH_MOVE(generator))
+ );
+ }
+
+ template <typename T>
+ class ConcatGenerator final : public IGenerator<T> {
+ std::vector<GeneratorWrapper<T>> m_generators;
+ size_t m_current_generator = 0;
+
+ void InsertGenerators( GeneratorWrapper<T>&& gen ) {
+ m_generators.push_back( CATCH_MOVE( gen ) );
+ }
+
+ template <typename... Generators>
+ void InsertGenerators( GeneratorWrapper<T>&& gen, Generators&&... gens ) {
+ m_generators.push_back( CATCH_MOVE( gen ) );
+ InsertGenerators( CATCH_MOVE( gens )... );
+ }
+
+ public:
+ template <typename... Generators>
+ ConcatGenerator( Generators&&... generators ) {
+ InsertGenerators( CATCH_MOVE( generators )... );
+ }
+
+ T const& get() const override {
+ return m_generators[m_current_generator].get();
+ }
+ bool next() override {
+ const bool success = m_generators[m_current_generator].next();
+ if ( success ) { return true; }
+
+ // If current generator is used up, we have to move to the next one
+ ++m_current_generator;
+ return m_current_generator < m_generators.size();
+ }
+
+ bool isFinite() const override {
+ for ( auto const& gen : m_generators ) {
+ if ( !gen.isFinite() ) { return false; }
+ }
+ return true;
+ }
+ };
+
+ template <typename T, typename... Generators>
+ GeneratorWrapper<T> cat( GeneratorWrapper<T>&& generator,
+ Generators&&... generators ) {
+ return GeneratorWrapper<T>(
+ Catch::Detail::make_unique<ConcatGenerator<T>>(
+ CATCH_MOVE( generator ), CATCH_MOVE( generators )... ) );
+ }
+
+
+} // namespace Generators
+} // namespace Catch
+
+
diff --git a/src/Progmasoft/Catch3/Generators/All.hpp b/src/Progmasoft/Catch3/Generators/All.hpp
new file mode 100644
index 00000000..ec42b493
--- /dev/null
+++ b/src/Progmasoft/Catch3/Generators/All.hpp
@@ -0,0 +1,29 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+/** \file
+ * This is a convenience header for Catch2's Generator support. It includes
+ * **all** of Catch2 headers related to generators.
+ *
+ * Generally the Catch2 users should use specific includes they need,
+ * but this header can be used instead for ease-of-experimentation, or
+ * just plain convenience, at the cost of (significantly) increased
+ * compilation times.
+ *
+ * When a new header is added to either the `generators` folder,
+ * or to the corresponding internal subfolder, it should be added here.
+ */
+
+#pragma once
+
+#include <Progmasoft/Catch3/Generators/Adapters.hpp>
+#include <Progmasoft/Catch3/Generators/GeneratorException.hpp>
+#include <Progmasoft/Catch3/Generators/Generators.hpp>
+#include <Progmasoft/Catch3/Generators/Random.hpp>
+#include <Progmasoft/Catch3/Generators/Range.hpp>
+#include <Progmasoft/Catch3/Generators/Throw.hpp>
+
diff --git a/src/Progmasoft/Catch3/Generators/GeneratorException.cpp b/src/Progmasoft/Catch3/Generators/GeneratorException.cpp
new file mode 100644
index 00000000..2a743302
--- /dev/null
+++ b/src/Progmasoft/Catch3/Generators/GeneratorException.cpp
@@ -0,0 +1,17 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <Progmasoft/Catch3/Generators/GeneratorException.hpp>
+
+namespace Catch {
+
+ const char* GeneratorException::what() const noexcept {
+ return m_msg;
+ }
+
+} // end namespace Catch
diff --git a/src/Progmasoft/Catch3/Generators/GeneratorException.hpp b/src/Progmasoft/Catch3/Generators/GeneratorException.hpp
new file mode 100644
index 00000000..aa63ba8d
--- /dev/null
+++ b/src/Progmasoft/Catch3/Generators/GeneratorException.hpp
@@ -0,0 +1,29 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#pragma once
+
+#include <exception>
+
+namespace Catch {
+
+ // Exception type to be thrown when a Generator runs into an error,
+ // e.g. it cannot initialize the first return value based on
+ // runtime information
+ class GeneratorException : public std::exception {
+ const char* const m_msg = "";
+
+ public:
+ GeneratorException(const char* msg):
+ m_msg(msg)
+ {}
+
+ const char* what() const noexcept final;
+ };
+
+} // end namespace Catch
+
diff --git a/src/Progmasoft/Catch3/Generators/Generators.cpp b/src/Progmasoft/Catch3/Generators/Generators.cpp
new file mode 100644
index 00000000..69740a73
--- /dev/null
+++ b/src/Progmasoft/Catch3/Generators/Generators.cpp
@@ -0,0 +1,32 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <Progmasoft/Catch3/Generators/Generators.hpp>
+#include <Progmasoft/Catch3/Interfaces/IResultCapture.hpp>
+
+namespace Catch {
+
+ IGeneratorTracker::~IGeneratorTracker() = default;
+
+namespace Generators {
+
+ GeneratorUntypedBase::~GeneratorUntypedBase() = default;
+
+ IGeneratorTracker* acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const& lineInfo ) {
+ return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo );
+ }
+
+ IGeneratorTracker* createGeneratorTracker( StringRef generatorName,
+ SourceLineInfo lineInfo,
+ GeneratorBasePtr&& generator ) {
+ return getResultCapture().createGeneratorTracker(
+ generatorName, lineInfo, CATCH_MOVE( generator ) );
+ }
+
+} // namespace Generators
+} // namespace Catch
diff --git a/src/Progmasoft/Catch3/Generators/Generators.hpp b/src/Progmasoft/Catch3/Generators/Generators.hpp
new file mode 100644
index 00000000..a7596f12
--- /dev/null
+++ b/src/Progmasoft/Catch3/Generators/Generators.hpp
@@ -0,0 +1,258 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#pragma once
+
+#include <Progmasoft/Catch3/ToString.hpp>
+#include <Progmasoft/Catch3/Generators/Throw.hpp>
+#include <Progmasoft/Catch3/Interfaces/IGeneratorTracker.hpp>
+#include <Progmasoft/Catch3/Internal/SourceLineInfo.hpp>
+#include <Progmasoft/Catch3/Internal/StringRef.hpp>
+#include <Progmasoft/Catch3/Internal/MoveAndForward.hpp>
+#include <Progmasoft/Catch3/Internal/UniqueName.hpp>
+
+#include <vector>
+#include <tuple>
+
+namespace Catch {
+
+namespace Generators {
+
+ template<typename T>
+ class IGenerator : public GeneratorUntypedBase {
+ std::string stringifyImpl() const override {
+ return ::Catch::Detail::stringify( get() );
+ }
+
+ public:
+ // Returns the current element of the generator
+ //
+ // \Precondition The generator is either freshly constructed,
+ // or the last call to `next()` returned true
+ virtual T const& get() const = 0;
+ using type = T;
+ };
+
+ template <typename T>
+ using GeneratorPtr = Catch::Detail::unique_ptr<IGenerator<T>>;
+
+ template <typename T>
+ class GeneratorWrapper final {
+ GeneratorPtr<T> m_generator;
+ public:
+ //! Takes ownership of the passed pointer.
+ GeneratorWrapper(IGenerator<T>* generator):
+ m_generator(generator) {}
+ GeneratorWrapper(GeneratorPtr<T> generator):
+ m_generator(CATCH_MOVE(generator)) {}
+
+ T const& get() const {
+ return m_generator->get();
+ }
+ bool next() {
+ return m_generator->countedNext();
+ }
+
+ bool isFinite() const { return m_generator->isFinite(); }
+ void skipToNthElement( size_t n ) { m_generator->skipToNthElement(n); }
+ };
+
+
+ template<typename T>
+ class SingleValueGenerator final : public IGenerator<T> {
+ T m_value;
+ public:
+ SingleValueGenerator(T const& value) :
+ m_value(value)
+ {}
+ SingleValueGenerator(T&& value):
+ m_value(CATCH_MOVE(value))
+ {}
+
+ T const& get() const override {
+ return m_value;
+ }
+ bool next() override {
+ return false;
+ }
+
+ bool isFinite() const override { return true; }
+ };
+
+ template<typename T>
+ class FixedValuesGenerator final : public IGenerator<T> {
+ static_assert(!std::is_same<T, bool>::value,
+ "FixedValuesGenerator does not support bools because of std::vector<bool>"
+ "specialization, use SingleValue Generator instead.");
+ std::vector<T> m_values;
+ size_t m_idx = 0;
+
+ void skipToNthElementImpl( std::size_t n ) override {
+ if ( n >= m_values.size() ) {
+ Detail::throw_generator_exception(
+ "Could not jump to Nth element: not enough elements" );
+ }
+ m_idx = n;
+ }
+
+ public:
+ FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
+
+ T const& get() const override {
+ return m_values[m_idx];
+ }
+ bool next() override {
+ ++m_idx;
+ return m_idx < m_values.size();
+ }
+
+ bool isFinite() const override { return true; }
+ };
+
+ template <typename T, typename DecayedT = std::decay_t<T>>
+ GeneratorWrapper<DecayedT> value( T&& value ) {
+ return GeneratorWrapper<DecayedT>(
+ Catch::Detail::make_unique<SingleValueGenerator<DecayedT>>(
+ CATCH_FORWARD( value ) ) );
+ }
+ template <typename T>
+ GeneratorWrapper<T> values(std::initializer_list<T> values) {
+ return GeneratorWrapper<T>(Catch::Detail::make_unique<FixedValuesGenerator<T>>(values));
+ }
+
+ template<typename T>
+ class Generators : public IGenerator<T> {
+ std::vector<GeneratorWrapper<T>> m_generators;
+ size_t m_current = 0;
+
+ void add_generator( GeneratorWrapper<T>&& generator ) {
+ m_generators.emplace_back( CATCH_MOVE( generator ) );
+ }
+ void add_generator( T const& val ) {
+ m_generators.emplace_back( value( val ) );
+ }
+ void add_generator( T&& val ) {
+ m_generators.emplace_back( value( CATCH_MOVE( val ) ) );
+ }
+ template <typename U>
+ std::enable_if_t<!std::is_same<std::decay_t<U>, T>::value>
+ add_generator( U&& val ) {
+ add_generator( T( CATCH_FORWARD( val ) ) );
+ }
+
+ template <typename U> void add_generators( U&& valueOrGenerator ) {
+ add_generator( CATCH_FORWARD( valueOrGenerator ) );
+ }
+
+ template <typename U, typename... Gs>
+ void add_generators( U&& valueOrGenerator, Gs&&... moreGenerators ) {
+ add_generator( CATCH_FORWARD( valueOrGenerator ) );
+ add_generators( CATCH_FORWARD( moreGenerators )... );
+ }
+
+ public:
+ template <typename... Gs>
+ Generators(Gs &&... moreGenerators) {
+ m_generators.reserve(sizeof...(Gs));
+ add_generators(CATCH_FORWARD(moreGenerators)...);
+ }
+
+ T const& get() const override {
+ return m_generators[m_current].get();
+ }
+
+ bool next() override {
+ if (m_current >= m_generators.size()) {
+ return false;
+ }
+ const bool current_status = m_generators[m_current].next();
+ if (!current_status) {
+ ++m_current;
+ }
+ return m_current < m_generators.size();
+ }
+
+ bool isFinite() const override {
+ for (auto const& gen : m_generators) {
+ if (!gen.isFinite()) { return false; }
+ }
+ return true;
+ }
+ };
+
+
+ template <typename... Ts>
+ GeneratorWrapper<std::tuple<std::decay_t<Ts>...>>
+ table( std::initializer_list<std::tuple<std::decay_t<Ts>...>> tuples ) {
+ return values<std::tuple<Ts...>>( tuples );
+ }
+
+ // Tag type to signal that a generator sequence should convert arguments to a specific type
+ template <typename T>
+ struct as {};
+
+ template<typename T, typename... Gs>
+ auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> {
+ return Generators<T>(CATCH_MOVE(generator), CATCH_FORWARD(moreGenerators)...);
+ }
+ template<typename T>
+ auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
+ return Generators<T>(CATCH_MOVE(generator));
+ }
+ template<typename T, typename... Gs>
+ auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<std::decay_t<T>> {
+ return makeGenerators( value( CATCH_FORWARD( val ) ), CATCH_FORWARD( moreGenerators )... );
+ }
+ template<typename T, typename U, typename... Gs>
+ auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> {
+ return makeGenerators( value( T( CATCH_FORWARD( val ) ) ), CATCH_FORWARD( moreGenerators )... );
+ }
+
+ IGeneratorTracker* acquireGeneratorTracker( StringRef generatorName,
+ SourceLineInfo const& lineInfo );
+ IGeneratorTracker* createGeneratorTracker( StringRef generatorName,
+ SourceLineInfo lineInfo,
+ GeneratorBasePtr&& generator );
+
+ template<typename L>
+ auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> typename decltype(generatorExpression())::type {
+ using UnderlyingType = typename decltype(generatorExpression())::type;
+
+ IGeneratorTracker* tracker = acquireGeneratorTracker( generatorName, lineInfo );
+ // Creation of tracker is delayed after generator creation, so
+ // that constructing generator can fail without breaking everything.
+ if (!tracker) {
+ tracker = createGeneratorTracker(
+ generatorName,
+ lineInfo,
+ Catch::Detail::make_unique<Generators<UnderlyingType>>(
+ generatorExpression() ) );
+ }
+
+ auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker->getGenerator() );
+ return generator.get();
+ }
+
+} // namespace Generators
+} // namespace Catch
+
+#define CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL( ... ) #__VA_ARGS__##_catch_sr
+#define CATCH_INTERNAL_GENERATOR_STRINGIZE(...) CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL(__VA_ARGS__)
+
+#define GENERATE( ... ) \
+ Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+ CATCH_INTERNAL_LINEINFO, \
+ [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+#define GENERATE_COPY( ... ) \
+ Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+ CATCH_INTERNAL_LINEINFO, \
+ [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+#define GENERATE_REF( ... ) \
+ Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+ CATCH_INTERNAL_LINEINFO, \
+ [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
+
diff --git a/src/Progmasoft/Catch3/Generators/Random.cpp b/src/Progmasoft/Catch3/Generators/Random.cpp
new file mode 100644
index 00000000..e45d944d
--- /dev/null
+++ b/src/Progmasoft/Catch3/Generators/Random.cpp
@@ -0,0 +1,46 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <Progmasoft/Catch3/Generators/Random.hpp>
+#include <Progmasoft/Catch3/Internal/Context.hpp>
+
+#include <random>
+
+namespace Catch {
+ namespace Generators {
+ namespace Detail {
+ std::uint32_t getSeed() { return sharedRng()(); }
+ } // namespace Detail
+
+ struct RandomFloatingGenerator<long double>::PImpl {
+ PImpl( long double a, long double b, uint32_t seed ):
+ rng( seed ), dist( a, b ) {}
+
+ Catch::SimplePcg32 rng;
+ std::uniform_real_distribution<long double> dist;
+ };
+
+ RandomFloatingGenerator<long double>::RandomFloatingGenerator(
+ long double a, long double b, std::uint32_t seed) :
+ m_pimpl(Catch::Detail::make_unique<PImpl>(a, b, seed)) {
+ static_cast<void>( next() );
+ }
+
+ RandomFloatingGenerator<long double>::~RandomFloatingGenerator() =
+ default;
+ bool RandomFloatingGenerator<long double>::next() {
+ m_current_number = m_pimpl->dist( m_pimpl->rng );
+ return true;
+ }
+
+ bool RandomFloatingGenerator<long double>::isFinite() const {
+ return false;
+ }
+
+ } // namespace Generators
+} // namespace Catch
diff --git a/src/Progmasoft/Catch3/Generators/Random.hpp b/src/Progmasoft/Catch3/Generators/Random.hpp
new file mode 100644
index 00000000..ae6b09a5
--- /dev/null
+++ b/src/Progmasoft/Catch3/Generators/Random.hpp
@@ -0,0 +1,108 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#pragma once
+
+#include <Progmasoft/Catch3/Generators/Generators.hpp>
+#include <Progmasoft/Catch3/Internal/RandomNumberGenerator.hpp>
+#include <Progmasoft/Catch3/Internal/UniformIntegerDistribution.hpp>
+#include <Progmasoft/Catch3/Internal/UniformFloatingPointDistribution.hpp>
+#include <Progmasoft/Catch3/Internal/UniquePtr.hpp>
+
+namespace Catch {
+namespace Generators {
+namespace Detail {
+ // Returns a suitable seed for a random floating generator based off
+ // the primary internal rng. It does so by taking current value from
+ // the rng and returning it as the seed.
+ std::uint32_t getSeed();
+}
+
+template <typename Float>
+class RandomFloatingGenerator final : public IGenerator<Float> {
+ Catch::SimplePcg32 m_rng;
+ Catch::uniform_floating_point_distribution<Float> m_dist;
+ Float m_current_number;
+public:
+ RandomFloatingGenerator( Float a, Float b, std::uint32_t seed ):
+ m_rng(seed),
+ m_dist(a, b) {
+ static_cast<void>(next());
+ }
+
+ Float const& get() const override {
+ return m_current_number;
+ }
+ bool next() override {
+ m_current_number = m_dist(m_rng);
+ return true;
+ }
+ bool isFinite() const override { return false; }
+};
+
+template <>
+class RandomFloatingGenerator<long double> final : public IGenerator<long double> {
+ // We still rely on <random> for this specialization, but we don't