mimic++ v4
Loading...
Searching...
No Matches
Sequence.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_SEQUENCE_HPP
7#define MIMICPP_SEQUENCE_HPP
8
9#pragma once
10
11#include "mimic++/Fwd.hpp"
12#include "mimic++/Printer.hpp"
13#include "mimic++/Reporter.hpp"
14#include "mimic++/Utility.hpp"
15
16#include <algorithm>
17#include <array>
18#include <cassert>
19#include <functional>
20#include <span>
21#include <tuple>
22
23namespace mimicpp::sequence
24{
77 namespace detail
78 {
79 template <typename Id, auto priorityStrategy>
80 requires std::is_enum_v<Id>
81 && std::signed_integral<std::underlying_type_t<Id>>
82 && std::convertible_to<
83 std::invoke_result_t<decltype(priorityStrategy), Id, int>,
84 int>
85 class BasicSequence
86 {
87 public:
88 using IdT = Id;
89
90 ~BasicSequence() noexcept(false)
91 {
92 const auto iter = std::ranges::find_if_not(
93 m_Entries.cbegin() + m_Cursor,
94 m_Entries.cend(),
95 [](const State s) noexcept
96 {
97 return s == State::satisfied
98 || s == State::saturated;
99 });
100
101 if (iter != m_Entries.cend())
102 {
103 mimicpp::detail::report_error(
104 format::format(
105 "Unfulfilled sequence. {} out of {} expectation(s) are satisfied.",
106 std::ranges::distance(m_Entries.cbegin(), iter),
107 m_Entries.size()));
108 }
109 }
110
111 [[nodiscard]]
112 BasicSequence() = default;
113
114 BasicSequence(const BasicSequence&) = delete;
115 BasicSequence& operator =(const BasicSequence&) = delete;
116 BasicSequence(BasicSequence&&) = delete;
117 BasicSequence& operator =(BasicSequence&&) = delete;
118
119 [[nodiscard]]
120 constexpr std::optional<int> priority_of(const IdT id) const noexcept
121 {
122 assert(is_valid(id));
123
124 if (is_consumable(id))
125 {
126 return std::invoke(
127 priorityStrategy,
128 id,
129 m_Cursor);
130 }
131
132 return std::nullopt;
133 }
134
135 constexpr void set_satisfied(const IdT id) noexcept
136 {
137 assert(is_valid(id));
138 assert(m_Cursor <= to_underlying(id));
139
140 auto& element = m_Entries[to_underlying(id)];
141 assert(element == State::unsatisfied);
142 element = State::satisfied;
143 }
144
145 constexpr void set_saturated(const IdT id) noexcept
146 {
147 assert(is_valid(id));
148 const auto index = to_underlying(id);
149 assert(m_Cursor <= index);
150
151 auto& element = m_Entries[index];
152 assert(
153 element == State::unsatisfied
154 || element == State::satisfied);
155 element = State::saturated;
156 }
157
158 [[nodiscard]]
159 constexpr bool is_consumable(const IdT id) const noexcept
160 {
161 assert(is_valid(id));
162
163 const int index = to_underlying(id);
164 const auto state = m_Entries[index];
165 return m_Cursor <= index
166 && std::ranges::all_of(
167 m_Entries.begin() + m_Cursor,
168 m_Entries.begin() + index,
169 [](const State s) noexcept
170 {
171 return s == State::satisfied
172 || s == State::saturated;
173 })
174 && (state == State::unsatisfied
175 || state == State::satisfied);
176 }
177
178 constexpr void consume(const IdT id) noexcept
179 {
180 assert(is_consumable(id));
181
182 m_Cursor = to_underlying(id);
183 }
184
185 [[nodiscard]]
186 constexpr IdT add()
187 {
188 if (!std::in_range<std::underlying_type_t<IdT>>(m_Entries.size()))
189 [[unlikely]]
190 {
191 throw std::runtime_error{
192 "Sequence already holds maximum amount of elements."
193 };
194 }
195
196 m_Entries.emplace_back(State::unsatisfied);
197 return static_cast<IdT>(m_Entries.size() - 1);
198 }
199
200 [[nodiscard]]
201 constexpr Tag tag() const noexcept
202 {
203 return Tag{
204 std::bit_cast<std::ptrdiff_t>(this)
205 };
206 }
207
208 private:
209 enum class State
210 {
211 unsatisfied,
212 satisfied,
213 saturated
214 };
215
216 std::vector<State> m_Entries{};
217 int m_Cursor{};
218
219 [[nodiscard]]
220 constexpr bool is_valid(const IdT id) const noexcept
221 {
222 return 0 <= to_underlying(id)
223 && std::cmp_less(to_underlying(id), m_Entries.size());
224 }
225 };
226
227 class LazyStrategy
228 {
229 public:
230 [[nodiscard]]
231 constexpr int operator ()(const auto id, const int cursor) const noexcept
232 {
233 const auto index = to_underlying(id);
234 assert(std::cmp_less_equal(cursor, index));
235
236 return std::numeric_limits<int>::max()
237 - (static_cast<int>(index) - cursor);
238 }
239 };
240
241 class GreedyStrategy
242 {
243 public:
244 [[nodiscard]]
245 constexpr int operator ()(const auto id, const int cursor) const noexcept
246 {
247 const auto index = to_underlying(id);
248 assert(std::cmp_less_equal(cursor, index));
249
250 return static_cast<int>(index) - cursor;
251 }
252 };
253
254 template <typename Id, auto priorityStrategy>
255 class BasicSequenceInterface
256 {
257 template <typename... Sequences>
258 friend class Config;
259
260 public:
261 using SequenceT = BasicSequence<Id, priorityStrategy>;
262
263 ~BasicSequenceInterface() = default;
264
265 [[nodiscard]]
266 BasicSequenceInterface() = default;
267
268 BasicSequenceInterface(const BasicSequenceInterface&) = delete;
269 BasicSequenceInterface& operator =(const BasicSequenceInterface&) = delete;
270 BasicSequenceInterface(BasicSequenceInterface&&) = delete;
271 BasicSequenceInterface& operator =(BasicSequenceInterface&&) = delete;
272
273 [[nodiscard]]
274 constexpr Tag tag() const noexcept
275 {
276 return m_Sequence->tag();
277 }
278
279 private:
280 std::shared_ptr<SequenceT> m_Sequence{
281 std::make_shared<SequenceT>()
282 };
283 };
284
285 template <typename... Sequences>
286 class Config
287 {
288 template <typename... Ts>
289 friend class Config;
290
291 public:
292 static constexpr std::size_t sequenceCount = sizeof...(Sequences);
293
294 [[nodiscard]]
295 Config()
296 requires (0 == sizeof...(Sequences))
297 = default;
298
299 template <typename... Interfaces>
300 requires (sizeof...(Interfaces) == sequenceCount)
301 [[nodiscard]]
302 explicit constexpr Config(Interfaces&... interfaces) noexcept(1 == sequenceCount)
303 : Config{
304 interfaces.m_Sequence...
305 }
306 {
307 }
308
309 [[nodiscard]]
310 constexpr auto& sequences() const noexcept
311 {
312 return m_Sequences;
313 }
314
315 template <typename... OtherSequences>
316 [[nodiscard]]
317 constexpr Config<Sequences..., OtherSequences...> concat(
318 const Config<OtherSequences...>& other
319 ) const
320 {
321 return std::apply(
322 [](auto... sequences)
323 {
324 return Config<Sequences..., OtherSequences...>{
325 std::move(sequences)...
326 };
327 },
328 std::tuple_cat(m_Sequences, other.sequences()));
329 }
330
331 private:
332 std::tuple<std::shared_ptr<Sequences>...> m_Sequences;
333
334 [[nodiscard]]
335 explicit constexpr Config(std::shared_ptr<Sequences>... sequences) noexcept(1 == sequenceCount)
336 requires (0 < sequenceCount)
337 {
338 if constexpr (1 < sequenceCount)
339 {
340 std::array tags{
341 sequences->tag()...
342 };
343
344 std::ranges::sort(tags);
345 if (!std::ranges::empty(std::ranges::unique(tags)))
346 {
347 throw std::invalid_argument{
348 "Expectations can not be assigned to the same sequence multiple times."
349 };
350 }
351 }
352
353 m_Sequences = std::tuple{
354 std::move(sequences)...
355 };
356 }
357 };
358 }
359}
360
361namespace mimicpp
362{
372 : public sequence::detail::BasicSequenceInterface<
373 sequence::Id,
374 sequence::detail::LazyStrategy{}>
375 {
376 };
377
387 : public sequence::detail::BasicSequenceInterface<
388 sequence::Id,
389 sequence::detail::GreedyStrategy{}>
390 {
391 };
392
398}
399
400namespace mimicpp::sequence::detail
401{
402 [[nodiscard]]
403 constexpr bool has_better_rating(
404 const std::span<const rating> lhs,
405 const std::span<const rating> rhs
406 ) noexcept
407 {
408 int rating{};
409 for (const auto& [lhsPriority, lhsTag] : lhs)
410 {
411 if (const auto iter = std::ranges::find(rhs, lhsTag, &rating::tag);
412 iter != std::ranges::end(rhs))
413 {
414 rating += lhsPriority < iter->priority
415 ? -1
416 : 1;
417 }
418 else
419 {
420 return true;
421 }
422 }
423
424 return 0 <= rating;
425 }
426}
427
428namespace mimicpp::expect
429{
439 template <typename Id, auto priorityStrategy>
440 [[nodiscard]]
441 constexpr auto in_sequence(sequence::detail::BasicSequenceInterface<Id, priorityStrategy>& sequence) noexcept
442 {
443 using ConfigT = sequence::detail::Config<
444 sequence::detail::BasicSequence<Id, priorityStrategy>>;
445
446 return ConfigT{
447 sequence
448 };
449 }
450
461 template <
462 typename FirstId,
463 auto firstPriorityStrategy,
464 typename SecondId,
465 auto secondPriorityStrategy,
466 typename... OtherIds,
467 auto... otherPriorityStrategies
468 >
469 [[nodiscard]]
470 constexpr auto in_sequences(
471 sequence::detail::BasicSequenceInterface<FirstId, firstPriorityStrategy>& firstSequence,
472 sequence::detail::BasicSequenceInterface<SecondId, secondPriorityStrategy>& secondSequence,
473 sequence::detail::BasicSequenceInterface<OtherIds, otherPriorityStrategies>&... otherSequences
474 )
475 {
476 using ConfigT = sequence::detail::Config<
477 sequence::detail::BasicSequence<FirstId, firstPriorityStrategy>,
478 sequence::detail::BasicSequence<SecondId, secondPriorityStrategy>,
479 sequence::detail::BasicSequence<OtherIds, otherPriorityStrategies>...>;
480
481 return ConfigT{
482 firstSequence,
483 secondSequence,
484 otherSequences...
485 };
486 }
487}
488
489#endif
The greedy sequence interface.
Definition Sequence.hpp:390
The lazy sequence interface.
Definition Sequence.hpp:375
constexpr auto in_sequences(sequence::detail::BasicSequenceInterface< FirstId, firstPriorityStrategy > &firstSequence, sequence::detail::BasicSequenceInterface< SecondId, secondPriorityStrategy > &secondSequence, sequence::detail::BasicSequenceInterface< OtherIds, otherPriorityStrategies > &... otherSequences)
Attaches the expectation onto the listed sequences.
Definition Sequence.hpp:470
constexpr auto in_sequence(sequence::detail::BasicSequenceInterface< Id, priorityStrategy > &sequence) noexcept
Attaches the expectation onto a sequence.
Definition Sequence.hpp:441
LazySequence SequenceT
The default sequence type (LazySequence).
Definition Sequence.hpp:397
Definition ControlPolicy.hpp:266
Definition Fwd.hpp:212
Tag
Definition Fwd.hpp:215
Id
Definition Fwd.hpp:220
Definition BoostTest.hpp:20
constexpr std::underlying_type_t< T > to_underlying(const T value) noexcept
Definition Utility.hpp:73
Tag tag
Definition Fwd.hpp:226