• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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