catch_generators_generic.hpp 8.3 KB

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