// 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 #include #include #include #include #include #include #include #include namespace Catch { namespace Generators { namespace Detail { //! Throws GeneratorException with the provided message [[noreturn]] void throw_generator_exception(char const * msg); } // end namespace detail template class IGenerator : public GeneratorUntypedBase { std::string stringifyImpl() const override { return ::Catch::Detail::stringify( get() ); } public: ~IGenerator() override = default; IGenerator() = default; IGenerator(IGenerator const&) = default; IGenerator& operator=(IGenerator const&) = default; // 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 using GeneratorPtr = Catch::Detail::unique_ptr>; template class GeneratorWrapper final { GeneratorPtr m_generator; public: //! Takes ownership of the passed pointer. GeneratorWrapper(IGenerator* generator): m_generator(generator) {} GeneratorWrapper(GeneratorPtr generator): m_generator(CATCH_MOVE(generator)) {} T const& get() const { return m_generator->get(); } bool next() { return m_generator->countedNext(); } }; template class SingleValueGenerator final : public IGenerator { 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 class FixedValuesGenerator final : public IGenerator { static_assert(!std::is_same::value, "FixedValuesGenerator does not support bools because of std::vector" "specialization, use SingleValue Generator instead."); std::vector m_values; size_t m_idx = 0; public: FixedValuesGenerator( std::initializer_list 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 > GeneratorWrapper value( T&& value ) { return GeneratorWrapper( Catch::Detail::make_unique>( CATCH_FORWARD( value ) ) ); } template GeneratorWrapper values(std::initializer_list values) { return GeneratorWrapper(Catch::Detail::make_unique>(values)); } template class Generators : public IGenerator { std::vector> m_generators; size_t m_current = 0; void add_generator( GeneratorWrapper&& 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 std::enable_if_t, T>::value> add_generator( U&& val ) { add_generator( T( CATCH_FORWARD( val ) ) ); } template void add_generators( U&& valueOrGenerator ) { add_generator( CATCH_FORWARD( valueOrGenerator ) ); } template void add_generators( U&& valueOrGenerator, Gs&&... moreGenerators ) { add_generator( CATCH_FORWARD( valueOrGenerator ) ); add_generators( CATCH_FORWARD( moreGenerators )... ); } public: template 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 GeneratorWrapper...>> table( std::initializer_list...>> tuples ) { return values>( tuples ); } // Tag type to signal that a generator sequence should convert arguments to a specific type template struct as {}; template auto makeGenerators( GeneratorWrapper&& generator, Gs &&... moreGenerators ) -> Generators { return Generators(CATCH_MOVE(generator), CATCH_FORWARD(moreGenerators)...); } template auto makeGenerators( GeneratorWrapper&& generator ) -> Generators { return Generators(CATCH_MOVE(generator)); } template auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators> { return makeGenerators( value( CATCH_FORWARD( val ) ), CATCH_FORWARD( moreGenerators )... ); } template auto makeGenerators( as, U&& val, Gs &&... moreGenerators ) -> Generators { return makeGenerators( value( T( CATCH_FORWARD( val ) ) ), CATCH_FORWARD( moreGenerators )... ); } auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; template 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 ); if (!tracker.hasGenerator()) { tracker.setGenerator(Catch::Detail::make_unique>(generatorExpression())); } auto const& generator = static_cast const&>( *tracker.getGenerator() ); return generator.get(); } } // namespace Generators } // namespace Catch #define GENERATE( ... ) \ Catch::Generators::generate( INTERNAL_CATCH_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( INTERNAL_CATCH_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( INTERNAL_CATCH_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