P4C
The P4 Compiler
 
Loading...
Searching...
No Matches
enumerator.h
1/*
2Copyright 2013-present Barefoot Networks, Inc.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17/* -*-c++-*-
18 C#-like enumerator interface */
19
20#ifndef LIB_ENUMERATOR_H_
21#define LIB_ENUMERATOR_H_
22
23#include <cstdint>
24#include <functional>
25#include <initializer_list>
26#include <iterator>
27#include <stdexcept>
28#include <string>
29#include <type_traits>
30#include <vector>
31
32#include "iterator_range.h"
33
34namespace Util {
35enum class EnumeratorState { NotStarted, Valid, PastEnd };
36
37template <typename T>
38class Enumerator;
39
43// FIXME: It is not a proper iterator (see reference type above) and should be removed
44// in favor of more standard approach. Note that Enumerator<T>::getCurrent() always
45// returns element by value, so more or less suitable only for copyable types that are cheap
46// to copy.
47template <typename T>
49 private:
50 Enumerator<T> *enumerator = nullptr; // when nullptr it represents end()
51 explicit EnumeratorHandle(Enumerator<T> *enumerator) : enumerator(enumerator) {}
52 friend class Enumerator<T>;
53
54 public:
55 using iterator_category = std::input_iterator_tag;
56 using difference_type = std::ptrdiff_t;
57 using value_type = T;
58 using reference = T;
59 using pointer = void;
60
61 reference operator*() const;
62 const EnumeratorHandle<T> &operator++();
63 bool operator!=(const EnumeratorHandle<T> &other) const;
64};
65
67template <class T>
69 protected:
70 EnumeratorState state = EnumeratorState::NotStarted;
71
72 // This is a weird oddity of C++: this class is a friend of itself with different templates
73 template <class S>
74 friend class Enumerator;
75 static std::vector<T> emptyVector;
76 template <typename S>
77 friend class EnumeratorHandle;
78
79 public:
80 using value_type = T;
81
82 Enumerator() { this->reset(); }
83
84 virtual ~Enumerator() = default;
85
88 virtual bool moveNext() = 0;
90 virtual T getCurrent() const = 0;
92 virtual void reset() { this->state = EnumeratorState::NotStarted; }
93
94 EnumeratorHandle<T> begin() {
95 this->moveNext();
96 return EnumeratorHandle<T>(this);
97 }
98 EnumeratorHandle<T> end() { return EnumeratorHandle<T>(nullptr); }
99
100 const char *stateName() const {
101 switch (this->state) {
102 case EnumeratorState::NotStarted:
103 return "NotStarted";
104 case EnumeratorState::Valid:
105 return "Valid";
106 case EnumeratorState::PastEnd:
107 return "PastEnd";
108 }
109 throw std::logic_error("Unexpected state " + std::to_string(static_cast<int>(this->state)));
110 }
111
113 template <typename Container>
114 [[deprecated(
115 "Use Util::enumerate() instead")]] static Enumerator<typename Container::value_type> *
116 createEnumerator(const Container &data);
117 static Enumerator<T> *emptyEnumerator(); // empty data
118 template <typename Iter>
119 [[deprecated("Use Util::enumerate() instead")]] static Enumerator<typename Iter::value_type> *
120 createEnumerator(Iter begin, Iter end);
121 template <typename Iter>
122 [[deprecated("Use Util::enumerate() instead")]] static Enumerator<typename Iter::value_type> *
123 createEnumerator(iterator_range<Iter> range);
124
126 template <typename Filter>
127 Enumerator<T> *where(Filter filter);
129 template <typename Mapper>
132 template <typename S>
138
139 std::vector<T> toVector() {
140 std::vector<T> result;
141 while (moveNext()) result.push_back(getCurrent());
142 return result;
143 }
144
146 uint64_t count() {
147 uint64_t found = 0;
148 while (this->moveNext()) found++;
149 return found;
150 }
151
153 bool any() { return this->moveNext(); }
154
156 T single() {
157 bool next = moveNext();
158 if (!next) throw std::logic_error("There is no element for `single()'");
159 T result = getCurrent();
160 next = moveNext();
161 if (next) throw std::logic_error("There are multiple elements when calling `single()'");
162 return result;
163 }
164
168 bool next = moveNext();
169 if (!next) return T{};
170 T result = getCurrent();
171 next = moveNext();
172 if (next) throw std::logic_error("There are multiple elements when calling `single()'");
173 return result;
174 }
175
178 bool next = moveNext();
179 if (!next) return T{};
180 return getCurrent();
181 }
182
184 T next() {
185 bool next = moveNext();
186 if (!next) throw std::logic_error("There is no element for `next()'");
187 return getCurrent();
188 }
189};
190
192// the implementation must be in the header file due to the templates
193
196
198template <typename Iter>
199class IteratorEnumerator : public Enumerator<typename Iter::value_type> {
200 protected:
201 Iter begin;
202 Iter end;
203 Iter current;
204 const char *name;
205 friend class Enumerator<typename Iter::value_type>;
206
207 public:
208 IteratorEnumerator(Iter begin, Iter end, const char *name)
209 : Enumerator<typename Iter::value_type>(),
210 begin(begin),
211 end(end),
212 current(begin),
213 name(name) {}
214
215 [[nodiscard]] std::string toString() const {
216 return std::string(this->name) + ":" + this->stateName();
217 }
218
219 bool moveNext() {
220 switch (this->state) {
221 case EnumeratorState::NotStarted:
222 this->current = this->begin;
223 if (this->current == this->end) {
224 this->state = EnumeratorState::PastEnd;
225 return false;
226 } else {
227 this->state = EnumeratorState::Valid;
228 }
229 return true;
230 case EnumeratorState::PastEnd:
231 return false;
232 case EnumeratorState::Valid:
233 ++this->current;
234 if (this->current == this->end) {
235 this->state = EnumeratorState::PastEnd;
236 return false;
237 }
238 return true;
239 }
240
241 throw std::runtime_error("Unexpected enumerator state");
242 }
243
244 typename Iter::value_type getCurrent() const {
245 switch (this->state) {
246 case EnumeratorState::NotStarted:
247 throw std::logic_error("You cannot call 'getCurrent' before 'moveNext'");
248 case EnumeratorState::PastEnd:
249 throw std::logic_error("You cannot call 'getCurrent' past the collection end");
250 case EnumeratorState::Valid:
251 return *this->current;
252 }
253 throw std::runtime_error("Unexpected enumerator state");
254 }
255};
256
258
260template <typename T>
261class EmptyEnumerator : public Enumerator<T> {
262 public:
263 [[nodiscard]] std::string toString() const { return "EmptyEnumerator"; }
265 bool moveNext() { return false; }
266 T getCurrent() const {
267 throw std::logic_error("You cannot call 'getCurrent' on an EmptyEnumerator");
268 }
269};
270
272
278template <typename T, typename Filter>
279class FilterEnumerator final : public Enumerator<T> {
280 Enumerator<T> *input;
281 Filter filter;
282 T current; // must prevent repeated evaluation
283
284 public:
285 FilterEnumerator(Enumerator<T> *input, Filter filter)
286 : input(input), filter(std::move(filter)) {}
287
288 private:
289 bool advance() {
290 this->state = EnumeratorState::Valid;
291 while (this->input->moveNext()) {
292 this->current = this->input->getCurrent();
293 bool match = this->filter(this->current);
294 if (match) return true;
295 }
296 this->state = EnumeratorState::PastEnd;
297 return false;
298 }
299
300 public:
301 [[nodiscard]] std::string toString() const {
302 return "FilterEnumerator(" + this->input->toString() + "):" + this->stateName();
303 }
304
305 void reset() {
306 this->input->reset();
308 }
309
310 bool moveNext() {
311 switch (this->state) {
312 case EnumeratorState::NotStarted:
313 case EnumeratorState::Valid:
314 return this->advance();
315 case EnumeratorState::PastEnd:
316 return false;
317 }
318 throw std::runtime_error("Unexpected enumerator state");
319 }
320
321 T getCurrent() const {
322 switch (this->state) {
323 case EnumeratorState::NotStarted:
324 throw std::logic_error("You cannot call 'getCurrent' before 'moveNext'");
325 case EnumeratorState::PastEnd:
326 throw std::logic_error("You cannot call 'getCurrent' past the collection end");
327 case EnumeratorState::Valid:
328 return this->current;
329 }
330 throw std::runtime_error("Unexpected enumerator state");
331 }
332};
333
335
336namespace Detail {
337// See if we can use ICastable interface to cast from T to S. This is only possible if:
338// - Both T and S are pointer types (let's denote T = From* and S = To*)
339// - Expression (From*)()->to<To>() is well-formed
340// Essentially this means the following code is well-formed:
341// From *current = input->getCurrent(); current->to<To>();
342template <typename From, typename To, typename = void>
343static constexpr bool can_be_casted = false;
344
345template <typename From, typename To>
346static constexpr bool
347 can_be_casted<From *, To *, std::void_t<decltype(std::declval<From *>()->template to<To>())>> =
348 true;
349} // namespace Detail
350
352template <typename T, typename S>
353class AsEnumerator final : public Enumerator<S> {
354 template <typename U = S>
355 typename std::enable_if_t<!Detail::can_be_casted<T, S>, U> getCurrentImpl() const {
356 T current = input->getCurrent();
357 return dynamic_cast<S>(current);
358 }
359
360 template <typename U = S>
361 typename std::enable_if_t<Detail::can_be_casted<T, S>, U> getCurrentImpl() const {
362 T current = input->getCurrent();
363 return current->template to<std::remove_pointer_t<S>>();
364 }
365
366 protected:
367 Enumerator<T> *input;
368
369 public:
370 explicit AsEnumerator(Enumerator<T> *input) : input(input) {}
371
372 std::string toString() const {
373 return "AsEnumerator(" + this->input->toString() + "):" + this->stateName();
374 }
375
376 void reset() override {
378 this->input->reset();
379 }
380
381 bool moveNext() override {
382 bool result = this->input->moveNext();
383 if (result)
384 this->state = EnumeratorState::Valid;
385 else
386 this->state = EnumeratorState::PastEnd;
387 return result;
388 }
389
390 S getCurrent() const override { return getCurrentImpl(); }
391};
392
394
396template <typename T, typename S, typename Mapper>
397class MapEnumerator final : public Enumerator<S> {
398 protected:
399 Enumerator<T> *input;
400 Mapper map;
401 S current;
402
403 public:
404 MapEnumerator(Enumerator<T> *input, Mapper map) : input(input), map(std::move(map)) {}
405
406 void reset() {
407 this->input->reset();
409 }
410
411 [[nodiscard]] std::string toString() const {
412 return "MapEnumerator(" + this->input->toString() + "):" + this->stateName();
413 }
414
415 bool moveNext() {
416 switch (this->state) {
417 case EnumeratorState::NotStarted:
418 case EnumeratorState::Valid: {
419 bool success = input->moveNext();
420 if (success) {
421 T currentInput = this->input->getCurrent();
422 this->current = this->map(currentInput);
423 this->state = EnumeratorState::Valid;
424 return true;
425 } else {
426 this->state = EnumeratorState::PastEnd;
427 return false;
428 }
429 }
430 case EnumeratorState::PastEnd:
431 return false;
432 }
433 throw std::runtime_error("Unexpected enumerator state");
434 }
435
436 S getCurrent() const {
437 switch (this->state) {
438 case EnumeratorState::NotStarted:
439 throw std::logic_error("You cannot call 'getCurrent' before 'moveNext'");
440 case EnumeratorState::PastEnd:
441 throw std::logic_error("You cannot call 'getCurrent' past the collection end");
442 case EnumeratorState::Valid:
443 return this->current;
444 }
445 throw std::runtime_error("Unexpected enumerator state");
446 }
447};
448
449template <typename T, typename Mapper>
450MapEnumerator(Enumerator<T> *,
451 Mapper) -> MapEnumerator<T, typename std::invoke_result_t<Mapper, T>, Mapper>;
452
454
456template <typename T>
457class ConcatEnumerator final : public Enumerator<T> {
458 std::vector<Enumerator<T> *> inputs;
459 T currentResult;
460
461 public:
462 ConcatEnumerator() = default;
463 // We take ownership of the vector
464 explicit ConcatEnumerator(std::vector<Enumerator<T> *> &&inputs) : inputs(std::move(inputs)) {
465 for (auto *currentInput : inputs)
466 if (currentInput == nullptr) throw std::logic_error("Null iterator in concatenation");
467 }
468
469 ConcatEnumerator(std::initializer_list<Enumerator<T> *> inputs) : inputs(inputs) {
470 for (auto *currentInput : inputs)
471 if (currentInput == nullptr) throw std::logic_error("Null iterator in concatenation");
472 }
473 explicit ConcatEnumerator(Enumerator<Enumerator<T> *> *inputs)
474 : ConcatEnumerator(inputs->toVector()) {}
475
476 [[nodiscard]] std::string toString() const { return "ConcatEnumerator:" + this->stateName(); }
477
478 private:
479 bool advance() {
480 this->state = EnumeratorState::Valid;
481 for (auto *currentInput : inputs) {
482 if (currentInput->moveNext()) {
483 this->currentResult = currentInput->getCurrent();
484 return true;
485 }
486 }
487
488 this->state = EnumeratorState::PastEnd;
489 return false;
490 }
491
492 public:
494 // Too late to add
495 if (this->state == EnumeratorState::PastEnd)
496 throw std::runtime_error("Invalid enumerator state to concatenate");
497
498 inputs.push_back(other);
499
500 return this;
501 }
502
503 void reset() override {
504 for (auto *currentInput : inputs) currentInput->reset();
506 }
507
508 bool moveNext() override {
509 switch (this->state) {
510 case EnumeratorState::NotStarted:
511 case EnumeratorState::Valid:
512 return this->advance();
513 case EnumeratorState::PastEnd:
514 return false;
515 }
516 throw std::runtime_error("Unexpected enumerator state");
517 }
518
519 T getCurrent() const override {
520 switch (this->state) {
521 case EnumeratorState::NotStarted:
522 throw std::logic_error("You cannot call 'getCurrent' before 'moveNext'");
523 case EnumeratorState::PastEnd:
524 throw std::logic_error("You cannot call 'getCurrent' past the collection end");
525 case EnumeratorState::Valid:
526 return this->currentResult;
527 }
528 throw std::runtime_error("Unexpected enumerator state");
529 }
530};
531
533
534template <typename T>
535template <typename Mapper>
537 return new MapEnumerator(this, std::move(map));
538}
539
540template <typename T>
541template <typename S>
543 return new AsEnumerator<T, S>(this);
544}
545
546template <typename T>
547template <typename Filter>
549 return new FilterEnumerator(this, std::move(filter));
550}
551
552template <typename T>
553template <typename Container>
555 return new IteratorEnumerator(data.begin(), data.end(), typeid(Container).name());
556}
557
558template <typename T>
559Enumerator<T> *Enumerator<T>::emptyEnumerator() {
560 return new EmptyEnumerator<T>();
561}
562
563template <typename T>
564template <typename Iter>
565Enumerator<typename Iter::value_type> *Enumerator<T>::createEnumerator(Iter begin, Iter end) {
566 return new IteratorEnumerator(begin, end, "iterator");
567}
568
569template <typename T>
570template <typename Iter>
571Enumerator<typename Iter::value_type> *Enumerator<T>::createEnumerator(iterator_range<Iter> range) {
572 return new IteratorEnumerator(range.begin(), range.end(), "range");
573}
574
575template <typename T>
579
580template <typename T>
582 return new ConcatEnumerator<T>({this, other});
583}
584
586
587template <typename T>
589 if (enumerator == nullptr) throw std::logic_error("Dereferencing end() iterator");
590 return enumerator->getCurrent();
591}
592
593template <typename T>
594const EnumeratorHandle<T> &EnumeratorHandle<T>::operator++() {
595 enumerator->moveNext();
596 return *this;
597}
598
599template <typename T>
600bool EnumeratorHandle<T>::operator!=(const EnumeratorHandle<T> &other) const {
601 if (this->enumerator == other.enumerator) return true;
602 if (other.enumerator != nullptr) throw std::logic_error("Comparison with different iterator");
603 return this->enumerator->state == EnumeratorState::Valid;
604}
605
606template <typename Iter>
607Enumerator<typename Iter::value_type> *enumerate(Iter begin, Iter end) {
608 return new IteratorEnumerator(begin, end, "iterator");
609}
610
611template <typename Iter>
612Enumerator<typename Iter::value_type> *enumerate(iterator_range<Iter> range) {
613 return new IteratorEnumerator(range.begin(), range.end(), "range");
614}
615
616template <typename Container>
617Enumerator<typename Container::value_type> *enumerate(const Container &data) {
618 using std::begin;
619 using std::end;
620 return new IteratorEnumerator(begin(data), end(data), typeid(data).name());
621}
622
623// TODO: Flatten ConcatEnumerator's during concatenation
624template <typename T>
625Enumerator<T> *concat(std::initializer_list<Enumerator<T> *> inputs) {
626 return new ConcatEnumerator<T>(inputs);
627}
628
629template <typename... Args>
630auto concat(Args &&...inputs) {
631 using FirstEnumeratorTy =
632 std::remove_pointer_t<std::decay_t<std::tuple_element_t<0, std::tuple<Args...>>>>;
633 std::initializer_list<Enumerator<typename FirstEnumeratorTy::value_type> *> init{
634 std::forward<Args>(inputs)...};
635 return concat(init);
636}
637
638} // namespace Util
639#endif /* LIB_ENUMERATOR_H_ */
Casts each element.
Definition enumerator.h:353
S getCurrent() const override
Get current element in the collection.
Definition enumerator.h:390
bool moveNext() override
Definition enumerator.h:381
void reset() override
Move back to the beginning of the collection.
Definition enumerator.h:376
Concatenation.
Definition enumerator.h:457
bool moveNext() override
Definition enumerator.h:508
T getCurrent() const override
Get current element in the collection.
Definition enumerator.h:519
Enumerator< T > * concat(Enumerator< T > *other) override
Append all elements of other after all elements of this.
Definition enumerator.h:493
void reset() override
Move back to the beginning of the collection.
Definition enumerator.h:503
Always empty iterator (equivalent to end())
Definition enumerator.h:261
T getCurrent() const
Get current element in the collection.
Definition enumerator.h:266
bool moveNext()
Always returns false.
Definition enumerator.h:265
Definition enumerator.h:48
Type-erased Enumerator interface.
Definition enumerator.h:68
T single()
The only next element; throws if the enumerator does not have exactly 1 element.
Definition enumerator.h:156
virtual void reset()
Move back to the beginning of the collection.
Definition enumerator.h:92
Enumerator< std::invoke_result_t< Mapper, T > > * map(Mapper map)
Apply specified function to all elements of this enumerator.
Definition enumerator.h:536
virtual bool moveNext()=0
Enumerator< T > * where(Filter filter)
Return an enumerator returning all elements that pass the filter.
Definition enumerator.h:548
uint64_t count()
Enumerate all elements and return the count.
Definition enumerator.h:146
Enumerator< S > * as()
Cast to an enumerator of S objects.
Definition enumerator.h:542
static Enumerator< T > * concatAll(Enumerator< Enumerator< T > * > *inputs)
Concatenate all these collections into a single one.
Definition enumerator.h:576
T next()
Next element; throws if there are no elements.
Definition enumerator.h:184
T nextOrDefault()
Next element, or the default value if none exists.
Definition enumerator.h:177
T singleOrDefault()
Definition enumerator.h:167
virtual T getCurrent() const =0
Get current element in the collection.
bool any()
True if the enumerator has at least one element.
Definition enumerator.h:153
virtual Enumerator< T > * concat(Enumerator< T > *other)
Append all elements of other after all elements of this.
Definition enumerator.h:581
Definition enumerator.h:279
bool moveNext()
Definition enumerator.h:310
void reset()
Move back to the beginning of the collection.
Definition enumerator.h:305
T getCurrent() const
Get current element in the collection.
Definition enumerator.h:321
A generic iterator returning elements of type T.
Definition enumerator.h:199
Iter::value_type getCurrent() const
Get current element in the collection.
Definition enumerator.h:244
bool moveNext()
Definition enumerator.h:219
Transforms all elements from type T to type S.
Definition enumerator.h:397
bool moveNext()
Definition enumerator.h:415
void reset()
Move back to the beginning of the collection.
Definition enumerator.h:406
S getCurrent() const
Get current element in the collection.
Definition enumerator.h:436