mimic++ v2
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
30 ) noexcept
31 {
32 // This is a workaround due to some issues with clang-17 with c++23 and libstdc++
33 // That configuration prevents the direct initialization, thus we have to default construct first and
34 // setup afterwards. Compilers will probably detect that and optimize accordingly.
35 std::tuple<std::tuple<std::shared_ptr<Sequences>, sequence::Id>...> result{};
36 std::invoke(
37 [&]<std::size_t... indices>([[maybe_unused]] const std::index_sequence<indices...>) noexcept
38 {
39 ((std::get<indices>(result) =
40 std::tuple{
41 std::get<indices>(sequences),
42 std::get<indices>(sequences)->add(),
43 }), ...);
44 },
45 std::index_sequence_for<Sequences...>{});
46 return result;
47 }
48
49 class TimesConfig
50 {
51 public:
52 [[nodiscard]]
53 TimesConfig() = default;
54
55 [[nodiscard]]
56 constexpr TimesConfig(const int min, const int max)
57 {
58 if (min < 0
59 || max < 0
60 || max < min)
61 {
62 throw std::invalid_argument{
63 "min must be less or equal to max and both must not be less than zero."
64 };
65 }
66
67 m_Min = min;
68 m_Max = max;
69 }
70
71 [[nodiscard]]
72 constexpr int min() const noexcept
73 {
74 return m_Min;
75 }
76
77 [[nodiscard]]
78 constexpr int max() const noexcept
79 {
80 return m_Max;
81 }
82
83 private:
84 int m_Min{1};
85 int m_Max{1};
86 };
87}
88
89namespace mimicpp
90{
91 namespace detail
92 {
93 [[nodiscard]]
94 control_state_t make_control_state(
95 const int min,
96 const int max,
97 const int count,
98 const auto& sequenceEntries
99 )
100 {
101 if (count == max)
102 {
103 return state_saturated{
104 .min = min,
105 .max = max,
106 .count = count,
107 .sequences = std::apply(
108 [](const auto&... entries)
109 {
110 return std::vector<sequence::Tag>{
111 std::get<0>(entries)->tag()...
112 };
113 },
114 sequenceEntries)
115 };
116 }
117
118 std::vector<sequence::Tag> inapplicable{};
119 std::vector<sequence::rating> ratings{};
120 std::apply(
121 [&](const auto&... entries)
122 {
123 const auto distribute = [&](auto& seq, const sequence::Id id)
124 {
125 if (const std::optional priority = seq->priority_of(id))
126 {
127 ratings.emplace_back(
128 *priority,
129 seq->tag());
130 }
131 else
132 {
133 inapplicable.emplace_back(seq->tag());
134 }
135 };
136
137 (...,
138 distribute(
139 std::get<0>(entries),
140 std::get<1>(entries)));
141 },
142 sequenceEntries);
143
144 if (!std::ranges::empty(inapplicable))
145 {
146 return state_inapplicable{
147 .min = min,
148 .max = max,
149 .count = count,
150 .sequenceRatings = std::move(ratings),
151 .inapplicableSequences = std::move(inapplicable)
152 };
153 }
154
155 return state_applicable{
156 .min = min,
157 .max = max,
158 .count = count,
159 .sequenceRatings = std::move(ratings),
160 };
161 }
162 }
163
164 template <typename... Sequences>
166 {
167 public:
168 static constexpr std::size_t sequenceCount{sizeof...(Sequences)};
169
170 [[nodiscard]]
171 explicit constexpr ControlPolicy(
172 const detail::TimesConfig& timesConfig,
173 const sequence::detail::Config<Sequences...>& sequenceConfig
174 ) noexcept
175 : m_Min{timesConfig.min()},
176 m_Max{timesConfig.max()},
177 m_Sequences{
178 detail::make_sequence_entries(sequenceConfig.sequences())
179 }
180 {
181 update_sequence_states();
182 }
183
184 [[nodiscard]]
185 constexpr bool is_satisfied() const noexcept
186 {
187 return m_Min <= m_Count
188 && m_Count <= m_Max;
189 }
190
191 [[nodiscard]]
192 constexpr bool is_saturated() const noexcept
193 {
194 return m_Count == m_Max;
195 }
196
197 [[nodiscard]]
198 constexpr bool is_applicable() const noexcept
199 {
200 return m_Count < m_Max
201 && std::apply(
202 [](const auto&... entries) noexcept
203 {
204 return (... && std::get<0>(entries)->is_consumable(std::get<1>(entries)));
205 },
206 m_Sequences);
207 }
208
209 constexpr void consume() noexcept
210 {
211 assert(is_applicable());
212
213 std::apply(
214 [](auto&... entries) noexcept
215 {
216 (..., std::get<0>(entries)->consume(std::get<1>(entries)));
217 },
218 m_Sequences);
219
220 ++m_Count;
221
222 update_sequence_states();
223 }
224
225 [[nodiscard]]
227 {
228 return detail::make_control_state(
229 m_Min,
230 m_Max,
231 m_Count,
232 m_Sequences);
233 }
234
235 private:
236 int m_Min;
237 int m_Max;
238 int m_Count{};
239 std::tuple<
240 std::tuple<std::shared_ptr<Sequences>, sequence::Id>...> m_Sequences{};
241
242 constexpr void update_sequence_states() noexcept
243 {
244 if (m_Count == m_Min)
245 {
246 std::apply(
247 [](auto&... entries) noexcept
248 {
249 (..., std::get<0>(entries)->set_satisfied(std::get<1>(entries)));
250 },
251 m_Sequences);
252 }
253 else if (m_Count == m_Max)
254 {
255 std::apply(
256 [](auto&... entries) noexcept
257 {
258 (..., std::get<0>(entries)->set_saturated(std::get<1>(entries)));
259 },
260 m_Sequences);
261 }
262 }
263 };
264}
265
267{
292 [[nodiscard]]
293 constexpr auto times(const int min, const int max)
294 {
295 return detail::TimesConfig{min, max};
296 }
297
307 [[nodiscard]]
308 constexpr auto times(const int exactly)
309 {
310 return detail::TimesConfig(exactly, exactly);
311 }
312
322 [[nodiscard]]
323 constexpr auto at_least(const int min)
324 {
325 return detail::TimesConfig{
326 min,
327 std::numeric_limits<int>::max()
328 };
329 }
330
340 [[nodiscard]]
341 constexpr auto at_most(const int max)
342 {
343 return detail::TimesConfig{
344 0,
345 max
346 };
347 }
348
356 [[nodiscard]]
357 consteval auto once() noexcept
358 {
359 constexpr detail::TimesConfig config{
360 1,
361 1
362 };
363
364 return config;
365 }
366
374 [[nodiscard]]
375 consteval auto twice() noexcept
376 {
377 constexpr detail::TimesConfig config{
378 2,
379 2
380 };
381
382 return config;
383 }
384
389}
390
391#endif
Definition ControlPolicy.hpp:166
control_state_t state() const
Definition ControlPolicy.hpp:226
constexpr void consume() noexcept
Definition ControlPolicy.hpp:209
constexpr bool is_satisfied() const noexcept
Definition ControlPolicy.hpp:185
constexpr ControlPolicy(const detail::TimesConfig &timesConfig, const sequence::detail::Config< Sequences... > &sequenceConfig) noexcept
Definition ControlPolicy.hpp:171
static constexpr std::size_t sequenceCount
Definition ControlPolicy.hpp:168
constexpr bool is_saturated() const noexcept
Definition ControlPolicy.hpp:192
constexpr bool is_applicable() const noexcept
Definition ControlPolicy.hpp:198
constexpr auto at_most(const int max)
Specifies a times policy with just an upper limit.
Definition ControlPolicy.hpp:341
constexpr auto times(const int min, const int max)
Specifies a times policy with a limit range.
Definition ControlPolicy.hpp:293
consteval auto twice() noexcept
Specifies a times policy with both limits set to 2.
Definition ControlPolicy.hpp:375
consteval auto once() noexcept
Specifies a times policy with both limits set to 1.
Definition ControlPolicy.hpp:357
constexpr auto at_least(const int min)
Specifies a times policy with just a lower limit.
Definition ControlPolicy.hpp:323
std::variant< state_inapplicable, state_applicable, state_saturated > control_state_t
Definition Reports.hpp:73
Definition ControlPolicy.hpp:267
Id
Definition Fwd.hpp:124
Definition BoostTest.hpp:20