11#ifndef LIB_ENUMERATOR_H_
12#define LIB_ENUMERATOR_H_
16#include <initializer_list>
23#include "iterator_range.h"
26enum class EnumeratorState { NotStarted, Valid, PastEnd };
39class EnumeratorHandle {
42 explicit EnumeratorHandle(
Enumerator<T> *enumerator) : enumerator(enumerator) {}
46 using iterator_category = std::input_iterator_tag;
47 using difference_type = std::ptrdiff_t;
52 reference operator*()
const;
53 const EnumeratorHandle<T> &operator++();
54 bool operator==(
const EnumeratorHandle<T> &other)
const;
55 bool operator!=(
const EnumeratorHandle<T> &other)
const;
62 EnumeratorState state = EnumeratorState::NotStarted;
66 friend class Enumerator;
67 static std::vector<T> emptyVector;
69 friend class EnumeratorHandle;
74 Enumerator() { this->
reset(); }
76 virtual ~Enumerator() =
default;
84 virtual void reset() { this->state = EnumeratorState::NotStarted; }
90 EnumeratorHandle<T> end() {
return EnumeratorHandle<T>(
nullptr); }
92 const char *stateName()
const {
93 switch (this->state) {
94 case EnumeratorState::NotStarted:
96 case EnumeratorState::Valid:
98 case EnumeratorState::PastEnd:
101 throw std::logic_error(
"Unexpected state " + std::to_string(
static_cast<int>(this->state)));
105 template <
typename Container>
107 "Use Util::enumerate() instead")]]
static Enumerator<typename Container::value_type> *
108 createEnumerator(
const Container &data);
109 static Enumerator<T> *emptyEnumerator();
110 template <
typename Iter>
111 [[deprecated(
"Use Util::enumerate() instead")]]
static Enumerator<typename Iter::value_type> *
112 createEnumerator(Iter begin, Iter end);
113 template <
typename Iter>
114 [[deprecated(
"Use Util::enumerate() instead")]]
static Enumerator<typename Iter::value_type> *
115 createEnumerator(iterator_range<Iter> range);
118 template <
typename Filter>
119 Enumerator<T> *
where(Filter filter);
121 template <
typename Mapper>
122 Enumerator<std::invoke_result_t<Mapper, T>> *
map(Mapper
map);
124 template <
typename S>
127 virtual Enumerator<T> *
concat(Enumerator<T> *other);
129 static Enumerator<T> *
concatAll(Enumerator<Enumerator<T> *> *inputs);
131 std::vector<T> toVector() {
132 std::vector<T> result;
150 if (!
next)
throw std::logic_error(
"There is no element for `single()'");
153 if (
next)
throw std::logic_error(
"There are multiple elements when calling `single()'");
161 if (!
next)
return T{};
164 if (
next)
throw std::logic_error(
"There are multiple elements when calling `single()'");
171 if (!
next)
return T{};
178 if (!
next)
throw std::logic_error(
"There is no element for `next()'");
190template <
typename Iter>
191class IteratorEnumerator :
public Enumerator<typename std::iterator_traits<Iter>::value_type> {
197 friend class Enumerator<typename
std::iterator_traits<Iter>::value_type>;
200 IteratorEnumerator(Iter begin, Iter end, const char *name)
201 : Enumerator<typename std::iterator_traits<Iter>::value_type>(),
207 [[nodiscard]] std::string toString()
const {
208 return std::string(this->name) +
":" + this->stateName();
212 switch (this->state) {
213 case EnumeratorState::NotStarted:
214 this->current = this->begin;
215 if (this->current == this->end) {
216 this->state = EnumeratorState::PastEnd;
219 this->state = EnumeratorState::Valid;
222 case EnumeratorState::PastEnd:
224 case EnumeratorState::Valid:
226 if (this->current == this->end) {
227 this->state = EnumeratorState::PastEnd;
233 throw std::runtime_error(
"Unexpected enumerator state");
236 typename std::iterator_traits<Iter>::value_type
getCurrent()
const override {
237 switch (this->state) {
238 case EnumeratorState::NotStarted:
239 throw std::logic_error(
"You cannot call 'getCurrent' before 'moveNext'");
240 case EnumeratorState::PastEnd:
241 throw std::logic_error(
"You cannot call 'getCurrent' past the collection end");
242 case EnumeratorState::Valid:
243 return *this->current;
245 throw std::runtime_error(
"Unexpected enumerator state");
249template <
typename Iter>
250IteratorEnumerator(Iter begin, Iter end,
const char *name) -> IteratorEnumerator<Iter>;
255class SingleEnumerator :
public Enumerator<T> {
259 explicit SingleEnumerator(T v) : Enumerator<T>(), value(v) {}
261 switch (this->state) {
262 case EnumeratorState::NotStarted:
263 this->state = EnumeratorState::Valid;
265 case EnumeratorState::PastEnd:
267 case EnumeratorState::Valid:
268 this->state = EnumeratorState::PastEnd;
271 throw std::runtime_error(
"Unexpected enumerator state");
274 switch (this->state) {
275 case EnumeratorState::NotStarted:
276 throw std::logic_error(
"You cannot call 'getCurrent' before 'moveNext'");
277 case EnumeratorState::PastEnd:
278 throw std::logic_error(
"You cannot call 'getCurrent' past the collection end");
279 case EnumeratorState::Valid:
282 throw std::runtime_error(
"Unexpected enumerator state");
292 [[nodiscard]] std::string toString()
const {
return "EmptyEnumerator"; }
296 throw std::logic_error(
"You cannot call 'getCurrent' on an EmptyEnumerator");
307template <
typename T,
typename Filter>
308class FilterEnumerator final :
public Enumerator<T> {
309 Enumerator<T> *input;
314 FilterEnumerator(Enumerator<T> *input, Filter filter)
315 : input(input), filter(std::move(filter)) {}
319 this->state = EnumeratorState::Valid;
320 while (this->input->moveNext()) {
321 this->current = this->input->getCurrent();
322 bool match = this->filter(this->current);
323 if (match)
return true;
325 this->state = EnumeratorState::PastEnd;
330 [[nodiscard]] std::string toString()
const {
331 return "FilterEnumerator(" + this->input->toString() +
"):" + this->stateName();
335 this->input->reset();
340 switch (this->state) {
341 case EnumeratorState::NotStarted:
342 case EnumeratorState::Valid:
343 return this->advance();
344 case EnumeratorState::PastEnd:
347 throw std::runtime_error(
"Unexpected enumerator state");
351 switch (this->state) {
352 case EnumeratorState::NotStarted:
353 throw std::logic_error(
"You cannot call 'getCurrent' before 'moveNext'");
354 case EnumeratorState::PastEnd:
355 throw std::logic_error(
"You cannot call 'getCurrent' past the collection end");
356 case EnumeratorState::Valid:
357 return this->current;
359 throw std::runtime_error(
"Unexpected enumerator state");
371template <
typename From,
typename To,
typename =
void>
372static constexpr bool can_be_casted =
false;
374template <
typename From,
typename To>
376 can_be_casted<From *, To *, std::void_t<decltype(std::declval<From *>()->template to<To>())>> =
381template <
typename T,
typename S>
382class AsEnumerator final :
public Enumerator<S> {
383 template <
typename U = S>
384 typename std::enable_if_t<!Detail::can_be_casted<T, S>, U> getCurrentImpl()
const {
385 T current = input->getCurrent();
386 return dynamic_cast<S
>(current);
389 template <
typename U = S>
390 typename std::enable_if_t<Detail::can_be_casted<T, S>, U> getCurrentImpl()
const {
391 T current = input->getCurrent();
392 return current->template to<std::remove_pointer_t<S>>();
396 Enumerator<T> *input;
399 explicit AsEnumerator(Enumerator<T> *input) : input(input) {}
401 std::string toString()
const {
402 return "AsEnumerator(" + this->input->toString() +
"):" + this->stateName();
407 this->input->
reset();
411 bool result = this->input->
moveNext();
413 this->state = EnumeratorState::Valid;
415 this->state = EnumeratorState::PastEnd;
425template <
typename T,
typename S,
typename Mapper>
426class MapEnumerator final :
public Enumerator<S> {
428 Enumerator<T> *input;
433 MapEnumerator(Enumerator<T> *input, Mapper map) : input(input), map(std::move(map)) {}
436 this->input->
reset();
440 [[nodiscard]] std::string toString()
const {
441 return "MapEnumerator(" + this->input->toString() +
"):" + this->stateName();
445 switch (this->state) {
446 case EnumeratorState::NotStarted:
447 case EnumeratorState::Valid: {
448 bool success = input->moveNext();
451 this->current = this->map(currentInput);
452 this->state = EnumeratorState::Valid;
455 this->state = EnumeratorState::PastEnd;
459 case EnumeratorState::PastEnd:
462 throw std::runtime_error(
"Unexpected enumerator state");
466 switch (this->state) {
467 case EnumeratorState::NotStarted:
468 throw std::logic_error(
"You cannot call 'getCurrent' before 'moveNext'");
469 case EnumeratorState::PastEnd:
470 throw std::logic_error(
"You cannot call 'getCurrent' past the collection end");
471 case EnumeratorState::Valid:
472 return this->current;
474 throw std::runtime_error(
"Unexpected enumerator state");
478template <
typename T,
typename Mapper>
479MapEnumerator(Enumerator<T> *,
480 Mapper) -> MapEnumerator<T, typename std::invoke_result_t<Mapper, T>, Mapper>;
486class ConcatEnumerator final :
public Enumerator<T> {
487 std::vector<Enumerator<T> *> inputs;
491 ConcatEnumerator() =
default;
493 explicit ConcatEnumerator(std::vector<Enumerator<T> *> &&inputs) : inputs(std::move(inputs)) {
494 for (
auto *currentInput : inputs)
495 if (currentInput ==
nullptr)
throw std::logic_error(
"Null iterator in concatenation");
498 ConcatEnumerator(std::initializer_list<Enumerator<T> *> inputs) : inputs(inputs) {
499 for (
auto *currentInput : inputs)
500 if (currentInput ==
nullptr)
throw std::logic_error(
"Null iterator in concatenation");
502 explicit ConcatEnumerator(Enumerator<Enumerator<T> *> *inputs)
503 : ConcatEnumerator(inputs->toVector()) {}
505 [[nodiscard]] std::string toString()
const {
return "ConcatEnumerator:" + this->stateName(); }
509 this->state = EnumeratorState::Valid;
510 for (
auto *currentInput : inputs) {
511 if (currentInput->moveNext()) {
512 this->currentResult = currentInput->getCurrent();
517 this->state = EnumeratorState::PastEnd;
522 Enumerator<T> *
concat(Enumerator<T> *other)
override {
524 if (this->state == EnumeratorState::PastEnd)
525 throw std::runtime_error(
"Invalid enumerator state to concatenate");
527 inputs.push_back(other);
533 for (
auto *currentInput : inputs) currentInput->reset();
538 switch (this->state) {
539 case EnumeratorState::NotStarted:
540 case EnumeratorState::Valid:
541 return this->advance();
542 case EnumeratorState::PastEnd:
545 throw std::runtime_error(
"Unexpected enumerator state");
549 switch (this->state) {
550 case EnumeratorState::NotStarted:
551 throw std::logic_error(
"You cannot call 'getCurrent' before 'moveNext'");
552 case EnumeratorState::PastEnd:
553 throw std::logic_error(
"You cannot call 'getCurrent' past the collection end");
554 case EnumeratorState::Valid:
555 return this->currentResult;
557 throw std::runtime_error(
"Unexpected enumerator state");
564template <
typename Mapper>
576template <
typename Filter>
582template <
typename Container>
588Enumerator<T> *Enumerator<T>::emptyEnumerator() {
589 return new EmptyEnumerator<T>();
593template <
typename Iter>
594Enumerator<typename Iter::value_type> *Enumerator<T>::createEnumerator(Iter begin, Iter end) {
595 return new IteratorEnumerator(begin, end,
"iterator");
599template <
typename Iter>
617T EnumeratorHandle<T>::operator*()
const {
618 if (enumerator ==
nullptr)
throw std::logic_error(
"Dereferencing end() iterator");
619 return enumerator->getCurrent();
623const EnumeratorHandle<T> &EnumeratorHandle<T>::operator++() {
624 enumerator->moveNext();
629bool EnumeratorHandle<T>::operator==(
const EnumeratorHandle<T> &other)
const {
630 return !(*
this != other);
635 if (this->enumerator == other.enumerator)
return true;
636 if (other.enumerator !=
nullptr)
throw std::logic_error(
"Comparison with different iterator");
637 return this->enumerator->state == EnumeratorState::Valid;
640template <
typename Iter>
645template <
typename Iter>
650template <
typename Container>
663template <
typename... Args>
664auto concat(Args &&...inputs) {
665 using FirstEnumeratorTy =
666 std::remove_pointer_t<std::decay_t<std::tuple_element_t<0, std::tuple<Args...>>>>;
667 std::initializer_list<Enumerator<typename FirstEnumeratorTy::value_type> *> init{
668 std::forward<Args>(inputs)...};
Casts each element.
Definition enumerator.h:382
S getCurrent() const override
Get current element in the collection.
Definition enumerator.h:419
void reset() override
Move back to the beginning of the collection.
Definition enumerator.h:405
bool moveNext() override
Definition enumerator.h:410
Concatenation.
Definition enumerator.h:486
void reset() override
Move back to the beginning of the collection.
Definition enumerator.h:532
bool moveNext() override
Definition enumerator.h:537
T getCurrent() const override
Get current element in the collection.
Definition enumerator.h:548
Enumerator< T > * concat(Enumerator< T > *other) override
Append all elements of other after all elements of this.
Definition enumerator.h:522
Always empty iterator (equivalent to end())
Definition enumerator.h:290
bool moveNext() override
Always returns false.
Definition enumerator.h:294
T getCurrent() const override
Get current element in the collection.
Definition enumerator.h:295
Definition enumerator.h:39
Type-erased Enumerator interface.
Definition enumerator.h:60
Enumerator< S > * as()
Cast to an enumerator of S objects.
Definition enumerator.h:571
Enumerator< std::invoke_result_t< Mapper, T > > * map(Mapper map)
Apply specified function to all elements of this enumerator.
Definition enumerator.h:565
virtual bool moveNext()=0
virtual Enumerator< T > * concat(Enumerator< T > *other)
Append all elements of other after all elements of this.
Definition enumerator.h:610
T nextOrDefault()
Next element, or the default value if none exists.
Definition enumerator.h:169
T single()
The only next element; throws if the enumerator does not have exactly 1 element.
Definition enumerator.h:148
virtual void reset()
Move back to the beginning of the collection.
Definition enumerator.h:84
bool any()
True if the enumerator has at least one element.
Definition enumerator.h:145
virtual T getCurrent() const =0
Get current element in the collection.
T next()
Next element; throws if there are no elements.
Definition enumerator.h:176
uint64_t count()
Enumerate all elements and return the count.
Definition enumerator.h:138
T singleOrDefault()
Definition enumerator.h:159
Enumerator< T > * where(Filter filter)
Return an enumerator returning all elements that pass the filter.
Definition enumerator.h:577
static Enumerator< T > * concatAll(Enumerator< Enumerator< T > * > *inputs)
Concatenate all these collections into a single one.
Definition enumerator.h:605
Definition enumerator.h:308
T getCurrent() const
Get current element in the collection.
Definition enumerator.h:350
void reset()
Move back to the beginning of the collection.
Definition enumerator.h:334
bool moveNext()
Definition enumerator.h:339
A generic iterator returning elements of type T.
Definition enumerator.h:191
bool moveNext() override
Definition enumerator.h:211
std::iterator_traits< Iter >::value_type getCurrent() const override
Get current element in the collection.
Definition enumerator.h:236
Transforms all elements from type T to type S.
Definition enumerator.h:426
void reset()
Move back to the beginning of the collection.
Definition enumerator.h:435
bool moveNext()
Definition enumerator.h:444
S getCurrent() const
Get current element in the collection.
Definition enumerator.h:465
bool moveNext()
Definition enumerator.h:260
T getCurrent() const
Get current element in the collection.
Definition enumerator.h:273
Definition iterator_range.h:44