mimic++ v2
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 const auto index = to_underlying(id);
139 assert(m_Cursor <= index);
140
141 auto& element = m_Entries[to_underlying(id)];
142 assert(element == State::unsatisfied);
143 element = State::satisfied;
144 }
145
146 constexpr void set_saturated(const IdT id) noexcept
147 {
148 assert(is_valid(id));
149 const auto index = to_underlying(id);
150 assert(m_Cursor <= index);
151
152 auto& element = m_Entries[index];
153 assert(
154 element == State::unsatisfied
155 || element == State::satisfied);
156 element = State::saturated;
157 }
158
159 [[nodiscard]]
160 constexpr bool is_consumable(const IdT id) const noexcept
161 {
162 assert(is_valid(id));
163
164 const int index = to_underlying(id);
165 const auto state = m_Entries[index];
166 return m_Cursor <= index
167 && std::ranges::all_of(
168 m_Entries.begin() + m_Cursor,
169 m_Entries.begin() + index,
170 [](const State s) noexcept
171 {
172 return s == State::satisfied
173 || s == State::saturated;
174 })
175 && (state == State::unsatisfied
176 || state == State::satisfied);
177 }
178
179 constexpr void consume(const IdT id) noexcept
180 {
181 assert(is_consumable(id));
182
183 m_Cursor = to_underlying(id);
184 }
185
186 [[nodiscard]]
187 constexpr IdT add()
188 {
189 if (!std::in_range<std::underlying_type_t<IdT>>(m_Entries.size()))
190 [[unlikely]]
191 {
192 throw std::runtime_error{
193 "Sequence already holds maximum amount of elements."
194 };
195 }
196
197 m_Entries.emplace_back(State::unsatisfied);
198 return static_cast<IdT>(m_Entries.size() - 1);
199 }
200
201 [[nodiscard]]
202 constexpr Tag tag() const noexcept
203 {
204 return Tag{
205 std::bit_cast<std::ptrdiff_t>(this)
206 };
207 }
208
209 private:
210 enum class State
211 {
212 unsatisfied,
213 satisfied,
214 saturated
215 };
216
217 std::vector<State> m_Entries{};
218 int m_Cursor{};
219
220 [[nodiscard]]
221 constexpr bool is_valid(const IdT id) const noexcept
222 {
223 return 0 <= to_underlying(id)
224 && std::cmp_less(to_underlying(id), m_Entries.size());
225 }
226 };
227
228 class LazyStrategy
229 {
230 public:
231 [[nodiscard]]
232 constexpr int operator ()(const auto id, const int cursor) const noexcept
233 {
234 const auto index = to_underlying(id);
235 assert(std::cmp_less_equal(cursor, index));
236
237 return std::numeric_limits<int>::max()
238 - (static_cast<int>(index) - cursor);
239 }
240 };
241
242 class GreedyStrategy
243 {
244 public:
245 [[nodiscard]]
246 constexpr int operator ()(const auto id, const int cursor) const noexcept
247 {
248 const auto index = to_underlying(id);
249 assert(std::cmp_less_equal(cursor, index));
250
251 return static_cast<int>(index) - cursor;
252 }
253 };
254
255 template <typename Id, auto priorityStrategy>
256 class BasicSequenceInterface
257 {
258 template <typename... Sequences>
259 friend class Config;
260
261 public:
262 using SequenceT = BasicSequence<Id, priorityStrategy>;
263
264 ~BasicSequenceInterface() = default;
265
266 [[nodiscard]]
267 BasicSequenceInterface() = default;
268
269 BasicSequenceInterface(const BasicSequenceInterface&) = delete;
270 BasicSequenceInterface& operator =(const BasicSequenceInterface&) = delete;
271 BasicSequenceInterface(BasicSequenceInterface&&) = delete;
272 BasicSequenceInterface& operator =(BasicSequenceInterface&&) = delete;
273
274 [[nodiscard]]
275 constexpr Tag tag() const noexcept
276 {
277 return m_Sequence->tag();
278 }
279
280 private:
281 std::shared_ptr<SequenceT> m_Sequence{
282 std::make_shared<SequenceT>()
283 };
284 };
285
286 template <typename... Sequences>
287 class Config
288 {
289 template <typename... Ts>
290 friend class Config;
291
292 public:
293 static constexpr std::size_t sequenceCount = sizeof...(Sequences);
294
295 [[nodiscard]]
296 Config()
297 requires (0 == sizeof...(Sequences))
298 = default;
299
300 template <typename... Interfaces>
301 requires (sizeof...(Interfaces) == sequenceCount)
302 [[nodiscard]]
303 explicit constexpr Config(Interfaces&... interfaces) noexcept(1 == sequenceCount)
304 : Config{
305 interfaces.m_Sequence...
306 }
307 {
308 }
309
310 [[nodiscard]]
311 constexpr auto& sequences() const noexcept
312 {
313 return m_Sequences;
314 }
315
316 template <typename... OtherSequences>
317 [[nodiscard]]
318 constexpr Config<Sequences..., OtherSequences...> concat(
319 const Config<OtherSequences...>& other
320 ) const
321 {
322 return std::apply(
323 [](auto... sequences)
324 {
325 return Config<Sequences..., OtherSequences...>{
326 std::move(sequences)...
327 };
328 },
329 std::tuple_cat(m_Sequences, other.sequences()));
330 }
331
332 private:
333 std::tuple<std::shared_ptr<Sequences>...> m_Sequences;
334
335 [[nodiscard]]
336 explicit constexpr Config(std::shared_ptr<Sequences>... sequences) noexcept(1 == sequenceCount)
337 requires (0 < sequenceCount)
338 {
339 if constexpr (1 < sequenceCount)
340 {
341 std::array tags{
342 sequences->tag()...
343 };
344
345 std::ranges::sort(tags);
346 if (!std::ranges::empty(std::ranges::unique(tags)))
347 {
348 throw std::invalid_argument{
349 "Expectations can not be assigned to the same sequence multiple times."
350 };
351 }
352 }
353
354 m_Sequences = std::tuple{
355 std::move(sequences)...
356 };
357 }
358 };
359 }
360}
361
362namespace mimicpp
363{
373 : public sequence::detail::BasicSequenceInterface<
374 sequence::Id,
375 sequence::detail::LazyStrategy{}>
376 {
377 };
378
388 : public sequence::detail::BasicSequenceInterface<
389 sequence::Id,
390 sequence::detail::GreedyStrategy{}>
391 {
392 };
393
399}
400
401namespace mimicpp::sequence::detail
402{
403 [[nodiscard]]
404 constexpr bool has_better_rating(
405 const std::span<const rating> lhs,
406 const std::span<const rating> rhs
407 ) noexcept
408 {
409 int rating{};
410 for (const auto& [lhsPriority, lhsTag] : lhs)
411 {
412 if (const auto iter = std::ranges::find(rhs, lhsTag, &rating::tag);
413 iter != std::ranges::end(rhs))
414 {
415 rating += lhsPriority < iter->priority
416 ? -1
417 : 1;
418 }
419 else
420 {
421 return true;
422 }
423 }
424
425 return 0 <= rating;
426 }
427}
428
429namespace mimicpp::expect
430{
440 template <typename Id, auto priorityStrategy>
441 [[nodiscard]]
442 constexpr auto in_sequence(sequence::detail::BasicSequenceInterface<Id, priorityStrategy>& sequence) noexcept
443 {
444 using ConfigT = sequence::detail::Config<
445 sequence::detail::BasicSequence<Id, priorityStrategy>>;
446
447 return ConfigT{
448 sequence
449 };
450 }
451
462 template <
463 typename FirstId,
464 auto firstPriorityStrategy,
465 typename SecondId,
466 auto secondPriorityStrategy,
467 typename... OtherIds,
468 auto... otherPriorityStrategies
469 >
470 [[nodiscard]]
471 constexpr auto in_sequences(
472 sequence::detail::BasicSequenceInterface<FirstId, firstPriorityStrategy>& firstSequence,
473 sequence::detail::BasicSequenceInterface<SecondId, secondPriorityStrategy>& secondSequence,
474 sequence::detail::BasicSequenceInterface<OtherIds, otherPriorityStrategies>&... otherSequences
475 )
476 {
477 using ConfigT = sequence::detail::Config<
478 sequence::detail::BasicSequence<FirstId, firstPriorityStrategy>,
479 sequence::detail::BasicSequence<SecondId, secondPriorityStrategy>,
480 sequence::detail::BasicSequence<OtherIds, otherPriorityStrategies>...>;
481
482 return ConfigT{
483 firstSequence,
484 secondSequence,
485 otherSequences...
486 };
487 }
488}
489
490#endif
The greedy sequence interface.
Definition Sequence.hpp:391
The lazy sequence interface.
Definition Sequence.hpp:376
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:471
constexpr auto in_sequence(sequence::detail::BasicSequenceInterface< Id, priorityStrategy > &sequence) noexcept
Attaches the expectation onto a sequence.
Definition Sequence.hpp:442
LazySequence SequenceT
The default sequence type (LazySequence).
Definition Sequence.hpp:398
Definition ControlPolicy.hpp:267
Definition Fwd.hpp:116
Tag
Definition Fwd.hpp:119
Id
Definition Fwd.hpp:124
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:130