1 /*
2  *  Created by Martin on 23/2/2019.
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_GENERIC_HPP_INCLUDED
8 #define TWOBLUECUBES_CATCH_GENERATORS_GENERIC_HPP_INCLUDED
9 
10 #include "catch_generators.hpp"
11 #include "catch_meta.hpp"
12 
13 namespace Catch {
14 namespace Generators {
15 
16     template <typename T>
17     class TakeGenerator : public IGenerator<T> {
18         GeneratorWrapper<T> m_generator;
19         size_t m_returned = 0;
20         size_t m_target;
21     public:
TakeGenerator(size_t target,GeneratorWrapper<T> && generator)22         TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
23             m_generator(std::move(generator)),
24             m_target(target)
25         {
26             assert(target != 0 && "Empty generators are not allowed");
27         }
get() const28         T const& get() const override {
29             return m_generator.get();
30         }
next()31         bool next() override {
32             ++m_returned;
33             if (m_returned >= m_target) {
34                 return false;
35             }
36 
37             const auto success = m_generator.next();
38             // If the underlying generator does not contain enough values
39             // then we cut short as well
40             if (!success) {
41                 m_returned = m_target;
42             }
43             return success;
44         }
45     };
46 
47     template <typename T>
take(size_t target,GeneratorWrapper<T> && generator)48     GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
49         return GeneratorWrapper<T>(pf::make_unique<TakeGenerator<T>>(target, std::move(generator)));
50     }
51 
52 
53     template <typename T, typename Predicate>
54     class FilterGenerator : public IGenerator<T> {
55         GeneratorWrapper<T> m_generator;
56         Predicate m_predicate;
57     public:
58         template <typename P = Predicate>
FilterGenerator(P && pred,GeneratorWrapper<T> && generator)59         FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
60             m_generator(std::move(generator)),
61             m_predicate(std::forward<P>(pred))
62         {
63             if (!m_predicate(m_generator.get())) {
64                 // It might happen that there are no values that pass the
65                 // filter. In that case we throw an exception.
66                 auto has_initial_value = next();
67                 if (!has_initial_value) {
68                     Catch::throw_exception(GeneratorException("No valid value found in filtered generator"));
69                 }
70             }
71         }
72 
get() const73         T const& get() const override {
74             return m_generator.get();
75         }
76 
next()77         bool next() override {
78             bool success = m_generator.next();
79             if (!success) {
80                 return false;
81             }
82             while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
83             return success;
84         }
85     };
86 
87 
88     template <typename T, typename Predicate>
filter(Predicate && pred,GeneratorWrapper<T> && generator)89     GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
90         return GeneratorWrapper<T>(std::unique_ptr<IGenerator<T>>(pf::make_unique<FilterGenerator<T, Predicate>>(std::forward<Predicate>(pred), std::move(generator))));
91     }
92 
93     template <typename T>
94     class RepeatGenerator : public IGenerator<T> {
95         static_assert(!std::is_same<T, bool>::value,
96             "RepeatGenerator currently does not support bools"
97             "because of std::vector<bool> specialization");
98         GeneratorWrapper<T> m_generator;
99         mutable std::vector<T> m_returned;
100         size_t m_target_repeats;
101         size_t m_current_repeat = 0;
102         size_t m_repeat_index = 0;
103     public:
RepeatGenerator(size_t repeats,GeneratorWrapper<T> && generator)104         RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator):
105             m_generator(std::move(generator)),
106             m_target_repeats(repeats)
107         {
108             assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
109         }
110 
get() const111         T const& get() const override {
112             if (m_current_repeat == 0) {
113                 m_returned.push_back(m_generator.get());
114                 return m_returned.back();
115             }
116             return m_returned[m_repeat_index];
117         }
118 
next()119         bool next() override {
120             // There are 2 basic cases:
121             // 1) We are still reading the generator
122             // 2) We are reading our own cache
123 
124             // In the first case, we need to poke the underlying generator.
125             // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
126             if (m_current_repeat == 0) {
127                 const auto success = m_generator.next();
128                 if (!success) {
129                     ++m_current_repeat;
130                 }
131                 return m_current_repeat < m_target_repeats;
132             }
133 
134             // In the second case, we need to move indices forward and check that we haven't run up against the end
135             ++m_repeat_index;
136             if (m_repeat_index == m_returned.size()) {
137                 m_repeat_index = 0;
138                 ++m_current_repeat;
139             }
140             return m_current_repeat < m_target_repeats;
141         }
142     };
143 
144     template <typename T>
repeat(size_t repeats,GeneratorWrapper<T> && generator)145     GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
146         return GeneratorWrapper<T>(pf::make_unique<RepeatGenerator<T>>(repeats, std::move(generator)));
147     }
148 
149     template <typename T, typename U, typename Func>
150     class MapGenerator : public IGenerator<T> {
151         // TBD: provide static assert for mapping function, for friendly error message
152         GeneratorWrapper<U> m_generator;
153         Func m_function;
154         // To avoid returning dangling reference, we have to save the values
155         T m_cache;
156     public:
157         template <typename F2 = Func>
MapGenerator(F2 && function,GeneratorWrapper<U> && generator)158         MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
159             m_generator(std::move(generator)),
160             m_function(std::forward<F2>(function)),
161             m_cache(m_function(m_generator.get()))
162         {}
163 
get() const164         T const& get() const override {
165             return m_cache;
166         }
next()167         bool next() override {
168             const auto success = m_generator.next();
169             if (success) {
170                 m_cache = m_function(m_generator.get());
171             }
172             return success;
173         }
174     };
175 
176     template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
map(Func && function,GeneratorWrapper<U> && generator)177     GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
178         return GeneratorWrapper<T>(
179             pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
180         );
181     }
182 
183     template <typename T, typename U, typename Func>
map(Func && function,GeneratorWrapper<U> && generator)184     GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
185         return GeneratorWrapper<T>(
186             pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
187         );
188     }
189 
190     template <typename T>
191     class ChunkGenerator final : public IGenerator<std::vector<T>> {
192         std::vector<T> m_chunk;
193         size_t m_chunk_size;
194         GeneratorWrapper<T> m_generator;
195         bool m_used_up = false;
196     public:
ChunkGenerator(size_t size,GeneratorWrapper<T> generator)197         ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
198             m_chunk_size(size), m_generator(std::move(generator))
199         {
200             m_chunk.reserve(m_chunk_size);
201             if (m_chunk_size != 0) {
202                 m_chunk.push_back(m_generator.get());
203                 for (size_t i = 1; i < m_chunk_size; ++i) {
204                     if (!m_generator.next()) {
205                         Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk"));
206                     }
207                     m_chunk.push_back(m_generator.get());
208                 }
209             }
210         }
get() const211         std::vector<T> const& get() const override {
212             return m_chunk;
213         }
next()214         bool next() override {
215             m_chunk.clear();
216             for (size_t idx = 0; idx < m_chunk_size; ++idx) {
217                 if (!m_generator.next()) {
218                     return false;
219                 }
220                 m_chunk.push_back(m_generator.get());
221             }
222             return true;
223         }
224     };
225 
226     template <typename T>
chunk(size_t size,GeneratorWrapper<T> && generator)227     GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) {
228         return GeneratorWrapper<std::vector<T>>(
229             pf::make_unique<ChunkGenerator<T>>(size, std::move(generator))
230         );
231     }
232 
233 } // namespace Generators
234 } // namespace Catch
235 
236 
237 #endif // TWOBLUECUBES_CATCH_GENERATORS_GENERIC_HPP_INCLUDED
238