[llvm] r278569 - [ADT] Add filter_iterator for filtering elements
Tim Shen via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 12 15:03:28 PDT 2016
Author: timshen
Date: Fri Aug 12 17:03:28 2016
New Revision: 278569
URL: http://llvm.org/viewvc/llvm-project?rev=278569&view=rev
Log:
[ADT] Add filter_iterator for filtering elements
Differential Revision: https://reviews.llvm.org/D22951
Modified:
llvm/trunk/include/llvm/ADT/STLExtras.h
llvm/trunk/include/llvm/ADT/iterator.h
llvm/trunk/unittests/Support/IteratorTest.cpp
Modified: llvm/trunk/include/llvm/ADT/STLExtras.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/STLExtras.h?rev=278569&r1=278568&r2=278569&view=diff
==============================================================================
--- llvm/trunk/include/llvm/ADT/STLExtras.h (original)
+++ llvm/trunk/include/llvm/ADT/STLExtras.h Fri Aug 12 17:03:28 2016
@@ -26,10 +26,18 @@
#include <memory>
#include <utility> // for std::pair
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Compiler.h"
namespace llvm {
+namespace detail {
+
+template <typename RangeT>
+using IterOfRange = decltype(std::begin(std::declval<RangeT>()));
+
+} // End detail namespace
//===----------------------------------------------------------------------===//
// Extra additions to <functional>
@@ -235,6 +243,90 @@ auto reverse(
llvm::make_reverse_iterator(std::begin(C)));
}
+/// An iterator adaptor that filters the elements of given inner iterators.
+///
+/// The predicate parameter should be a callable object that accepts the wrapped
+/// iterator's reference type and returns a bool. When incrementing or
+/// decrementing the iterator, it will call the predicate on each element and
+/// skip any where it returns false.
+///
+/// \code
+/// int A[] = { 1, 2, 3, 4 };
+/// auto R = make_filter_range(A, [](int N) { return N % 2 == 1; });
+/// // R contains { 1, 3 }.
+/// \endcode
+template <typename WrappedIteratorT, typename PredicateT>
+class filter_iterator
+ : public iterator_adaptor_base<
+ filter_iterator<WrappedIteratorT, PredicateT>, WrappedIteratorT,
+ typename std::common_type<
+ std::forward_iterator_tag,
+ typename std::iterator_traits<
+ WrappedIteratorT>::iterator_category>::type> {
+ using BaseT = iterator_adaptor_base<
+ filter_iterator<WrappedIteratorT, PredicateT>, WrappedIteratorT,
+ typename std::common_type<
+ std::forward_iterator_tag,
+ typename std::iterator_traits<WrappedIteratorT>::iterator_category>::
+ type>;
+
+ struct PayloadType {
+ WrappedIteratorT End;
+ PredicateT Pred;
+ };
+
+ Optional<PayloadType> Payload;
+
+ void findNextValid() {
+ assert(Payload && "Payload should be engaged when findNextValid is called");
+ while (this->I != Payload->End && !Payload->Pred(*this->I))
+ BaseT::operator++();
+ }
+
+ // Construct the begin iterator. The begin iterator requires to know where end
+ // is, so that it can properly stop when it hits end.
+ filter_iterator(WrappedIteratorT Begin, WrappedIteratorT End, PredicateT Pred)
+ : BaseT(std::move(Begin)),
+ Payload(PayloadType{std::move(End), std::move(Pred)}) {
+ findNextValid();
+ }
+
+ // Construct the end iterator. It's not incrementable, so Payload doesn't
+ // have to be engaged.
+ filter_iterator(WrappedIteratorT End) : BaseT(End) {}
+
+public:
+ using BaseT::operator++;
+
+ filter_iterator &operator++() {
+ BaseT::operator++();
+ findNextValid();
+ return *this;
+ }
+
+ template <typename RT, typename PT>
+ friend iterator_range<filter_iterator<detail::IterOfRange<RT>, PT>>
+ make_filter_range(RT &&, PT);
+};
+
+/// Convenience function that takes a range of elements and a predicate,
+/// and return a new filter_iterator range.
+///
+/// FIXME: Currently if RangeT && is a rvalue reference to a temporary, the
+/// lifetime of that temporary is not kept by the returned range object, and the
+/// temporary is going to be dropped on the floor after the make_iterator_range
+/// full expression that contains this function call.
+template <typename RangeT, typename PredicateT>
+iterator_range<filter_iterator<detail::IterOfRange<RangeT>, PredicateT>>
+make_filter_range(RangeT &&Range, PredicateT Pred) {
+ using FilterIteratorT =
+ filter_iterator<detail::IterOfRange<RangeT>, PredicateT>;
+ return make_range(FilterIteratorT(std::begin(std::forward<RangeT>(Range)),
+ std::end(std::forward<RangeT>(Range)),
+ std::move(Pred)),
+ FilterIteratorT(std::end(std::forward<RangeT>(Range))));
+}
+
//===----------------------------------------------------------------------===//
// Extra additions to <utility>
//===----------------------------------------------------------------------===//
Modified: llvm/trunk/include/llvm/ADT/iterator.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ADT/iterator.h?rev=278569&r1=278568&r2=278569&view=diff
==============================================================================
--- llvm/trunk/include/llvm/ADT/iterator.h (original)
+++ llvm/trunk/include/llvm/ADT/iterator.h Fri Aug 12 17:03:28 2016
@@ -175,15 +175,7 @@ protected:
iterator_adaptor_base() = default;
- template <typename U>
- explicit iterator_adaptor_base(
- U &&u,
- typename std::enable_if<
- !std::is_base_of<typename std::remove_cv<
- typename std::remove_reference<U>::type>::type,
- DerivedT>::value,
- int>::type = 0)
- : I(std::forward<U &&>(u)) {}
+ explicit iterator_adaptor_base(WrappedIteratorT u) : I(std::move(u)) {}
const WrappedIteratorT &wrapped() const { return I; }
Modified: llvm/trunk/unittests/Support/IteratorTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/IteratorTest.cpp?rev=278569&r1=278568&r2=278569&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/IteratorTest.cpp (original)
+++ llvm/trunk/unittests/Support/IteratorTest.cpp Fri Aug 12 17:03:28 2016
@@ -116,4 +116,73 @@ TEST(PointeeIteratorTest, SmartPointer)
EXPECT_EQ(End, I);
}
+TEST(FilterIteratorTest, Lambda) {
+ auto IsOdd = [](int N) { return N % 2 == 1; };
+ int A[] = {0, 1, 2, 3, 4, 5, 6};
+ auto Range = make_filter_range(A, IsOdd);
+ SmallVector<int, 3> Actual(Range.begin(), Range.end());
+ EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);
+}
+
+TEST(FilterIteratorTest, CallableObject) {
+ int Counter = 0;
+ struct Callable {
+ int &Counter;
+
+ Callable(int &Counter) : Counter(Counter) {}
+
+ bool operator()(int N) {
+ Counter++;
+ return N % 2 == 1;
+ }
+ };
+ Callable IsOdd(Counter);
+ int A[] = {0, 1, 2, 3, 4, 5, 6};
+ auto Range = make_filter_range(A, IsOdd);
+ EXPECT_EQ(2, Counter);
+ SmallVector<int, 3> Actual(Range.begin(), Range.end());
+ EXPECT_GE(Counter, 7);
+ EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);
+}
+
+TEST(FilterIteratorTest, FunctionPointer) {
+ bool (*IsOdd)(int) = [](int N) { return N % 2 == 1; };
+ int A[] = {0, 1, 2, 3, 4, 5, 6};
+ auto Range = make_filter_range(A, IsOdd);
+ SmallVector<int, 3> Actual(Range.begin(), Range.end());
+ EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);
+}
+
+TEST(FilterIteratorTest, Composition) {
+ auto IsOdd = [](int N) { return N % 2 == 1; };
+ std::unique_ptr<int> A[] = {make_unique<int>(0), make_unique<int>(1),
+ make_unique<int>(2), make_unique<int>(3),
+ make_unique<int>(4), make_unique<int>(5),
+ make_unique<int>(6)};
+ using PointeeIterator = pointee_iterator<std::unique_ptr<int> *>;
+ auto Range = make_filter_range(
+ make_range(PointeeIterator(std::begin(A)), PointeeIterator(std::end(A))),
+ IsOdd);
+ SmallVector<int, 3> Actual(Range.begin(), Range.end());
+ EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);
+}
+
+TEST(FilterIteratorTest, InputIterator) {
+ struct InputIterator
+ : iterator_adaptor_base<InputIterator, int *, std::input_iterator_tag> {
+ using BaseT =
+ iterator_adaptor_base<InputIterator, int *, std::input_iterator_tag>;
+
+ InputIterator(int *It) : BaseT(It) {}
+ };
+
+ auto IsOdd = [](int N) { return N % 2 == 1; };
+ int A[] = {0, 1, 2, 3, 4, 5, 6};
+ auto Range = make_filter_range(
+ make_range(InputIterator(std::begin(A)), InputIterator(std::end(A))),
+ IsOdd);
+ SmallVector<int, 3> Actual(Range.begin(), Range.end());
+ EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);
+}
+
} // anonymous namespace
More information about the llvm-commits
mailing list