mimic++ v4
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 (...,
124 std::invoke(
125 [&](auto& seq, const sequence::Id id)
126 {
127 if (const std::optional priority = seq->priority_of(id))
128 {
129 ratings.emplace_back(
130 *priority,
131 seq->tag());
132 }
133 else
134 {
135 inapplicable.emplace_back(seq->tag());
136 }
137 },
138 std::get<0>(entries),
139 std::get<1>(entries)));
140 },
141 sequenceEntries);
142
143 if (!std::ranges::empty(inapplicable))
144 {
145 return state_inapplicable{
146 .min = min,
147 .max = max,
148 .count = count,
149 .sequenceRatings = std::move(ratings),
150 .inapplicableSequences = std::move(inapplicable)
151 };
152 }
153
154 return state_applicable{
155 .min = min,
156 .max = max,
157 .count = count,
158 .sequenceRatings = std::move(ratings),
159 };
160 }
161 }
162
163 template <typename... Sequences>
165 {
166 public:
167 static constexpr std::size_t sequenceCount{sizeof...(Sequences)};
168
169 [[nodiscard]]
170 explicit constexpr ControlPolicy(
171 const detail::TimesConfig& timesConfig,
172 const sequence::detail::Config<Sequences...>& sequenceConfig
173 ) noexcept
174 : m_Min{timesConfig.min()},
175 m_Max{timesConfig.max()},
176 m_Sequences{
177 detail::make_sequence_entries(sequenceConfig.sequences())
178 }
179 {
180 update_sequence_states();
181 }
182
183 [[nodiscard]]
184 constexpr bool is_satisfied() const noexcept
185 {
186 return m_Min <= m_Count
187 && m_Count <= m_Max;
188 }
189
190 [[nodiscard]]
191 constexpr bool is_saturated() const noexcept
192 {
193 return m_Count == m_Max;
194 }
195
196 [[nodiscard]]
197 constexpr bool is_applicable() const noexcept
198 {
199 return m_Count < m_Max
200 && std::apply(
201 [](const auto&... entries) noexcept
202 {
203 return (... && std::get<0>(entries)->is_consumable(std::get<1>(entries)));
204 },
205 m_Sequences);
206 }
207
208 constexpr void consume() noexcept
209 {
210 assert(is_applicable());
211
212 std::apply(
213 [](auto&... entries) noexcept
214 {
215 (..., std::get<0>(entries)->consume(std::get<1>(entries)));
216 },
217 m_Sequences);
218
219 ++m_Count;
220
221 update_sequence_states();
222 }
223
224 [[nodiscard]]
226 {
227 return detail::make_control_state(
228 m_Min,
229 m_Max,
230 m_Count,
231 m_Sequences);
232 }
233
234 private:
235 int m_Min;
236 int m_Max;
237 int m_Count{};
238 std::tuple<
239 std::tuple<std::shared_ptr<Sequences>, sequence::Id>...> m_Sequences{};
240
241 constexpr void update_sequence_states() noexcept
242 {
243 if (m_Count == m_Min)
244 {
245 std::apply(
246 [](auto&... entries) noexcept
247 {
248 (..., std::get<0>(entries)->set_satisfied(std::get<1>(entries)));
249 },
250 m_Sequences);
251 }
252 else if (m_Count == m_Max)
253 {
254 std::apply(
255 [](auto&... entries) noexcept
256 {
257 (..., std::get<0>(entries)->set_saturated(std::get<1>(entries)));
258 },
259 m_Sequences);
260 }
261 }
262 };
263}
264
266{
291 [[nodiscard]]
292 constexpr auto times(const int min, const int max)
293 {
294 return detail::TimesConfig{min, max};
295 }
296
306 [[nodiscard]]
307 constexpr auto times(const int exactly)
308 {
309 return detail::TimesConfig(exactly, exactly);
310 }
311
321 [[nodiscard]]
322 constexpr auto at_least(const int min)
323 {
324 return detail::TimesConfig{
325 min,
326 std::numeric_limits<int>::max()
327 };
328 }
329
339 [[nodiscard]]
340 constexpr auto at_most(const int max)
341 {
342 return detail::TimesConfig{
343 0,
344 max
345 };
346 }
347
355 [[nodiscard]]
356 consteval auto once() noexcept
357 {
358 constexpr detail::TimesConfig config{
359 1,
360 1
361 };
362
363 return config;
364 }
365
373 [[nodiscard]]
374 consteval auto twice() noexcept
375 {
376 constexpr detail::TimesConfig config{
377 2,
378 2
379 };
380
381 return config;
382 }
383
388}
389
390#endif
Definition ControlPolicy.hpp:165
control_state_t state() const
Definition ControlPolicy.hpp:225
constexpr void consume() noexcept
Definition ControlPolicy.hpp:208
constexpr bool is_satisfied() const noexcept
Definition ControlPolicy.hpp:184
constexpr ControlPolicy(const detail::TimesConfig &timesConfig, const sequence::detail::Config< Sequences... > &sequenceConfig) noexcept
Definition ControlPolicy.hpp:170
static constexpr std::size_t sequenceCount
Definition ControlPolicy.hpp:167
constexpr bool is_saturated() const noexcept
Definition ControlPolicy.hpp:191
constexpr bool is_applicable() const noexcept
Definition ControlPolicy.hpp:197
constexpr auto at_most(const int max)
Specifies a times policy with just an upper limit.
Definition ControlPolicy.hpp:340
constexpr auto times(const int min, const int max)
Specifies a times policy with a limit range.
Definition ControlPolicy.hpp:292
consteval auto twice() noexcept
Specifies a times policy with both limits set to 2.
Definition ControlPolicy.hpp:374
consteval auto once() noexcept
Specifies a times policy with both limits set to 1.
Definition ControlPolicy.hpp:356
constexpr auto at_least(const int min)
Specifies a times policy with just a lower limit.
Definition ControlPolicy.hpp:322
std::variant< state_inapplicable, state_applicable, state_saturated > control_state_t
Definition Reports.hpp:73
Definition ControlPolicy.hpp:266
Id
Definition Fwd.hpp:220
Definition BoostTest.hpp:20