halcheck 1.0
Loading...
Searching...
No Matches
forward_shrinks.hpp
1#ifndef HALCHECK_GEN_FORWARD_SHRINKS_HPP
2#define HALCHECK_GEN_FORWARD_SHRINKS_HPP
3
4#include <halcheck/gen/shrink.hpp>
5#include <halcheck/lib/effect.hpp>
6#include <halcheck/lib/functional.hpp>
7#include <halcheck/lib/iterator.hpp>
8#include <halcheck/lib/optional.hpp>
9#include <halcheck/lib/pp.hpp>
10#include <halcheck/lib/type_traits.hpp>
11#include <halcheck/lib/utility.hpp>
12
13#include <algorithm>
14#include <cstddef>
15#include <cstdint>
16#include <memory>
17#include <stdexcept>
18#include <thread>
19#include <utility>
20#include <vector>
21
22namespace halcheck { namespace gen {
23
24namespace detail {
25
26struct forward_shrink_append {
27 std::vector<std::uintmax_t> operator()(std::uintmax_t value) const {
28 auto output = input;
29 output.push_back(value);
30 return output;
31 }
32
34};
35
36struct forward_shrink_handler : lib::effect::handler<forward_shrink_handler, gen::shrink_effect> {
37 explicit forward_shrink_handler(std::vector<std::uintmax_t> input)
38 : data(new data_t{std::move(input), 0, 0}), origin(std::this_thread::get_id()) {}
39
40 lib::optional<std::uintmax_t> operator()(gen::shrink_effect args) final {
41 if (std::this_thread::get_id() != origin)
42 throw std::runtime_error("cannot use forward shrinking with multiple threads");
43
44 if (data->index >= data->input.size()) {
45 data->remaining += args.size;
46 return lib::nullopt;
47 }
48
49 if (data->input[data->index] >= args.size) {
50 data->input[data->index] -= args.size;
51 return lib::nullopt;
52 }
53
54 return data->input[data->index++];
55 }
56
57 struct data_t {
60 std::size_t remaining;
61 };
62
64 std::thread::id origin;
65};
66
67} // namespace detail
68
69template<typename T>
70struct forward_shrinks {
71public:
72 template<typename F, typename... Args, HALCHECK_REQUIRE(lib::is_invocable_r<T, F, Args...>())>
73 forward_shrinks(std::vector<std::uintmax_t> input, F func, Args &&...args)
74 : forward_shrinks(detail::forward_shrink_handler(input), input, std::move(func), std::forward<Args>(args)...) {}
75
76 lib::add_lvalue_reference_t<const T> get() const { return _value.get(); }
77 lib::add_lvalue_reference_t<T> get() { return _value.get(); }
78
79 using children_view = lib::transform_view<lib::iota_view<std::uintmax_t>, detail::forward_shrink_append>;
80
81 const children_view &children() const { return _children; }
82
83private:
84 template<typename F, typename... Args>
85 forward_shrinks(detail::forward_shrink_handler handler, std::vector<uintmax_t> input, F func, Args &&...args)
86 : _value(handler.handle([&] { return lib::make_result_holder(func, std::forward<Args>(args)...); })),
87 _children(lib::transform(lib::iota(handler.data->remaining), detail::forward_shrink_append{std::move(input)})) {
88 }
89
90 lib::result_holder<T> _value;
91 children_view _children;
92};
93
95 template<typename F, typename... Args, HALCHECK_REQUIRE(lib::is_invocable<F, Args...>())>
96 gen::forward_shrinks<lib::invoke_result_t<F, Args...>>
97 operator()(std::vector<std::uintmax_t> input, F func, Args &&...args) const {
98 return gen::forward_shrinks<lib::invoke_result_t<F, Args...>>(
99 std::move(input),
100 std::move(func),
101 std::forward<Args>(args)...);
102 }
103
104 template<typename F, typename... Args, HALCHECK_REQUIRE(lib::is_invocable<F, Args...>())>
105 gen::forward_shrinks<lib::invoke_result_t<F, Args...>> operator()(F func, Args &&...args) const {
106 return gen::forward_shrinks<lib::invoke_result_t<F, Args...>>(
108 std::move(func),
109 std::forward<Args>(args)...);
110 }
111} make_forward_shrinks;
112
113}} // namespace halcheck::gen
114
115#endif
T data(T... args)
T forward(T... args)
T get_id(T... args)
decltype(lib::invoke(std::declval< F >(), std::declval< Args >()...)) invoke_result_t
An implementation of std::invoke_result_t.
Definition invoke.hpp:42
HALCHECK_INLINE_CONSTEXPR struct halcheck::lib::@31 transform
Constructs a transform_view.
static constexpr nullopt_t nullopt
An implementation of std::nullopt.
Definition optional.hpp:41
#define HALCHECK_INLINE_CONSTEXPR
A backwards-compatible substitute for inline constexpr.
Definition pp.hpp:70
typename std::add_lvalue_reference< T >::type add_lvalue_reference_t
An implementation of std::add_lvalue_reference_t.
Definition type_traits.hpp:199
#define HALCHECK_REQUIRE(...)
Expands to a template argument that is only valid if the given argument evaluates to true.
Definition type_traits.hpp:24
lib::variant< std::integral_constant< std::size_t, Ints >... > index(lib::atom id, lib::index_sequence< Ints... >={})
Generates a random std::integral_constant from the given set of indices.
Definition variant.hpp:39
STL namespace.
T push_back(T... args)