123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- /*
- * Created by Martin on 23/2/2019.
- *
- * Distributed under the Boost Software License, Version 1.0. (See accompanying
- * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- */
- #ifndef TWOBLUECUBES_CATCH_GENERATORS_GENERIC_HPP_INCLUDED
- #define TWOBLUECUBES_CATCH_GENERATORS_GENERIC_HPP_INCLUDED
- #include "catch_generators.hpp"
- #include "catch_meta.hpp"
- namespace Catch {
- namespace Generators {
- template <typename T>
- class TakeGenerator : public IGenerator<T> {
- GeneratorWrapper<T> m_generator;
- size_t m_returned = 0;
- size_t m_target;
- public:
- TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
- m_generator(std::move(generator)),
- m_target(target)
- {
- assert(target != 0 && "Empty generators are not allowed");
- }
- T const& get() const override {
- return m_generator.get();
- }
- bool next() override {
- ++m_returned;
- if (m_returned >= m_target) {
- return false;
- }
- const auto success = m_generator.next();
- // If the underlying generator does not contain enough values
- // then we cut short as well
- if (!success) {
- m_returned = m_target;
- }
- return success;
- }
- };
- template <typename T>
- GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
- return GeneratorWrapper<T>(pf::make_unique<TakeGenerator<T>>(target, std::move(generator)));
- }
- template <typename T, typename Predicate>
- class FilterGenerator : public IGenerator<T> {
- GeneratorWrapper<T> m_generator;
- Predicate m_predicate;
- public:
- template <typename P = Predicate>
- FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
- m_generator(std::move(generator)),
- m_predicate(std::forward<P>(pred))
- {
- if (!m_predicate(m_generator.get())) {
- // It might happen that there are no values that pass the
- // filter. In that case we throw an exception.
- auto has_initial_value = next();
- if (!has_initial_value) {
- Catch::throw_exception(GeneratorException("No valid value found in filtered generator"));
- }
- }
- }
- T const& get() const override {
- return m_generator.get();
- }
- bool next() override {
- bool success = m_generator.next();
- if (!success) {
- return false;
- }
- while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
- return success;
- }
- };
- template <typename T, typename Predicate>
- GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
- return GeneratorWrapper<T>(std::unique_ptr<IGenerator<T>>(pf::make_unique<FilterGenerator<T, Predicate>>(std::forward<Predicate>(pred), std::move(generator))));
- }
- template <typename T>
- class RepeatGenerator : public IGenerator<T> {
- static_assert(!std::is_same<T, bool>::value,
- "RepeatGenerator currently does not support bools"
- "because of std::vector<bool> specialization");
- GeneratorWrapper<T> m_generator;
- mutable std::vector<T> m_returned;
- size_t m_target_repeats;
- size_t m_current_repeat = 0;
- size_t m_repeat_index = 0;
- public:
- RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator):
- m_generator(std::move(generator)),
- m_target_repeats(repeats)
- {
- assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
- }
- T const& get() const override {
- if (m_current_repeat == 0) {
- m_returned.push_back(m_generator.get());
- return m_returned.back();
- }
- return m_returned[m_repeat_index];
- }
- bool next() override {
- // There are 2 basic cases:
- // 1) We are still reading the generator
- // 2) We are reading our own cache
- // In the first case, we need to poke the underlying generator.
- // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
- if (m_current_repeat == 0) {
- const auto success = m_generator.next();
- if (!success) {
- ++m_current_repeat;
- }
- return m_current_repeat < m_target_repeats;
- }
- // In the second case, we need to move indices forward and check that we haven't run up against the end
- ++m_repeat_index;
- if (m_repeat_index == m_returned.size()) {
- m_repeat_index = 0;
- ++m_current_repeat;
- }
- return m_current_repeat < m_target_repeats;
- }
- };
- template <typename T>
- GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
- return GeneratorWrapper<T>(pf::make_unique<RepeatGenerator<T>>(repeats, std::move(generator)));
- }
- template <typename T, typename U, typename Func>
- class MapGenerator : public IGenerator<T> {
- // TBD: provide static assert for mapping function, for friendly error message
- GeneratorWrapper<U> m_generator;
- Func m_function;
- // To avoid returning dangling reference, we have to save the values
- T m_cache;
- public:
- template <typename F2 = Func>
- MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
- m_generator(std::move(generator)),
- m_function(std::forward<F2>(function)),
- m_cache(m_function(m_generator.get()))
- {}
- T const& get() const override {
- return m_cache;
- }
- bool next() override {
- const auto success = m_generator.next();
- if (success) {
- m_cache = m_function(m_generator.get());
- }
- return success;
- }
- };
- template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
- GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
- return GeneratorWrapper<T>(
- pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
- );
- }
- template <typename T, typename U, typename Func>
- GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
- return GeneratorWrapper<T>(
- pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
- );
- }
- template <typename T>
- class ChunkGenerator final : public IGenerator<std::vector<T>> {
- std::vector<T> m_chunk;
- size_t m_chunk_size;
- GeneratorWrapper<T> m_generator;
- bool m_used_up = false;
- public:
- ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
- m_chunk_size(size), m_generator(std::move(generator))
- {
- m_chunk.reserve(m_chunk_size);
- if (m_chunk_size != 0) {
- m_chunk.push_back(m_generator.get());
- for (size_t i = 1; i < m_chunk_size; ++i) {
- if (!m_generator.next()) {
- Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk"));
- }
- m_chunk.push_back(m_generator.get());
- }
- }
- }
- std::vector<T> const& get() const override {
- return m_chunk;
- }
- bool next() override {
- m_chunk.clear();
- for (size_t idx = 0; idx < m_chunk_size; ++idx) {
- if (!m_generator.next()) {
- return false;
- }
- m_chunk.push_back(m_generator.get());
- }
- return true;
- }
- };
- template <typename T>
- GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) {
- return GeneratorWrapper<std::vector<T>>(
- pf::make_unique<ChunkGenerator<T>>(size, std::move(generator))
- );
- }
- } // namespace Generators
- } // namespace Catch
- #endif // TWOBLUECUBES_CATCH_GENERATORS_GENERIC_HPP_INCLUDED
|