Hegel 0.11.4
Property-based testing for C++
Loading...
Searching...
No Matches
default.h
1#pragma once
2
3#include "hegel/config.h"
4
5// default_generator (type-directed derivation) is the one feature that needs
6// reflect-cpp. Building with HEGEL_REFLECTION=OFF drops it so the rest of the
7// library can be consumed from C++17.
8#if HEGEL_HAS_REFLECTION
9
10#include <map>
11#include <optional>
12#include <set>
13#include <variant>
14#include <vector>
15
16#include <rfl.hpp>
17
18#include "hegel/core.h"
19#include "hegel/generators/builds.h"
20#include "hegel/generators/collections.h"
21#include "hegel/generators/combinators.h"
22#include "hegel/generators/numeric.h"
23#include "hegel/generators/primitives.h"
24#include "hegel/generators/strings.h"
25
26namespace hegel::generators {
27
30
31 template <typename T> class DerivedGenerator;
32
33 // Forward declaration — defined below after all specializations
34 template <typename T> DerivedGenerator<T> default_generator();
35
37 namespace detail {
38
39 // =================================================================
40 // Type traits for container detection
41 // =================================================================
42
43 template <typename T> struct is_vector : std::false_type {};
44 template <typename T>
45 struct is_vector<std::vector<T>> : std::true_type {};
46
47 template <typename T> struct is_set : std::false_type {};
48 template <typename T> struct is_set<std::set<T>> : std::true_type {};
49
50 template <typename T> struct is_map : std::false_type {};
51 template <typename K, typename V>
52 struct is_map<std::map<K, V>> : std::true_type {};
53
54 template <typename T> struct is_optional : std::false_type {};
55 template <typename T>
56 struct is_optional<std::optional<T>> : std::true_type {};
57
58 template <typename T> struct is_tuple : std::false_type {};
59 template <typename... Ts>
60 struct is_tuple<std::tuple<Ts...>> : std::true_type {};
61
62 template <typename T> struct is_variant : std::false_type {};
63 template <typename... Ts>
64 struct is_variant<std::variant<Ts...>> : std::true_type {};
65
66 // =================================================================
67 // Reflectable struct detection
68 // =================================================================
69
70 template <typename T, typename = void>
71 struct is_reflectable_struct : std::false_type {};
72
73 template <typename T>
74 struct is_reflectable_struct<
75 T, std::void_t<decltype(rfl::to_view(std::declval<T&>()))>>
76 : std::true_type {};
77
78 // =================================================================
79 // DefaultGenerator trait — primary template (struct fallback)
80 // =================================================================
81
82 template <typename T, typename Enable = void> struct DefaultGenerator {
83 static_assert(
84 is_reflectable_struct<T>::value,
85 "default_generator<T>(): T must be a supported primitive, "
86 "container, or reflectable struct");
87
88 static Generator<T> generator() {
89 return compose([](const TestCase& tc) -> T {
90 T result{};
91 auto view = rfl::to_view(result);
92 view.apply([&tc](const auto& field) {
93 using PtrType =
94 typename std::remove_cvref_t<decltype(field)>::Type;
95 using FieldType = std::remove_pointer_t<PtrType>;
96 *field.value() =
97 default_generator<FieldType>().do_draw(tc);
98 });
99 return result;
100 });
101 }
102 };
103
104 // =================================================================
105 // Specializations for primitive types
106 // =================================================================
107
108 template <> struct DefaultGenerator<bool> {
109 static Generator<bool> generator() { return booleans(); }
110 };
111
112 template <> struct DefaultGenerator<std::string> {
113 static Generator<std::string> generator() { return text(); }
114 };
115
116 template <> struct DefaultGenerator<std::monostate> {
117 static Generator<std::monostate> generator() {
118 return just(std::monostate{});
119 }
120 };
121
122 template <typename T>
123 struct DefaultGenerator<T, std::enable_if_t<std::is_integral_v<T> &&
124 !std::is_same_v<T, bool>>> {
125 static Generator<T> generator() { return integers<T>(); }
126 };
127
128 template <typename T>
129 struct DefaultGenerator<T,
130 std::enable_if_t<std::is_floating_point_v<T>>> {
131 static Generator<T> generator() { return floats<T>(); }
132 };
133
134 // =================================================================
135 // Specializations for standard containers
136 // =================================================================
137
138 template <typename T>
139 struct DefaultGenerator<T, std::enable_if_t<is_vector<T>::value>> {
140 static Generator<T> generator() {
141 using Elem = typename T::value_type;
142 return vectors(default_generator<Elem>());
143 }
144 };
145
146 template <typename T>
147 struct DefaultGenerator<T, std::enable_if_t<is_set<T>::value>> {
148 static Generator<T> generator() {
149 using Elem = typename T::value_type;
150 return sets(default_generator<Elem>());
151 }
152 };
153
154 template <typename T>
155 struct DefaultGenerator<T, std::enable_if_t<is_map<T>::value>> {
156 static Generator<T> generator() {
157 using K = typename T::key_type;
158 using V = typename T::mapped_type;
159 return maps(default_generator<K>(), default_generator<V>());
160 }
161 };
162
163 template <typename T>
164 struct DefaultGenerator<T, std::enable_if_t<is_optional<T>::value>> {
165 static Generator<T> generator() {
166 using Inner = typename T::value_type;
167 return optional(default_generator<Inner>());
168 }
169 };
170
171 // --- std::tuple<Ts...> ---
172
173 template <typename Tuple, size_t... Is>
174 auto make_default_tuple_gen(std::index_sequence<Is...>) {
175 return tuples(
176 default_generator<std::tuple_element_t<Is, Tuple>>()...);
177 }
178
179 template <typename T>
180 struct DefaultGenerator<T, std::enable_if_t<is_tuple<T>::value>> {
181 static Generator<T> generator() {
182 return make_default_tuple_gen<T>(
183 std::make_index_sequence<std::tuple_size_v<T>>{});
184 }
185 };
186
187 // --- std::variant<Ts...> ---
188
189 template <typename Variant, size_t... Is>
190 auto make_default_variant_gen(std::index_sequence<Is...>) {
191 return variant(default_generator<
192 std::variant_alternative_t<Is, Variant>>()...);
193 }
194
195 template <typename T>
196 struct DefaultGenerator<T, std::enable_if_t<is_variant<T>::value>> {
197 static Generator<T> generator() {
198 return make_default_variant_gen<T>(
199 std::make_index_sequence<std::variant_size_v<T>>{});
200 }
201 };
202
203 } // namespace detail
205
206 // =============================================================================
207 // Public API
208 // =============================================================================
209
224 template <typename T> class DerivedGenerator : public Generator<T> {
225 public:
227 DerivedGenerator(Generator<T> base) : Generator<T>(std::move(base)) {}
229
247 template <typename... Fields>
248 DerivedGenerator<T> override(Fields... fields) const {
249 Generator<T> base = *this;
250 auto fields_tuple = std::make_tuple(std::move(fields)...);
251 return DerivedGenerator<T>(
252 compose([base, fields_tuple](const TestCase& tc) mutable -> T {
253 T result = base.do_draw(tc);
254 std::apply(
255 [&result, &tc](auto&... fs) {
256 ((result.*(std::remove_reference_t<
257 decltype(fs)>::member_ptr) =
258 fs.generator.do_draw(tc)),
259 ...);
260 },
261 fields_tuple);
262 return result;
263 }));
264 }
265 };
266
290 template <typename T> DerivedGenerator<T> default_generator() {
291 return DerivedGenerator<T>(detail::DefaultGenerator<T>::generator());
292 }
293
295
296} // namespace hegel::generators
297
298#endif // HEGEL_HAS_REFLECTION
Handle to the currently-executing test case.
Definition test_case.h:40
A Generator produced by default_generator<T>().
Definition default.h:224
The base class of all generators.
Definition core.h:67
Hegel generators.
Definition core.h:17
Generator< std::tuple< Ts... > > tuples(Generator< Ts >... gens)
Generate tuples from multiple generators.
Definition collections.h:314
Generator< T > just(T value)
Generate a constant value.
Definition primitives.h:43
Generator< std::vector< T > > vectors(Generator< T > elements, VectorsParams params={})
Generate vectors with elements from another generator.
Definition collections.h:210
Field< MemberPtr, Gen > field(Gen gen)
Create a field specification for builds_agg().
Definition builds.h:73
auto compose(F &&fn)
*‍/
Definition core.h:253
Generator< bool > booleans()
Generate random boolean values.
Generator< std::variant< Ts... > > variant(Generator< Ts >... gens)
Generate std::variant from heterogeneous generators.
Definition combinators.h:235
Generator< std::optional< T > > optional(Generator< T > gen)
Generate optional values (present or absent).
Definition combinators.h:256
Generator< std::string > text(const TextParams &params={})
Generate random text strings.
Generator< std::set< T > > sets(Generator< T > elements, SetsParams params={})
Generate sets with elements from another generator.
Definition collections.h:230
DerivedGenerator< T > default_generator()
Create a default generator for type T.
Definition default.h:290
Generator< std::map< K, V > > maps(Generator< K > keys, Generator< V > values, MapsParams params={})
Generate maps with configurable key and value types.
Definition collections.h:258