1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 18:54:02 +01:00

[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

llvm-svn: 338955
This commit is contained in:
Chandler Carruth 2018-08-04 08:17:26 +00:00
parent 28625d59d9
commit 069da7dcfd
2 changed files with 135 additions and 0 deletions

View File

@ -21,6 +21,7 @@
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h" #include "llvm/ADT/iterator_range.h"
#include "llvm/Config/abi-breaking.h"
#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorHandling.h"
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
@ -418,6 +419,89 @@ make_filter_range(RangeT &&Range, PredicateT Pred) {
std::end(std::forward<RangeT>(Range)), Pred)); std::end(std::forward<RangeT>(Range)), Pred));
} }
/// A pseudo-iterator adaptor that is designed to implement "early increment"
/// style loops.
///
/// This is *not a normal iterator* and should almost never be used directly. It
/// is intended primarily to be used with range based for loops and some range
/// algorithms.
///
/// The iterator isn't quite an `OutputIterator` or an `InputIterator` but
/// somewhere between them. The constraints of these iterators are:
///
/// - On construction or after being incremented, it is comparable and
/// dereferencable. It is *not* incrementable.
/// - After being dereferenced, it is neither comparable nor dereferencable, it
/// is only incrementable.
///
/// This means you can only dereference the iterator once, and you can only
/// increment it once between dereferences.
template <typename WrappedIteratorT>
class early_inc_iterator_impl
: public iterator_adaptor_base<early_inc_iterator_impl<WrappedIteratorT>,
WrappedIteratorT, std::input_iterator_tag> {
using BaseT =
iterator_adaptor_base<early_inc_iterator_impl<WrappedIteratorT>,
WrappedIteratorT, std::input_iterator_tag>;
using PointerT = typename std::iterator_traits<WrappedIteratorT>::pointer;
protected:
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
bool IsEarlyIncremented = false;
#endif
public:
early_inc_iterator_impl(WrappedIteratorT I) : BaseT(I) {}
using BaseT::operator*;
typename BaseT::reference operator*() {
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
assert(!IsEarlyIncremented && "Cannot dereference twice!");
IsEarlyIncremented = true;
#endif
return *(this->I)++;
}
using BaseT::operator++;
early_inc_iterator_impl &operator++() {
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
assert(IsEarlyIncremented && "Cannot increment before dereferencing!");
IsEarlyIncremented = false;
#endif
return *this;
}
using BaseT::operator==;
bool operator==(const early_inc_iterator_impl &RHS) const {
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
assert(!IsEarlyIncremented && "Cannot compare after dereferencing!");
#endif
return BaseT::operator==(RHS);
}
};
/// Make a range that does early increment to allow mutation of the underlying
/// range without disrupting iteration.
///
/// The underlying iterator will be incremented immediately after it is
/// dereferenced, allowing deletion of the current node or insertion of nodes to
/// not disrupt iteration provided they do not invalidate the *next* iterator --
/// the current iterator can be invalidated.
///
/// This requires a very exact pattern of use that is only really suitable to
/// range based for loops and other range algorithms that explicitly guarantee
/// to dereference exactly once each element, and to increment exactly once each
/// element.
template <typename RangeT>
iterator_range<early_inc_iterator_impl<detail::IterOfRange<RangeT>>>
make_early_inc_range(RangeT &&Range) {
using EarlyIncIteratorT =
early_inc_iterator_impl<detail::IterOfRange<RangeT>>;
return make_range(EarlyIncIteratorT(std::begin(std::forward<RangeT>(Range))),
EarlyIncIteratorT(std::end(std::forward<RangeT>(Range))));
}
// forward declarations required by zip_shortest/zip_first // forward declarations required by zip_shortest/zip_first
template <typename R, typename UnaryPredicate> template <typename R, typename UnaryPredicate>
bool all_of(R &&range, UnaryPredicate P); bool all_of(R &&range, UnaryPredicate P);

View File

@ -364,4 +364,55 @@ TEST(STLExtrasTest, ADLTest) {
EXPECT_EQ(5, count); EXPECT_EQ(5, count);
} }
TEST(STLExtrasTest, EarlyIncrementTest) {
std::list<int> L = {1, 2, 3, 4};
auto EIR = make_early_inc_range(L);
auto I = EIR.begin();
auto EI = EIR.end();
EXPECT_NE(I, EI);
EXPECT_EQ(1, *I);
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
#ifndef NDEBUG
// Repeated dereferences are not allowed.
EXPECT_DEATH(*I, "Cannot dereference");
// Comparison after dereference is not allowed.
EXPECT_DEATH((void)(I == EI), "Cannot compare");
EXPECT_DEATH((void)(I != EI), "Cannot compare");
#endif
#endif
++I;
EXPECT_NE(I, EI);
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
#ifndef NDEBUG
// You cannot increment prior to dereference.
EXPECT_DEATH(++I, "Cannot increment");
#endif
#endif
EXPECT_EQ(2, *I);
#if LLVM_ENABLE_ABI_BREAKING_CHECKS
#ifndef NDEBUG
// Repeated dereferences are not allowed.
EXPECT_DEATH(*I, "Cannot dereference");
#endif
#endif
// Inserting shouldn't break anything. We should be able to keep dereferencing
// the currrent iterator and increment. The increment to go to the "next"
// iterator from before we inserted.
L.insert(std::next(L.begin(), 2), -1);
++I;
EXPECT_EQ(3, *I);
// Erasing the front including the current doesn't break incrementing.
L.erase(L.begin(), std::prev(L.end()));
++I;
EXPECT_EQ(4, *I);
++I;
EXPECT_EQ(EIR.end(), I);
}
} // namespace } // namespace