6#ifndef MIMICPP_SEQUENCE_HPP
7#define MIMICPP_SEQUENCE_HPP
21#ifndef MIMICPP_DETAIL_IS_MODULE
88namespace mimicpp::sequence::detail
90 template <
typename Id, auto priorityStrategy>
91 requires std::is_enum_v<Id>
92 && std::signed_integral<std::underlying_type_t<Id>>
93 && std::convertible_to<
94 std::invoke_result_t<
decltype(priorityStrategy),
Id,
int>,
99 BasicSequence(BasicSequence
const&) =
delete;
100 BasicSequence& operator=(BasicSequence
const&) =
delete;
101 BasicSequence(BasicSequence&&) =
delete;
102 BasicSequence& operator=(BasicSequence&&) =
delete;
104 ~BasicSequence() noexcept(false)
106 auto const iter = std::ranges::find_if_not(m_Entries, &Entry::is_fulfilled);
107 auto const satisfiedCount = std::ranges::distance(m_Entries.cbegin(), iter);
108 MIMICPP_ASSERT(m_Cursor <= satisfiedCount,
"Cursor skipped unsatisfied entries.");
110 if (iter != m_Entries.cend())
112 reporting::detail::report_error(
114 "Unfulfilled sequence. {} out of {} expectation(s) are satisfied.",
121 explicit constexpr BasicSequence(util::SourceLocation loc = {})
noexcept
122 : m_Loc{std::move(loc)}
127 constexpr util::SourceLocation
const& from() const noexcept
133 constexpr std::optional<util::SourceLocation> head_from()
const
135 auto const pending = std::span{m_Entries}.subspan(m_Cursor);
136 auto const iter = std::ranges::find_if(pending, &Entry::is_active);
137 if (iter != pending.end())
146 constexpr std::optional<int> priority_of(
Id const id)
const noexcept
150 if (is_consumable(
id))
152 return std::invoke(priorityStrategy,
id, m_Cursor);
158 constexpr void set_satisfied(
Id const id)
noexcept
164 MIMICPP_ASSERT(element.is_unsatisfied(),
"Element is in unexpected state.");
165 element.state = State::satisfied;
168 constexpr void set_saturated(
Id const id)
noexcept
174 auto& element = m_Entries[index];
175 MIMICPP_ASSERT(element.is_active(),
"Element is in unexpected state.");
176 element.state = State::saturated;
180 constexpr bool is_consumable(
Id const id)
const noexcept
184 std::span
const pending = std::span{m_Entries}.subspan(m_Cursor);
188 && std::ranges::all_of(pending.first(index), &Entry::is_fulfilled)
189 && pending[index].is_active();
192 constexpr void consume(
Id const id)
noexcept
194 MIMICPP_ASSERT(is_consumable(
id),
"Sequence is not in consumable state.");
200 constexpr Id add(util::SourceLocation info)
202 if (!std::in_range<std::underlying_type_t<Id>>(m_Entries.size()))
205 throw std::runtime_error{
"Sequence already holds maximum amount of elements."};
208 m_Entries.emplace_back(State::unsatisfied, std::move(info));
210 return static_cast<Id>(m_Entries.size() - 1);
214 constexpr Tag tag() const noexcept
220 util::SourceLocation m_Loc;
231 State state{State::unsatisfied};
232 util::SourceLocation loc{};
235 constexpr bool is_fulfilled() const noexcept
237 return state == State::satisfied
238 || state == State::saturated;
242 constexpr bool is_active() const noexcept
244 return state == State::unsatisfied
245 || state == State::satisfied;
249 constexpr bool is_unsatisfied() const noexcept
251 return state == State::unsatisfied;
255 std::vector<Entry> m_Entries{};
259 constexpr bool is_valid(
Id const id)
const noexcept
264 && index < std::ssize(m_Entries);
272 constexpr int operator()(
auto const id,
int const cursor)
const noexcept
275 MIMICPP_ASSERT(std::cmp_less_equal(cursor, index),
"Invalid state.");
277 return std::numeric_limits<int>::max()
286 constexpr int operator()(
auto const id,
int const cursor)
const noexcept
289 MIMICPP_ASSERT(std::cmp_less_equal(cursor, index),
"Invalid state.");
291 return index - cursor;
295 template <
typename... Sequences>
298 template <
typename Id, auto priorityStrategy>
299 class BasicSequenceInterface
301 using Sequence = BasicSequence<Id, priorityStrategy>;
304 BasicSequenceInterface(
const BasicSequenceInterface&) =
delete;
305 BasicSequenceInterface& operator=(
const BasicSequenceInterface&) =
delete;
307 ~BasicSequenceInterface() =
default;
310 explicit BasicSequenceInterface(util::SourceLocation loc = {})
311 : m_Sequence{std::make_shared<Sequence>(std::move(loc))}
316 BasicSequenceInterface(BasicSequenceInterface&&) =
default;
317 BasicSequenceInterface& operator=(BasicSequenceInterface&&) =
default;
320 constexpr Tag tag() const noexcept
322 return m_Sequence->tag();
326 util::SourceLocation
const& from() const noexcept
328 return m_Sequence->from();
332 std::optional<util::SourceLocation> head_from()
const
334 return m_Sequence->head_from();
337 template <
typename... Sequences>
338 requires util::same_as_any<Sequence, Sequences...>
339 constexpr std::shared_ptr<Sequence> sequence([[maybe_unused]] util::pass_key<Config<Sequences...>> key)
const
345 std::shared_ptr<Sequence> m_Sequence;
348 template <
typename... Sequences>
351 static constexpr util::pass_key<Config> selfKey{};
354 static constexpr std::size_t sequenceCount =
sizeof...(Sequences);
358 requires(0u == sequenceCount)
361 template <
typename First,
typename... Others>
362 requires(1u +
sizeof...(Others) == sequenceCount)
363 && (!std::same_as<util::pass_key<Config>, std::remove_cvref_t<First>>)
364 && (!std::same_as<Config, std::remove_cvref_t<First>>)
366 explicit constexpr Config(First& firstInterface, Others&... interfaces)
noexcept(1u == sequenceCount)
369 firstInterface.sequence(selfKey),
370 interfaces.sequence(selfKey)...}
374 template <
typename... OtherSequences>
376 explicit constexpr Config(
377 [[maybe_unused]] util::pass_key<Config<OtherSequences...>>
const key,
378 std::shared_ptr<Sequences>... sequences)
379 noexcept(1u == sequenceCount)
381 if constexpr (1u < sequenceCount)
383 std::array tags{sequences->tag()...};
385 std::ranges::sort(tags);
386 if (!std::ranges::empty(std::ranges::unique(tags)))
388 throw std::invalid_argument{
389 "Expectations can not be assigned to the same sequence multiple times."};
393 m_Sequences = std::tuple{std::move(sequences)...};
397 constexpr auto& sequences() const noexcept
402 template <
typename... OtherSequences>
404 constexpr Config<Sequences..., OtherSequences...> concat(Config<OtherSequences...>
const& other)
const
406 return std::make_from_tuple<Config<Sequences..., OtherSequences...>>(
407 std::tuple_cat(std::make_tuple(selfKey), m_Sequences, other.sequences()));
411 std::tuple<std::shared_ptr<Sequences>...> m_Sequences;
426 :
public sequence::detail::BasicSequenceInterface<
428 sequence::detail::LazyStrategy{}>
433 : BasicSequenceInterface{std::move(loc)}
435 static_assert(0u ==
sizeof...(canary),
"LazySequence does not accept constructor arguments.");
448 :
public sequence::detail::BasicSequenceInterface<
450 sequence::detail::GreedyStrategy{}>
455 : BasicSequenceInterface{std::move(loc)}
457 static_assert(0u ==
sizeof...(canary),
"GreedySequence does not accept constructor arguments.");
466 using SequenceT [[deprecated(
"Please use mimicpp::Sequence instead.")]] =
LazySequence;
475namespace mimicpp::sequence::detail
478 constexpr bool has_better_rating(
479 std::span<rating const>
const lhs,
480 std::span<rating const>
const rhs)
noexcept
483 for (
auto const& [lhsPriority, lhsTag] : lhs)
485 if (
auto const iter = std::ranges::find(rhs, lhsTag, &
rating::tag);
486 iter != std::ranges::end(rhs))
488 rating += lhsPriority < iter->priority ? -1 : 1;
511 template <
typename Id, auto priorityStrategy>
515 using Config = sequence::detail::Config<
516 sequence::detail::BasicSequence<Id, priorityStrategy>>;
533 auto firstPriorityStrategy,
535 auto secondPriorityStrategy,
536 typename... OtherIds,
537 auto... otherPriorityStrategies>
540 sequence::detail::BasicSequenceInterface<FirstId, firstPriorityStrategy>& firstSequence,
541 sequence::detail::BasicSequenceInterface<SecondId, secondPriorityStrategy>& secondSequence,
542 sequence::detail::BasicSequenceInterface<OtherIds, otherPriorityStrategies>&... otherSequences)
544 using Config = sequence::detail::Config<
545 sequence::detail::BasicSequence<FirstId, firstPriorityStrategy>,
546 sequence::detail::BasicSequence<SecondId, secondPriorityStrategy>,
547 sequence::detail::BasicSequence<OtherIds, otherPriorityStrategies>...>;
549 return Config{firstSequence, secondSequence, otherSequences...};
#define MIMICPP_ASSERT(condition, msg)
Definition Config.hpp:51
#define MIMICPP_DETAIL_MODULE_EXPORT
Definition Config.hpp:19
GreedySequence(auto &&... canary, util::SourceLocation loc={})
Definition Sequence.hpp:454
The lazy sequence interface.
Definition Sequence.hpp:429
LazySequence(auto &&... canary, util::SourceLocation loc={})
Definition Sequence.hpp:432
A thin wrapper around general source-location info.
Definition SourceLocation.hpp:38
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:539
LazySequence Sequence
The default sequence type (LazySequence).
Definition Sequence.hpp:472
constexpr auto in_sequence(sequence::detail::BasicSequenceInterface< Id, priorityStrategy > &sequence)
Attaches the expectation onto a sequence.
Definition Sequence.hpp:513
Definition ArgRequirementPolicies.hpp:125
Tag
Definition Fwd.hpp:402
constexpr std::underlying_type_t< T > to_underlying(T const value) noexcept
Definition C++23Backports.hpp:62
std::enable_if_t< sizeof(To)==sizeof(From) &&std::is_trivially_copyable_v< From > &&std::is_trivially_copyable_v< To >, To > bit_cast(From const &src) noexcept
Definition C++20Compatibility.hpp:45
Tag tag
Definition Fwd.hpp:412