mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-11-04 00:51:52 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			214 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
 *  Created by Phil Nash on 15/6/2018.
 | 
						|
 *
 | 
						|
 *  Distributed under the Boost Software License, Version 1.0. (See accompanying
 | 
						|
 *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | 
						|
 */
 | 
						|
#ifndef TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
 | 
						|
#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
 | 
						|
 | 
						|
#include "catch_interfaces_generatortracker.h"
 | 
						|
#include "catch_common.h"
 | 
						|
#include "catch_enforce.h"
 | 
						|
 | 
						|
#include <memory>
 | 
						|
#include <vector>
 | 
						|
#include <cassert>
 | 
						|
 | 
						|
#include <utility>
 | 
						|
#include <exception>
 | 
						|
 | 
						|
namespace Catch {
 | 
						|
 | 
						|
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;
 | 
						|
};
 | 
						|
 | 
						|
namespace Generators {
 | 
						|
 | 
						|
    // !TBD move this into its own location?
 | 
						|
    namespace pf{
 | 
						|
        template<typename T, typename... Args>
 | 
						|
        std::unique_ptr<T> make_unique( Args&&... args ) {
 | 
						|
            return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    template<typename T>
 | 
						|
    struct IGenerator : GeneratorUntypedBase {
 | 
						|
        virtual ~IGenerator() = 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<typename T>
 | 
						|
    class SingleValueGenerator final : public IGenerator<T> {
 | 
						|
        T m_value;
 | 
						|
    public:
 | 
						|
        SingleValueGenerator(T const& value) : m_value( value ) {}
 | 
						|
        SingleValueGenerator(T&& value) : m_value(std::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,
 | 
						|
            "ValuesGenerator 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>
 | 
						|
    class GeneratorWrapper final {
 | 
						|
        std::unique_ptr<IGenerator<T>> m_generator;
 | 
						|
    public:
 | 
						|
        GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator):
 | 
						|
            m_generator(std::move(generator))
 | 
						|
        {}
 | 
						|
        T const& get() const {
 | 
						|
            return m_generator->get();
 | 
						|
        }
 | 
						|
        bool next() {
 | 
						|
            return m_generator->next();
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
    template <typename T>
 | 
						|
    GeneratorWrapper<T> value(T&& value) {
 | 
						|
        return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value)));
 | 
						|
    }
 | 
						|
    template <typename T>
 | 
						|
    GeneratorWrapper<T> values(std::initializer_list<T> values) {
 | 
						|
        return GeneratorWrapper<T>(pf::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 populate(GeneratorWrapper<T>&& generator) {
 | 
						|
            m_generators.emplace_back(std::move(generator));
 | 
						|
        }
 | 
						|
        void populate(T&& val) {
 | 
						|
            m_generators.emplace_back(value(std::move(val)));
 | 
						|
        }
 | 
						|
        template<typename U>
 | 
						|
        void populate(U&& val) {
 | 
						|
            populate(T(std::move(val)));
 | 
						|
        }
 | 
						|
        template<typename U, typename... Gs>
 | 
						|
        void populate(U&& valueOrGenerator, Gs... moreGenerators) {
 | 
						|
            populate(std::forward<U>(valueOrGenerator));
 | 
						|
            populate(std::forward<Gs>(moreGenerators)...);
 | 
						|
        }
 | 
						|
 | 
						|
    public:
 | 
						|
        template <typename... Gs>
 | 
						|
        Generators(Gs... moreGenerators) {
 | 
						|
            m_generators.reserve(sizeof...(Gs));
 | 
						|
            populate(std::forward<Gs>(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<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> 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>(std::move(generator), std::forward<Gs>(moreGenerators)...);
 | 
						|
    }
 | 
						|
    template<typename T>
 | 
						|
    auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
 | 
						|
        return Generators<T>(std::move(generator));
 | 
						|
    }
 | 
						|
    template<typename T, typename... Gs>
 | 
						|
    auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> {
 | 
						|
        return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
 | 
						|
    }
 | 
						|
    template<typename T, typename U, typename... Gs>
 | 
						|
    auto makeGenerators( as<T>, U&& val, Gs... moreGenerators ) -> Generators<T> {
 | 
						|
        return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
 | 
						|
    }
 | 
						|
 | 
						|
    auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
 | 
						|
 | 
						|
    template<typename L>
 | 
						|
    // Note: The type after -> is weird, because VS2015 cannot parse
 | 
						|
    //       the expression used in the typedef inside, when it is in
 | 
						|
    //       return type. Yeah.
 | 
						|
    auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) {
 | 
						|
        using UnderlyingType = typename decltype(generatorExpression())::type;
 | 
						|
 | 
						|
        IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo );
 | 
						|
        if (!tracker.hasGenerator()) {
 | 
						|
            tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression()));
 | 
						|
        }
 | 
						|
 | 
						|
        auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() );
 | 
						|
        return generator.get();
 | 
						|
    }
 | 
						|
 | 
						|
} // namespace Generators
 | 
						|
} // namespace Catch
 | 
						|
 | 
						|
#define GENERATE( ... ) \
 | 
						|
    Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
 | 
						|
#define GENERATE_COPY( ... ) \
 | 
						|
    Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
 | 
						|
#define GENERATE_REF( ... ) \
 | 
						|
    Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
 | 
						|
 | 
						|
#endif // TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
 |