| /* |
| * 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&& 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, |
| "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> |
| 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::forward<T>(val))); |
| } |
| template<typename U> |
| void populate(U&& val) { |
| populate(T(std::forward<U>(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__ ); } ) //NOLINT(google-build-using-namespace) |
| #define GENERATE_COPY( ... ) \ |
| Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) |
| #define GENERATE_REF( ... ) \ |
| Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) |
| |
| #endif // TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED |