1 /*
2 * Created by Martin on 15/6/2018.
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_SPECIFIC_HPP_INCLUDED
8 #define TWOBLUECUBES_CATCH_GENERATORS_SPECIFIC_HPP_INCLUDED
9
10 #include "catch_context.h"
11 #include "catch_generators.hpp"
12 #include "catch_interfaces_config.h"
13 #include "catch_random_number_generator.h"
14
15 #include <random>
16
17 namespace Catch {
18 namespace Generators {
19
20 template <typename Float>
21 class RandomFloatingGenerator final : public IGenerator<Float> {
22 Catch::SimplePcg32& m_rng;
23 std::uniform_real_distribution<Float> m_dist;
24 Float m_current_number;
25 public:
26
RandomFloatingGenerator(Float a,Float b)27 RandomFloatingGenerator(Float a, Float b):
28 m_rng(rng()),
29 m_dist(a, b) {
30 static_cast<void>(next());
31 }
32
get() const33 Float const& get() const override {
34 return m_current_number;
35 }
next()36 bool next() override {
37 m_current_number = m_dist(m_rng);
38 return true;
39 }
40 };
41
42 template <typename Integer>
43 class RandomIntegerGenerator final : public IGenerator<Integer> {
44 Catch::SimplePcg32& m_rng;
45 std::uniform_int_distribution<Integer> m_dist;
46 Integer m_current_number;
47 public:
48
RandomIntegerGenerator(Integer a,Integer b)49 RandomIntegerGenerator(Integer a, Integer b):
50 m_rng(rng()),
51 m_dist(a, b) {
52 static_cast<void>(next());
53 }
54
get() const55 Integer const& get() const override {
56 return m_current_number;
57 }
next()58 bool next() override {
59 m_current_number = m_dist(m_rng);
60 return true;
61 }
62 };
63
64 // TODO: Ideally this would be also constrained against the various char types,
65 // but I don't expect users to run into that in practice.
66 template <typename T>
67 typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value,
68 GeneratorWrapper<T>>::type
random(T a,T b)69 random(T a, T b) {
70 return GeneratorWrapper<T>(
71 pf::make_unique<RandomIntegerGenerator<T>>(a, b)
72 );
73 }
74
75 template <typename T>
76 typename std::enable_if<std::is_floating_point<T>::value,
77 GeneratorWrapper<T>>::type
random(T a,T b)78 random(T a, T b) {
79 return GeneratorWrapper<T>(
80 pf::make_unique<RandomFloatingGenerator<T>>(a, b)
81 );
82 }
83
84
85 template <typename T>
86 class RangeGenerator final : public IGenerator<T> {
87 T m_current;
88 T m_end;
89 T m_step;
90 bool m_positive;
91
92 public:
RangeGenerator(T const & start,T const & end,T const & step)93 RangeGenerator(T const& start, T const& end, T const& step):
94 m_current(start),
95 m_end(end),
96 m_step(step),
97 m_positive(m_step > T(0))
98 {
99 assert(m_current != m_end && "Range start and end cannot be equal");
100 assert(m_step != T(0) && "Step size cannot be zero");
101 assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end");
102 }
103
RangeGenerator(T const & start,T const & end)104 RangeGenerator(T const& start, T const& end):
105 RangeGenerator(start, end, (start < end) ? T(1) : T(-1))
106 {}
107
get() const108 T const& get() const override {
109 return m_current;
110 }
111
next()112 bool next() override {
113 m_current += m_step;
114 return (m_positive) ? (m_current < m_end) : (m_current > m_end);
115 }
116 };
117
118 template <typename T>
range(T const & start,T const & end,T const & step)119 GeneratorWrapper<T> range(T const& start, T const& end, T const& step) {
120 static_assert(std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, "Type must be numeric");
121 return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end, step));
122 }
123
124 template <typename T>
range(T const & start,T const & end)125 GeneratorWrapper<T> range(T const& start, T const& end) {
126 static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer");
127 return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end));
128 }
129
130
131 template <typename T>
132 class IteratorGenerator final : public IGenerator<T> {
133 static_assert(!std::is_same<T, bool>::value,
134 "IteratorGenerator currently does not support bools"
135 "because of std::vector<bool> specialization");
136
137 std::vector<T> m_elems;
138 size_t m_current = 0;
139 public:
140 template <typename InputIterator, typename InputSentinel>
IteratorGenerator(InputIterator first,InputSentinel last)141 IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) {
142 if (m_elems.empty()) {
143 Catch::throw_exception(GeneratorException("IteratorGenerator received no valid values"));
144 }
145 }
146
get() const147 T const& get() const override {
148 return m_elems[m_current];
149 }
150
next()151 bool next() override {
152 ++m_current;
153 return m_current != m_elems.size();
154 }
155 };
156
157 template <typename InputIterator,
158 typename InputSentinel,
159 typename ResultType = typename std::iterator_traits<InputIterator>::value_type>
from_range(InputIterator from,InputSentinel to)160 GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) {
161 return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(from, to));
162 }
163
164 template <typename Container,
165 typename ResultType = typename Container::value_type>
from_range(Container const & cnt)166 GeneratorWrapper<ResultType> from_range(Container const& cnt) {
167 return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(cnt.begin(), cnt.end()));
168 }
169
170
171 } // namespace Generators
172 } // namespace Catch
173
174
175 #endif // TWOBLUECUBES_CATCH_GENERATORS_SPECIFIC_HPP_INCLUDED
176