| | |
| | |
| | |
| | |
| | |
| | |
| | #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 (!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())) { |
| | |
| | |
| | auto has_initial_value = nextImpl(); |
| | 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 { |
| | return nextImpl(); |
| | } |
| |
|
| | private: |
| | bool nextImpl() { |
| | 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 { |
| | |
| | |
| | |
| |
|
| | |
| | |
| | if (m_current_repeat == 0) { |
| | const auto success = m_generator.next(); |
| | if (!success) { |
| | ++m_current_repeat; |
| | } |
| | return m_current_repeat < m_target_repeats; |
| | } |
| |
|
| | |
| | ++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> { |
| | |
| | GeneratorWrapper<U> m_generator; |
| | Func m_function; |
| | |
| | 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)) |
| | ); |
| | } |
| |
|
| | } |
| | } |
| |
|
| |
|
| | #endif |
| |
|