diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-06-29 19:25:29 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-06-29 19:25:29 +0100 |
| commit | bc524d70253a4ab2fe40c3ca3e5666e267c0a4d1 (patch) | |
| tree | 1e629e7b46b1d9972a973bc93fd100bcebd395be /src/catch2/generators | |
| download | nihil-vendor/catch2/3.8.1.tar.gz nihil-vendor/catch2/3.8.1.tar.bz2 | |
import catch2 3.8.1vendor/catch2/3.8.1vendor/catch2
Diffstat (limited to 'src/catch2/generators')
| -rw-r--r-- | src/catch2/generators/catch_generator_exception.cpp | 17 | ||||
| -rw-r--r-- | src/catch2/generators/catch_generator_exception.hpp | 31 | ||||
| -rw-r--r-- | src/catch2/generators/catch_generators.cpp | 42 | ||||
| -rw-r--r-- | src/catch2/generators/catch_generators.hpp | 244 | ||||
| -rw-r--r-- | src/catch2/generators/catch_generators_adapters.hpp | 241 | ||||
| -rw-r--r-- | src/catch2/generators/catch_generators_all.hpp | 30 | ||||
| -rw-r--r-- | src/catch2/generators/catch_generators_random.cpp | 41 | ||||
| -rw-r--r-- | src/catch2/generators/catch_generators_random.hpp | 107 | ||||
| -rw-r--r-- | src/catch2/generators/catch_generators_range.hpp | 111 |
9 files changed, 864 insertions, 0 deletions
diff --git a/src/catch2/generators/catch_generator_exception.cpp b/src/catch2/generators/catch_generator_exception.cpp new file mode 100644 index 0000000..6432403 --- /dev/null +++ b/src/catch2/generators/catch_generator_exception.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 <catch2/generators/catch_generator_exception.hpp> + +namespace Catch { + + const char* GeneratorException::what() const noexcept { + return m_msg; + } + +} // end namespace Catch diff --git a/src/catch2/generators/catch_generator_exception.hpp b/src/catch2/generators/catch_generator_exception.hpp new file mode 100644 index 0000000..f353042 --- /dev/null +++ b/src/catch2/generators/catch_generator_exception.hpp @@ -0,0 +1,31 @@ + +// 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 +#ifndef CATCH_GENERATOR_EXCEPTION_HPP_INCLUDED +#define CATCH_GENERATOR_EXCEPTION_HPP_INCLUDED + +#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 override final; + }; + +} // end namespace Catch + +#endif // CATCH_GENERATOR_EXCEPTION_HPP_INCLUDED diff --git a/src/catch2/generators/catch_generators.cpp b/src/catch2/generators/catch_generators.cpp new file mode 100644 index 0000000..3514e9f --- /dev/null +++ b/src/catch2/generators/catch_generators.cpp @@ -0,0 +1,42 @@ + +// 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 <catch2/generators/catch_generators.hpp> +#include <catch2/internal/catch_enforce.hpp> +#include <catch2/generators/catch_generator_exception.hpp> +#include <catch2/interfaces/catch_interfaces_capture.hpp> + +namespace Catch { + + IGeneratorTracker::~IGeneratorTracker() = default; + +namespace Generators { + +namespace Detail { + + [[noreturn]] + void throw_generator_exception(char const* msg) { + Catch::throw_exception(GeneratorException{ msg }); + } +} // end namespace Detail + + 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/catch2/generators/catch_generators.hpp b/src/catch2/generators/catch_generators.hpp new file mode 100644 index 0000000..0f35a99 --- /dev/null +++ b/src/catch2/generators/catch_generators.hpp @@ -0,0 +1,244 @@ + +// 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 +#ifndef CATCH_GENERATORS_HPP_INCLUDED +#define CATCH_GENERATORS_HPP_INCLUDED + +#include <catch2/catch_tostring.hpp> +#include <catch2/interfaces/catch_interfaces_generatortracker.hpp> +#include <catch2/internal/catch_source_line_info.hpp> +#include <catch2/internal/catch_stringref.hpp> +#include <catch2/internal/catch_move_and_forward.hpp> +#include <catch2/internal/catch_unique_name.hpp> + +#include <vector> +#include <tuple> + +namespace Catch { + +namespace Generators { + +namespace Detail { + + //! Throws GeneratorException with the provided message + [[noreturn]] + void throw_generator_exception(char const * msg); + +} // end namespace detail + + 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(); + } + }; + + + 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; + } + }; + + 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; + 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(); + } + }; + + 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(); + } + }; + + + 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) + +#endif // CATCH_GENERATORS_HPP_INCLUDED diff --git a/src/catch2/generators/catch_generators_adapters.hpp b/src/catch2/generators/catch_generators_adapters.hpp new file mode 100644 index 0000000..d5fc1e1 --- /dev/null +++ b/src/catch2/generators/catch_generators_adapters.hpp @@ -0,0 +1,241 @@ + +// 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 +#ifndef CATCH_GENERATORS_ADAPTERS_HPP_INCLUDED +#define CATCH_GENERATORS_ADAPTERS_HPP_INCLUDED + +#include <catch2/generators/catch_generators.hpp> +#include <catch2/internal/catch_meta.hpp> +#include <catch2/internal/catch_move_and_forward.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; + 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; + } + }; + + 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; + public: + template <typename P = Predicate> + 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; + } + }; + + + template <typename T, typename Predicate> + GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) { + return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, Predicate>>(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"); + } + + 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; + } + }; + + 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 + T m_cache; + public: + template <typename F2 = Func> + MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) : + m_generator(CATCH_MOVE(generator)), + m_function(CATCH_FORWARD(function)), + m_cache(m_function(m_generator.get())) + {} + + T const& get() const override { + return m_cache; + } + bool next() override { + const auto success = m_generator.next(); + if (success) { + m_cache = m_function(m_generator.get()); + } + return success; + } + }; + + 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; + bool m_used_up = false; + 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; + } + }; + + 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)) + ); + } + +} // namespace Generators +} // namespace Catch + + +#endif // CATCH_GENERATORS_ADAPTERS_HPP_INCLUDED diff --git a/src/catch2/generators/catch_generators_all.hpp b/src/catch2/generators/catch_generators_all.hpp new file mode 100644 index 0000000..c12d314 --- /dev/null +++ b/src/catch2/generators/catch_generators_all.hpp @@ -0,0 +1,30 @@ + +// 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. + */ + +#ifndef CATCH_GENERATORS_ALL_HPP_INCLUDED +#define CATCH_GENERATORS_ALL_HPP_INCLUDED + +#include <catch2/generators/catch_generator_exception.hpp> +#include <catch2/generators/catch_generators.hpp> +#include <catch2/generators/catch_generators_adapters.hpp> +#include <catch2/generators/catch_generators_random.hpp> +#include <catch2/generators/catch_generators_range.hpp> + +#endif // CATCH_GENERATORS_ALL_HPP_INCLUDED diff --git a/src/catch2/generators/catch_generators_random.cpp b/src/catch2/generators/catch_generators_random.cpp new file mode 100644 index 0000000..00a8e63 --- /dev/null +++ b/src/catch2/generators/catch_generators_random.cpp @@ -0,0 +1,41 @@ + +// 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 <catch2/generators/catch_generators_random.hpp> +#include <catch2/internal/catch_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; + } + } // namespace Generators +} // namespace Catch diff --git a/src/catch2/generators/catch_generators_random.hpp b/src/catch2/generators/catch_generators_random.hpp new file mode 100644 index 0000000..7128356 --- /dev/null +++ b/src/catch2/generators/catch_generators_random.hpp @@ -0,0 +1,107 @@ + +// 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 +#ifndef CATCH_GENERATORS_RANDOM_HPP_INCLUDED +#define CATCH_GENERATORS_RANDOM_HPP_INCLUDED + +#include <catch2/generators/catch_generators.hpp> +#include <catch2/internal/catch_random_number_generator.hpp> +#include <catch2/internal/catch_uniform_integer_distribution.hpp> +#include <catch2/internal/catch_uniform_floating_point_distribution.hpp> +#include <catch2/internal/catch_unique_ptr.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; + } +}; + +template <> +class RandomFloatingGenerator<long double> final : public IGenerator<long double> { + // We still rely on <random> for this specialization, but we don't + // want to drag it into the header. + struct PImpl; + Catch::Detail::unique_ptr<PImpl> m_pimpl; + long double m_current_number; + +public: + RandomFloatingGenerator( long double a, long double b, std::uint32_t seed ); + + long double const& get() const override { return m_current_number; } + bool next() override; + + ~RandomFloatingGenerator() override; // = default +}; + +template <typename Integer> +class RandomIntegerGenerator final : public IGenerator<Integer> { + Catch::SimplePcg32 m_rng; + Catch::uniform_integer_distribution<Integer> m_dist; + Integer m_current_number; +public: + RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ): + m_rng(seed), + m_dist(a, b) { + static_cast<void>(next()); + } + + Integer const& get() const override { + return m_current_number; + } + bool next() override { + m_current_number = m_dist(m_rng); + return true; + } +}; + +template <typename T> +std::enable_if_t<std::is_integral<T>::value, GeneratorWrapper<T>> +random(T a, T b) { + return GeneratorWrapper<T>( + Catch::Detail::make_unique<RandomIntegerGenerator<T>>(a, b, Detail::getSeed()) + ); +} + +template <typename T> +std::enable_if_t<std::is_floating_point<T>::value, +GeneratorWrapper<T>> +random(T a, T b) { + return GeneratorWrapper<T>( + Catch::Detail::make_unique<RandomFloatingGenerator<T>>(a, b, Detail::getSeed()) + ); +} + + +} // namespace Generators +} // namespace Catch + + +#endif // CATCH_GENERATORS_RANDOM_HPP_INCLUDED diff --git a/src/catch2/generators/catch_generators_range.hpp b/src/catch2/generators/catch_generators_range.hpp new file mode 100644 index 0000000..55d673c --- /dev/null +++ b/src/catch2/generators/catch_generators_range.hpp @@ -0,0 +1,111 @@ + +// 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 +#ifndef CATCH_GENERATORS_RANGE_HPP_INCLUDED +#define CATCH_GENERATORS_RANGE_HPP_INCLUDED + +#include <catch2/generators/catch_generators.hpp> + +#include <iterator> +#include <type_traits> + +namespace Catch { +namespace Generators { + + +template <typename T> +class RangeGenerator final : public IGenerator<T> { + T m_current; + T m_end; + T m_step; + bool m_positive; + +public: + RangeGenerator(T const& start, T const& end, T const& step): + m_current(start), + m_end(end), + m_step(step), + m_positive(m_step > T(0)) + { + assert(m_current != m_end && "Range start and end cannot be equal"); + assert(m_step != T(0) && "Step size cannot be zero"); + assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end"); + } + + RangeGenerator(T const& start, T const& end): + RangeGenerator(start, end, (start < end) ? T(1) : T(-1)) + {} + + T const& get() const override { + return m_current; + } + + bool next() override { + m_current += m_step; + return (m_positive) ? (m_current < m_end) : (m_current > m_end); + } +}; + +template <typename T> +GeneratorWrapper<T> range(T const& start, T const& end, T const& step) { + static_assert(std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, "Type must be numeric"); + return GeneratorWrapper<T>(Catch::Detail::make_unique<RangeGenerator<T>>(start, end, step)); +} + +template <typename T> +GeneratorWrapper<T> range(T const& start, T const& end) { + static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer"); + return GeneratorWrapper<T>(Catch::Detail::make_unique<RangeGenerator<T>>(start, end)); +} + + +template <typename T> +class IteratorGenerator final : public IGenerator<T> { + static_assert(!std::is_same<T, bool>::value, + "IteratorGenerator currently does not support bools" + "because of std::vector<bool> specialization"); + + std::vector<T> m_elems; + size_t m_current = 0; +public: + template <typename InputIterator, typename InputSentinel> + IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) { + if (m_elems.empty()) { + Detail::throw_generator_exception("IteratorGenerator received no valid values"); + } + } + + T const& get() const override { + return m_elems[m_current]; + } + + bool next() override { + ++m_current; + return m_current != m_elems.size(); + } +}; + +template <typename InputIterator, + typename InputSentinel, + typename ResultType = std::remove_const_t<typename std::iterator_traits<InputIterator>::value_type>> +GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) { + return GeneratorWrapper<ResultType>(Catch::Detail::make_unique<IteratorGenerator<ResultType>>(from, to)); +} + +template <typename Container> +auto from_range(Container const& cnt) { + using std::begin; + using std::end; + return from_range( begin( cnt ), end( cnt ) ); +} + + +} // namespace Generators +} // namespace Catch + + +#endif // CATCH_GENERATORS_RANGE_HPP_INCLUDED |
