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 #include "catch_stringref.h" 14 15 #include <memory> 16 #include <vector> 17 #include <cassert> 18 19 #include <utility> 20 #include <exception> 21 22 namespace Catch { 23 24 class GeneratorException : public std::exception { 25 const char* const m_msg = ""; 26 27 public: GeneratorException(const char * msg)28 GeneratorException(const char* msg): 29 m_msg(msg) 30 {} 31 32 const char* what() const noexcept override final; 33 }; 34 35 namespace Generators { 36 37 // !TBD move this into its own location? 38 namespace pf{ 39 template<typename T, typename... Args> make_unique(Args &&...args)40 std::unique_ptr<T> make_unique( Args&&... args ) { 41 return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); 42 } 43 } 44 45 template<typename T> 46 struct IGenerator : GeneratorUntypedBase { 47 virtual ~IGenerator() = default; 48 49 // Returns the current element of the generator 50 // 51 // \Precondition The generator is either freshly constructed, 52 // or the last call to `next()` returned true 53 virtual T const& get() const = 0; 54 using type = T; 55 }; 56 57 template<typename T> 58 class SingleValueGenerator final : public IGenerator<T> { 59 T m_value; 60 public: 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 static_assert(!std::is_same<T, bool>::value, 74 "FixedValuesGenerator does not support bools because of std::vector<bool>" 75 "specialization, use SingleValue Generator instead."); 76 std::vector<T> m_values; 77 size_t m_idx = 0; 78 public: FixedValuesGenerator(std::initializer_list<T> values)79 FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {} 80 get() const81 T const& get() const override { 82 return m_values[m_idx]; 83 } next()84 bool next() override { 85 ++m_idx; 86 return m_idx < m_values.size(); 87 } 88 }; 89 90 template <typename T> 91 class GeneratorWrapper final { 92 std::unique_ptr<IGenerator<T>> m_generator; 93 public: GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator)94 GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator): 95 m_generator(std::move(generator)) 96 {} get() const97 T const& get() const { 98 return m_generator->get(); 99 } next()100 bool next() { 101 return m_generator->next(); 102 } 103 }; 104 105 template <typename T> value(T && value)106 GeneratorWrapper<T> value(T&& value) { 107 return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value))); 108 } 109 template <typename T> values(std::initializer_list<T> values)110 GeneratorWrapper<T> values(std::initializer_list<T> values) { 111 return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values)); 112 } 113 114 template<typename T> 115 class Generators : public IGenerator<T> { 116 std::vector<GeneratorWrapper<T>> m_generators; 117 size_t m_current = 0; 118 populate(GeneratorWrapper<T> && generator)119 void populate(GeneratorWrapper<T>&& generator) { 120 m_generators.emplace_back(std::move(generator)); 121 } populate(T && val)122 void populate(T&& val) { 123 m_generators.emplace_back(value(std::forward<T>(val))); 124 } 125 template<typename U> populate(U && val)126 void populate(U&& val) { 127 populate(T(std::forward<U>(val))); 128 } 129 template<typename U, typename... Gs> populate(U && valueOrGenerator,Gs &&...moreGenerators)130 void populate(U&& valueOrGenerator, Gs &&... moreGenerators) { 131 populate(std::forward<U>(valueOrGenerator)); 132 populate(std::forward<Gs>(moreGenerators)...); 133 } 134 135 public: 136 template <typename... Gs> Generators(Gs &&...moreGenerators)137 Generators(Gs &&... moreGenerators) { 138 m_generators.reserve(sizeof...(Gs)); 139 populate(std::forward<Gs>(moreGenerators)...); 140 } 141 get() const142 T const& get() const override { 143 return m_generators[m_current].get(); 144 } 145 next()146 bool next() override { 147 if (m_current >= m_generators.size()) { 148 return false; 149 } 150 const bool current_status = m_generators[m_current].next(); 151 if (!current_status) { 152 ++m_current; 153 } 154 return m_current < m_generators.size(); 155 } 156 }; 157 158 159 template<typename... Ts> table(std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples)160 GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) { 161 return values<std::tuple<Ts...>>( tuples ); 162 } 163 164 // Tag type to signal that a generator sequence should convert arguments to a specific type 165 template <typename T> 166 struct as {}; 167 168 template<typename T, typename... Gs> makeGenerators(GeneratorWrapper<T> && generator,Gs &&...moreGenerators)169 auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> { 170 return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...); 171 } 172 template<typename T> makeGenerators(GeneratorWrapper<T> && generator)173 auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> { 174 return Generators<T>(std::move(generator)); 175 } 176 template<typename T, typename... Gs> makeGenerators(T && val,Gs &&...moreGenerators)177 auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<T> { 178 return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... ); 179 } 180 template<typename T, typename U, typename... Gs> makeGenerators(as<T>,U && val,Gs &&...moreGenerators)181 auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> { 182 return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... ); 183 } 184 185 auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; 186 187 template<typename L> 188 // Note: The type after -> is weird, because VS2015 cannot parse 189 // the expression used in the typedef inside, when it is in 190 // return type. Yeah. generate(StringRef generatorName,SourceLineInfo const & lineInfo,L const & generatorExpression)191 auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) { 192 using UnderlyingType = typename decltype(generatorExpression())::type; 193 194 IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo ); 195 if (!tracker.hasGenerator()) { 196 tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression())); 197 } 198 199 auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() ); 200 return generator.get(); 201 } 202 203 } // namespace Generators 204 } // namespace Catch 205 206 #define GENERATE( ... ) \ 207 Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ 208 CATCH_INTERNAL_LINEINFO, \ 209 [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) 210 #define GENERATE_COPY( ... ) \ 211 Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ 212 CATCH_INTERNAL_LINEINFO, \ 213 [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) 214 #define GENERATE_REF( ... ) \ 215 Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ 216 CATCH_INTERNAL_LINEINFO, \ 217 [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) 218 219 #endif // TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED 220