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