mimic++ v5
Loading...
Searching...
No Matches
ControlPolicy.hpp
Go to the documentation of this file.
1// // Copyright Dominic (DNKpp) Koepke 2024 - 2024.
2// // Distributed under the Boost Software License, Version 1.0.
3// // (See accompanying file LICENSE_1_0.txt or copy at
4// // https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef MIMICPP_CONTROL_POLICY_HPP
7#define MIMICPP_CONTROL_POLICY_HPP
8
9#pragma once
10
11#include "mimic++/Reports.hpp"
12#include "mimic++/Sequence.hpp"
13#include "mimic++/Utility.hpp"
14
15#include <cassert>
16#include <limits>
17#include <memory>
18#include <optional>
19#include <stdexcept>
20#include <tuple>
21#include <utility>
22#include <vector>
23
24namespace mimicpp::detail
25{
26 template <typename... Sequences>
27 [[nodiscard]]
28 constexpr std::tuple<std::tuple<std::shared_ptr<Sequences>, sequence::Id>...> make_sequence_entries(
29 const std::tuple<std::shared_ptr<Sequences>...>& sequences) noexcept
30 {
31 // This is a workaround due to some issues with clang-17 with c++23 and libstdc++
32 // That configuration prevents the direct initialization, thus we have to default construct first and
33 // setup afterwards. Compilers will probably detect that and optimize accordingly.
34 std::tuple<std::tuple<std::shared_ptr<Sequences>, sequence::Id>...> result{};
35 std::invoke(
36 [&]<std::size_t... indices>([[maybe_unused]] const std::index_sequence<indices...>) noexcept {
37 ((std::get<indices>(result) =
38 std::tuple{
39 std::get<indices>(sequences),
40 std::get<indices>(sequences)->add(),
41 }),
42 ...);
43 },
44 std::index_sequence_for<Sequences...>{});
45 return result;
46 }
47
48 class TimesConfig
49 {
50 public:
51 [[nodiscard]]
52 TimesConfig() = default;
53
54 [[nodiscard]]
55 constexpr TimesConfig(const int min, const int max)
56 {
57 if (min < 0
58 || max < 0
59 || max < min)
60 {
61 throw std::invalid_argument{
62 "min must be less or equal to max and both must not be less than zero."};
63 }
64
65 m_Min = min;
66 m_Max = max;
67 }
68
69 [[nodiscard]]
70 constexpr int min() const noexcept
71 {
72 return m_Min;
73 }
74
75 [[nodiscard]]
76 constexpr int max() const noexcept
77 {
78 return m_Max;
79 }
80
81 private:
82 int m_Min{1};
83 int m_Max{1};
84 };
85}
86
87namespace mimicpp
88{
89 namespace detail
90 {
91 [[nodiscard]]
92 control_state_t make_control_state(
93 const int min,
94 const int max,
95 const int count,
96 const auto& sequenceEntries)
97 {
98 if (count == max)
99 {
100 return state_saturated{
101 .min = min,
102 .max = max,
103 .count = count,
104 .sequences = std::apply(
105 [](const auto&... entries) {
106 return std::vector<sequence::Tag>{
107 std::get<0>(entries)->tag()...};
108 },
109 sequenceEntries)};
110 }
111
112 std::vector<sequence::Tag> inapplicable{};
113 std::vector<sequence::rating> ratings{};
114 std::apply(
115 [&](const auto&... entries) {
116 (...,
117 std::invoke(
118 [&](auto& seq, const sequence::Id id) {
119 if (const std::optional priority = seq->priority_of(id))
120 {
121 ratings.emplace_back(
122 *priority,
123 seq->tag());
124 }
125 else
126 {
127 inapplicable.emplace_back(seq->tag());
128 }
129 },
130 std::get<0>(entries),
131 std::get<1>(entries)));
132 },
133 sequenceEntries);
134
135 if (!std::ranges::empty(inapplicable))
136 {
137 return state_inapplicable{
138 .min = min,
139 .max = max,
140 .count = count,
141 .sequenceRatings = std::move(ratings),
142 .inapplicableSequences = std::move(inapplicable)};
143 }
144
145 return state_applicable{
146 .min = min,
147 .max = max,
148 .count = count,
149 .sequenceRatings = std::move(ratings),
150 };
151 }
152 }
153
154 template <typename... Sequences>
156 {
157 public:
158 static constexpr std::size_t sequenceCount{sizeof...(Sequences)};
159
160 [[nodiscard]]
161 explicit constexpr ControlPolicy(
162 const detail::TimesConfig& timesConfig,
163 const sequence::detail::Config<Sequences...>& sequenceConfig) noexcept
164 : m_Min{timesConfig.min()},
165 m_Max{timesConfig.max()},
166 m_Sequences{
167 detail::make_sequence_entries(sequenceConfig.sequences())}
168 {
169 update_sequence_states();
170 }
171
172 [[nodiscard]]
173 constexpr bool is_satisfied() const noexcept
174 {
175 return m_Min <= m_Count
176 && m_Count <= m_Max;
177 }
178
179 [[nodiscard]]
180 constexpr bool is_saturated() const noexcept
181 {
182 return m_Count == m_Max;
183 }
184
185 [[nodiscard]]
186 constexpr bool is_applicable() const noexcept
187 {
188 return m_Count < m_Max
189 && std::apply(
190 [](const auto&... entries) noexcept {
191 return (... && std::get<0>(entries)->is_consumable(std::get<1>(entries)));
192 },
193 m_Sequences);
194 }
195
196 constexpr void consume() noexcept
197 {
198 assert(is_applicable());
199
200 std::apply(
201 [](auto&... entries) noexcept {
202 (..., std::get<0>(entries)->consume(std::get<1>(entries)));
203 },
204 m_Sequences);
205
206 ++m_Count;
207
208 update_sequence_states();
209 }
210
211 [[nodiscard]]
213 {
214 return detail::make_control_state(
215 m_Min,
216 m_Max,
217 m_Count,
218 m_Sequences);
219 }
220
221 private:
222 int m_Min;
223 int m_Max;
224 int m_Count{};
225 std::tuple<
226 std::tuple<std::shared_ptr<Sequences>, sequence::Id>...>
227 m_Sequences{};
228
229 constexpr void update_sequence_states() noexcept
230 {
231 if (m_Count == m_Min)
232 {
233 std::apply(
234 [](auto&... entries) noexcept {
235 (..., std::get<0>(entries)->set_satisfied(std::get<1>(entries)));
236 },
237 m_Sequences);
238 }
239 else if (m_Count == m_Max)
240 {
241 std::apply(
242 [](auto&... entries) noexcept {
243 (..., std::get<0>(entries)->set_saturated(std::get<1>(entries)));
244 },
245 m_Sequences);
246 }
247 }
248 };
249}
250
252{
277 [[nodiscard]]
278 constexpr auto times(const int min, const int max)
279 {
280 return detail::TimesConfig{min, max};
281 }
282
292 [[nodiscard]]
293 constexpr auto times(const int exactly)
294 {
295 return detail::TimesConfig(exactly, exactly);
296 }
297
307 [[nodiscard]]
308 constexpr auto at_least(const int min)
309 {
310 return detail::TimesConfig{
311 min,
312 std::numeric_limits<int>::max()};
313 }
314
324 [[nodiscard]]
325 constexpr auto at_most(const int max)
326 {
327 return detail::TimesConfig{
328 0,
329 max};
330 }
331
339 [[nodiscard]]
340 consteval auto once() noexcept
341 {
342 constexpr detail::TimesConfig config{
343 1,
344 1};
345
346 return config;
347 }
348
356 [[nodiscard]]
357 consteval auto twice() noexcept
358 {
359 constexpr detail::TimesConfig config{
360 2,
361 2};
362
363 return config;
364 }
365
370}
371
372#endif
Definition ControlPolicy.hpp:156
control_state_t state() const
Definition ControlPolicy.hpp:212
constexpr void consume() noexcept
Definition ControlPolicy.hpp:196
constexpr bool is_satisfied() const noexcept
Definition ControlPolicy.hpp:173
constexpr ControlPolicy(const detail::TimesConfig &timesConfig, const sequence::detail::Config< Sequences... > &sequenceConfig) noexcept
Definition ControlPolicy.hpp:161
static constexpr std::size_t sequenceCount
Definition ControlPolicy.hpp:158
constexpr bool is_saturated() const noexcept
Definition ControlPolicy.hpp:180
constexpr bool is_applicable() const noexcept
Definition ControlPolicy.hpp:186
constexpr auto at_most(const int max)
Specifies a times policy with just an upper limit.
Definition ControlPolicy.hpp:325
constexpr auto times(const int min, const int max)
Specifies a times policy with a limit range.
Definition ControlPolicy.hpp:278
consteval auto twice() noexcept
Specifies a times policy with both limits set to 2.
Definition ControlPolicy.hpp:357
consteval auto once() noexcept
Specifies a times policy with both limits set to 1.
Definition ControlPolicy.hpp:340
constexpr auto at_least(const int min)
Specifies a times policy with just a lower limit.
Definition ControlPolicy.hpp:308
std::variant< state_inapplicable, state_applicable, state_saturated > control_state_t
Definition Reports.hpp:73
Definition ControlPolicy.hpp:252
Id
Definition Fwd.hpp:353
Definition BoostTest.hpp:20