llvm.org GIT mirror llvm / b2e6802
[ADT] Add an early-increment iterator-like type and range adaptor. This allows us to model the common LLVM idiom of incrementing immediately after dereferencing so that we can remove or update the entity w/o losing our ability to reach the "next". However, these are not real or proper iterators. They are just enough to allow range based for loops and very simple range algorithms to work, but should not be considered full general. Differential Revision: https://reviews.llvm.org/D49956 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@338955 91177308-0d34-0410-b5e6-96231b3b80d8 Chandler Carruth 2 years ago
2 changed file(s) with 135 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
2020 #include "llvm/ADT/SmallVector.h"
2121 #include "llvm/ADT/iterator.h"
2222 #include "llvm/ADT/iterator_range.h"
23 #include "llvm/Config/abi-breaking.h"
2324 #include "llvm/Support/ErrorHandling.h"
2425 #include
2526 #include
417418 std::end(std::forward(Range)), Pred));
418419 }
419420
421 /// A pseudo-iterator adaptor that is designed to implement "early increment"
422 /// style loops.
423 ///
424 /// This is *not a normal iterator* and should almost never be used directly. It
425 /// is intended primarily to be used with range based for loops and some range
426 /// algorithms.
427 ///
428 /// The iterator isn't quite an `OutputIterator` or an `InputIterator` but
429 /// somewhere between them. The constraints of these iterators are:
430 ///
431 /// - On construction or after being incremented, it is comparable and
432 /// dereferencable. It is *not* incrementable.
433 /// - After being dereferenced, it is neither comparable nor dereferencable, it
434 /// is only incrementable.
435 ///
436 /// This means you can only dereference the iterator once, and you can only
437 /// increment it once between dereferences.
438 template
439 class early_inc_iterator_impl
440 : public iterator_adaptor_base,
441 WrappedIteratorT, std::input_iterator_tag> {
442 using BaseT =
443 iterator_adaptor_base,
444 WrappedIteratorT, std::input_iterator_tag>;
445
446 using PointerT = typename std::iterator_traits::pointer;
447
448 protected:
449 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
450 bool IsEarlyIncremented = false;
451 #endif
452
453 public:
454 early_inc_iterator_impl(WrappedIteratorT I) : BaseT(I) {}
455
456 using BaseT::operator*;
457 typename BaseT::reference operator*() {
458 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
459 assert(!IsEarlyIncremented && "Cannot dereference twice!");
460 IsEarlyIncremented = true;
461 #endif
462 return *(this->I)++;
463 }
464
465 using BaseT::operator++;
466 early_inc_iterator_impl &operator++() {
467 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
468 assert(IsEarlyIncremented && "Cannot increment before dereferencing!");
469 IsEarlyIncremented = false;
470 #endif
471 return *this;
472 }
473
474 using BaseT::operator==;
475 bool operator==(const early_inc_iterator_impl &RHS) const {
476 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
477 assert(!IsEarlyIncremented && "Cannot compare after dereferencing!");
478 #endif
479 return BaseT::operator==(RHS);
480 }
481 };
482
483 /// Make a range that does early increment to allow mutation of the underlying
484 /// range without disrupting iteration.
485 ///
486 /// The underlying iterator will be incremented immediately after it is
487 /// dereferenced, allowing deletion of the current node or insertion of nodes to
488 /// not disrupt iteration provided they do not invalidate the *next* iterator --
489 /// the current iterator can be invalidated.
490 ///
491 /// This requires a very exact pattern of use that is only really suitable to
492 /// range based for loops and other range algorithms that explicitly guarantee
493 /// to dereference exactly once each element, and to increment exactly once each
494 /// element.
495 template
496 iterator_range>>
497 make_early_inc_range(RangeT &&Range) {
498 using EarlyIncIteratorT =
499 early_inc_iterator_impl>;
500 return make_range(EarlyIncIteratorT(std::begin(std::forward(Range))),
501 EarlyIncIteratorT(std::end(std::forward(Range))));
502 }
503
420504 // forward declarations required by zip_shortest/zip_first
421505 template
422506 bool all_of(R &&range, UnaryPredicate P);
363363 EXPECT_EQ(5, count);
364364 }
365365
366 TEST(STLExtrasTest, EarlyIncrementTest) {
367 std::list L = {1, 2, 3, 4};
368
369 auto EIR = make_early_inc_range(L);
370
371 auto I = EIR.begin();
372 auto EI = EIR.end();
373 EXPECT_NE(I, EI);
374
375 EXPECT_EQ(1, *I);
376 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
377 #ifndef NDEBUG
378 // Repeated dereferences are not allowed.
379 EXPECT_DEATH(*I, "Cannot dereference");
380 // Comparison after dereference is not allowed.
381 EXPECT_DEATH((void)(I == EI), "Cannot compare");
382 EXPECT_DEATH((void)(I != EI), "Cannot compare");
383 #endif
384 #endif
385
386 ++I;
387 EXPECT_NE(I, EI);
388 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
389 #ifndef NDEBUG
390 // You cannot increment prior to dereference.
391 EXPECT_DEATH(++I, "Cannot increment");
392 #endif
393 #endif
394 EXPECT_EQ(2, *I);
395 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
396 #ifndef NDEBUG
397 // Repeated dereferences are not allowed.
398 EXPECT_DEATH(*I, "Cannot dereference");
399 #endif
400 #endif
401
402 // Inserting shouldn't break anything. We should be able to keep dereferencing
403 // the currrent iterator and increment. The increment to go to the "next"
404 // iterator from before we inserted.
405 L.insert(std::next(L.begin(), 2), -1);
406 ++I;
407 EXPECT_EQ(3, *I);
408
409 // Erasing the front including the current doesn't break incrementing.
410 L.erase(L.begin(), std::prev(L.end()));
411 ++I;
412 EXPECT_EQ(4, *I);
413 ++I;
414 EXPECT_EQ(EIR.end(), I);
415 }
416
366417 } // namespace