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 && value)60 SingleValueGenerator(T&& value) : m_value(std::move(value)) {} 61 get() const62 T const& get() const override { 63 return m_value; 64 } next()65 bool next() override { 66 return false; 67 } 68 }; 69 70 template<typename T> 71 class FixedValuesGenerator final : public IGenerator<T> { 72 static_assert(!std::is_same<T, bool>::value, 73 "FixedValuesGenerator does not support bools because of std::vector<bool>" 74 "specialization, use SingleValue Generator instead."); 75 std::vector<T> m_values; 76 size_t m_idx = 0; 77 public: FixedValuesGenerator(std::initializer_list<T> values)78 FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {} 79 get() const80 T const& get() const override { 81 return m_values[m_idx]; 82 } next()83 bool next() override { 84 ++m_idx; 85 return m_idx < m_values.size(); 86 } 87 }; 88 89 template <typename T> 90 class GeneratorWrapper final { 91 std::unique_ptr<IGenerator<T>> m_generator; 92 public: GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator)93 GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator): 94 m_generator(std::move(generator)) 95 {} get() const96 T const& get() const { 97 return m_generator->get(); 98 } next()99 bool next() { 100 return m_generator->next(); 101 } 102 }; 103 104 template <typename T> value(T && value)105 GeneratorWrapper<T> value(T&& value) { 106 return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value))); 107 } 108 template <typename T> values(std::initializer_list<T> values)109 GeneratorWrapper<T> values(std::initializer_list<T> values) { 110 return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values)); 111 } 112 113 template<typename T> 114 class Generators : public IGenerator<T> { 115 std::vector<GeneratorWrapper<T>> m_generators; 116 size_t m_current = 0; 117 populate(GeneratorWrapper<T> && generator)118 void populate(GeneratorWrapper<T>&& generator) { 119 m_generators.emplace_back(std::move(generator)); 120 } populate(T && val)121 void populate(T&& val) { 122 m_generators.emplace_back(value(std::forward<T>(val))); 123 } 124 template<typename U> populate(U && val)125 void populate(U&& val) { 126 populate(T(std::forward<U>(val))); 127 } 128 template<typename U, typename... Gs> populate(U && valueOrGenerator,Gs &&...moreGenerators)129 void populate(U&& valueOrGenerator, Gs &&... moreGenerators) { 130 populate(std::forward<U>(valueOrGenerator)); 131 populate(std::forward<Gs>(moreGenerators)...); 132 } 133 134 public: 135 template <typename... Gs> Generators(Gs &&...moreGenerators)136 Generators(Gs &&... moreGenerators) { 137 m_generators.reserve(sizeof...(Gs)); 138 populate(std::forward<Gs>(moreGenerators)...); 139 } 140 get() const141 T const& get() const override { 142 return m_generators[m_current].get(); 143 } 144 next()145 bool next() override { 146 if (m_current >= m_generators.size()) { 147 return false; 148 } 149 const bool current_status = m_generators[m_current].next(); 150 if (!current_status) { 151 ++m_current; 152 } 153 return m_current < m_generators.size(); 154 } 155 }; 156 157 158 template<typename... Ts> table(std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples)159 GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) { 160 return values<std::tuple<Ts...>>( tuples ); 161 } 162 163 // Tag type to signal that a generator sequence should convert arguments to a specific type 164 template <typename T> 165 struct as {}; 166 167 template<typename T, typename... Gs> makeGenerators(GeneratorWrapper<T> && generator,Gs &&...moreGenerators)168 auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> { 169 return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...); 170 } 171 template<typename T> makeGenerators(GeneratorWrapper<T> && generator)172 auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> { 173 return Generators<T>(std::move(generator)); 174 } 175 template<typename T, typename... Gs> makeGenerators(T && val,Gs &&...moreGenerators)176 auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<T> { 177 return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... ); 178 } 179 template<typename T, typename U, typename... Gs> makeGenerators(as<T>,U && val,Gs &&...moreGenerators)180 auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> { 181 return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... ); 182 } 183 184 auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; 185 186 template<typename L> 187 // Note: The type after -> is weird, because VS2015 cannot parse 188 // the expression used in the typedef inside, when it is in 189 // return type. Yeah. generate(SourceLineInfo const & lineInfo,L const & generatorExpression)190 auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) { 191 using UnderlyingType = typename decltype(generatorExpression())::type; 192 193 IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); 194 if (!tracker.hasGenerator()) { 195 tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression())); 196 } 197 198 auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() ); 199 return generator.get(); 200 } 201 202 } // namespace Generators 203 } // namespace Catch 204 205 #define GENERATE( ... ) \ 206 Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) 207 #define GENERATE_COPY( ... ) \ 208 Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) 209 #define GENERATE_REF( ... ) \ 210 Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) 211 212 #endif // TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED 213