1 /* 2 * Created by Phil Nash on 15/6/2018. 3 * 4 * Distributed under the Boost Software License, Version 1.0. (See accompanying 5 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 */ 7 #ifndef TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED 8 #define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED 9 10 #include "catch_interfaces_generatortracker.h" 11 #include "catch_common.h" 12 #include "catch_enforce.h" 13 14 #include <memory> 15 #include <vector> 16 #include <cassert> 17 18 #include <utility> 19 #include <exception> 20 21 namespace Catch { 22 23 class GeneratorException : public std::exception { 24 const char* const m_msg = ""; 25 26 public: GeneratorException(const char * msg)27 GeneratorException(const char* msg): 28 m_msg(msg) 29 {} 30 31 const char* what() const noexcept override final; 32 }; 33 34 namespace Generators { 35 36 // !TBD move this into its own location? 37 namespace pf{ 38 template<typename T, typename... Args> make_unique(Args &&...args)39 std::unique_ptr<T> make_unique( Args&&... args ) { 40 return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); 41 } 42 } 43 44 template<typename T> 45 struct IGenerator : GeneratorUntypedBase { 46 virtual ~IGenerator() = default; 47 48 // Returns the current element of the generator 49 // 50 // \Precondition The generator is either freshly constructed, 51 // or the last call to `next()` returned true 52 virtual T const& get() const = 0; 53 using type = T; 54 }; 55 56 template<typename T> 57 class SingleValueGenerator final : public IGenerator<T> { 58 T m_value; 59 public: SingleValueGenerator(T const & value)60 SingleValueGenerator(T const& value) : m_value( value ) {} SingleValueGenerator(T && value)61 SingleValueGenerator(T&& value) : m_value(std::move(value)) {} 62 get() const63 T const& get() const override { 64 return m_value; 65 } next()66 bool next() override { 67 return false; 68 } 69 }; 70 71 template<typename T> 72 class FixedValuesGenerator final : public IGenerator<T> { 73 std::vector<T> m_values; 74 size_t m_idx = 0; 75 public: FixedValuesGenerator(std::initializer_list<T> values)76 FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {} 77 get() const78 T const& get() const override { 79 return m_values[m_idx]; 80 } next()81 bool next() override { 82 ++m_idx; 83 return m_idx < m_values.size(); 84 } 85 }; 86 87 template <typename T> 88 class GeneratorWrapper final { 89 std::unique_ptr<IGenerator<T>> m_generator; 90 public: GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator)91 GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator): 92 m_generator(std::move(generator)) 93 {} get() const94 T const& get() const { 95 return m_generator->get(); 96 } next()97 bool next() { 98 return m_generator->next(); 99 } 100 }; 101 102 template <typename T> value(T && value)103 GeneratorWrapper<T> value(T&& value) { 104 return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value))); 105 } 106 template <typename T> values(std::initializer_list<T> values)107 GeneratorWrapper<T> values(std::initializer_list<T> values) { 108 return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values)); 109 } 110 111 template<typename T> 112 class Generators : public IGenerator<T> { 113 std::vector<GeneratorWrapper<T>> m_generators; 114 size_t m_current = 0; 115 populate(GeneratorWrapper<T> && generator)116 void populate(GeneratorWrapper<T>&& generator) { 117 m_generators.emplace_back(std::move(generator)); 118 } populate(T && val)119 void populate(T&& val) { 120 m_generators.emplace_back(value(std::move(val))); 121 } 122 template<typename U> populate(U && val)123 void populate(U&& val) { 124 populate(T(std::move(val))); 125 } 126 template<typename U, typename... Gs> populate(U && valueOrGenerator,Gs...moreGenerators)127 void populate(U&& valueOrGenerator, Gs... moreGenerators) { 128 populate(std::forward<U>(valueOrGenerator)); 129 populate(std::forward<Gs>(moreGenerators)...); 130 } 131 132 public: 133 template <typename... Gs> Generators(Gs...moreGenerators)134 Generators(Gs... moreGenerators) { 135 m_generators.reserve(sizeof...(Gs)); 136 populate(std::forward<Gs>(moreGenerators)...); 137 } 138 get() const139 T const& get() const override { 140 return m_generators[m_current].get(); 141 } 142 next()143 bool next() override { 144 if (m_current >= m_generators.size()) { 145 return false; 146 } 147 const bool current_status = m_generators[m_current].next(); 148 if (!current_status) { 149 ++m_current; 150 } 151 return m_current < m_generators.size(); 152 } 153 }; 154 155 156 template<typename... Ts> table(std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples)157 GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) { 158 return values<std::tuple<Ts...>>( tuples ); 159 } 160 161 // Tag type to signal that a generator sequence should convert arguments to a specific type 162 template <typename T> 163 struct as {}; 164 165 template<typename T, typename... Gs> makeGenerators(GeneratorWrapper<T> && generator,Gs...moreGenerators)166 auto makeGenerators( GeneratorWrapper<T>&& generator, Gs... moreGenerators ) -> Generators<T> { 167 return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...); 168 } 169 template<typename T> makeGenerators(GeneratorWrapper<T> && generator)170 auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> { 171 return Generators<T>(std::move(generator)); 172 } 173 template<typename T, typename... Gs> makeGenerators(T && val,Gs...moreGenerators)174 auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> { 175 return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... ); 176 } 177 template<typename T, typename U, typename... Gs> makeGenerators(as<T>,U && val,Gs...moreGenerators)178 auto makeGenerators( as<T>, U&& val, Gs... moreGenerators ) -> Generators<T> { 179 return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... ); 180 } 181 182 auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; 183 184 template<typename L> 185 // Note: The type after -> is weird, because VS2015 cannot parse 186 // the expression used in the typedef inside, when it is in 187 // return type. Yeah. generate(SourceLineInfo const & lineInfo,L const & generatorExpression)188 auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) { 189 using UnderlyingType = typename decltype(generatorExpression())::type; 190 191 IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); 192 if (!tracker.hasGenerator()) { 193 tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression())); 194 } 195 196 auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() ); 197 return generator.get(); 198 } 199 200 } // namespace Generators 201 } // namespace Catch 202 203 #define GENERATE( ... ) \ 204 Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) 205 206 207 #endif // TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED 208