Skip to content

Attempt to make filter return a forward iterator #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion filter.hpp
Original file line number Diff line number Diff line change
@@ -72,12 +72,16 @@ class iter::impl::Filtered {
}

public:
using iterator_category = std::input_iterator_tag;
using iterator_category = select_input_or_forward_iter<ContainerT>;
using value_type = iterator_traits_deref<ContainerT>;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;

template <bool Dummy = true, typename = enable_if_has_forward_iter_t<
DependentType<Dummy, ContainerT>>>
Iterator() : filter_func_(nullptr) {}

Iterator(IteratorWrapper<ContainerT>&& sub_iter,
IteratorWrapper<ContainerT>&& sub_end, FilterFunc& filter_func)
: sub_iter_{std::move(sub_iter)},
29 changes: 29 additions & 0 deletions internal/iterbase.hpp
Original file line number Diff line number Diff line change
@@ -75,6 +75,14 @@ namespace iter {
using get_iters::get_begin;
using get_iters::get_end;

template <bool, typename T>
struct DependentTypeImpl {
using type = T;
};

template <bool Dummy, typename T>
using DependentType = typename DependentTypeImpl<Dummy, T>::type;

template <typename T>
struct type_is {
using type = T;
@@ -196,6 +204,27 @@ namespace iter {

template <typename T>
using has_random_access_iter = is_random_access_iter<iterator_type<T>>;

template <typename Iter>
using is_forward_iter = std::integral_constant<bool,
std::is_convertible<
typename std::iterator_traits<Iter>::iterator_category,
std::forward_iterator_tag>::value
&& std::is_default_constructible<Iter>::value>;

template <typename Container>
using has_forward_iter = is_forward_iter<iterator_type<Container>>;

template <typename ContainerT>
using select_input_or_forward_iter =
typename std::conditional<has_forward_iter<ContainerT>::value,
std::forward_iterator_tag, std::input_iterator_tag>::type;

template <typename ContainerT, typename ResultT = void>
using enable_if_has_forward_iter_t =
typename std::enable_if<has_forward_iter<ContainerT>::value,
ResultT>::type;

// because std::advance assumes a lot and is actually smart, I need a dumb

// version that will work with most things
134 changes: 132 additions & 2 deletions test/helpers.hpp
Original file line number Diff line number Diff line change
@@ -12,6 +12,123 @@

namespace itertest {

namespace archetypes {
template <typename Iter>
class InputIterator {
using Traits = std::iterator_traits<Iter>;
Iter it_;

public:
using iterator_category = std::input_iterator_tag;
using value_type = typename Traits::value_type;
using difference_type = typename Traits::difference_type;
using pointer = Iter;
using reference = typename Traits::reference;

Iter base() const {
return it_;
}

explicit InputIterator(Iter it) : it_(it) {}

reference operator*() const {
return *it_;
}
pointer operator->() const {
return it_;
}

InputIterator& operator++() {
++it_;
return *this;
}
InputIterator operator++(int) {
InputIterator tmp(*this);
++(*this);
return tmp;
}

friend bool operator==(
const InputIterator& LHS, const InputIterator& RHS) {
return LHS.it_ == RHS.it_;
}
friend bool operator!=(
const InputIterator& LHS, const InputIterator& RHS) {
return LHS.it_ != RHS.it_;
}
};

template <typename Iter>
class ForwardIterator {
using Traits = std::iterator_traits<Iter>;
Iter it_;

public:
using iterator_category = std::forward_iterator_tag;
using value_type = typename Traits::value_type;
using difference_type = typename Traits::difference_type;
using pointer = Iter;
using reference = typename Traits::reference;

Iter base() const {
return it_;
}

ForwardIterator() : it_() {}
explicit ForwardIterator(Iter it) : it_(it) {}

reference operator*() const {
return *it_;
}
pointer operator->() const {
return it_;
}

ForwardIterator& operator++() {
++it_;
return *this;
}
ForwardIterator operator++(int) {
ForwardIterator tmp(*this);
++(*this);
return tmp;
}

friend bool operator==(
const ForwardIterator& LHS, const ForwardIterator& RHS) {
return LHS.it_ == RHS.it_;
}
friend bool operator!=(
const ForwardIterator& LHS, const ForwardIterator& RHS) {
return LHS.it_ != RHS.it_;
}
};

template <typename ContainerT, template <typename Iter> typename IterWrap>
class Container {
ContainerT container_;

public:
using iterator = IterWrap<typename ContainerT::iterator>;
using const_iterator = IterWrap<typename ContainerT::const_iterator>;

Container(ContainerT const& c) : container_(c) {}

iterator begin() {
return iterator(container_.begin());
}
iterator end() {
return iterator(container_.end());
}
const_iterator begin() const {
return const_iterator(container_.begin());
}
const_iterator end() const {
return const_iterator(container_.end());
}
};
} // namespace archetypes

// non-copyable. non-movable. non-default-constructible
class SolidInt {
private:
@@ -217,8 +334,9 @@ namespace itertest {
decltype(++std::declval<T&>()), // prefix ++
decltype(std::declval<T&>()++), // postfix ++
decltype(
std::declval<const T&>() != std::declval<const T&>()), // !=
decltype(std::declval<const T&>() == std::declval<const T&>()) // ==
std::declval<const T&>() != std::declval<const T&>()), // !=
decltype(
std::declval<const T&>() == std::declval<const T&>()) // ==,
>> : std::true_type {};

template <typename T>
@@ -297,6 +415,12 @@ class DiffEndRange {
SubIter end_;

public:
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;

#ifdef CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
Iterator() = default;
#endif
@@ -335,6 +459,12 @@ class DiffEndRange {
SubIter end_;

public:
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;

#ifdef CHAR_RANGE_DEFAULT_CONSTRUCTIBLE
ReverseIterator() = default;
#endif
22 changes: 22 additions & 0 deletions test/test_filter.cpp
Original file line number Diff line number Diff line change
@@ -192,6 +192,28 @@ TEST_CASE("filter: iterator meets requirements", "[filter]") {
REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
}

TEST_CASE("filter: iterator is input if base is input", "[filter]") {
using namespace itertest::archetypes;
using C = Container<std::string, InputIterator>;
C c(std::string{"abcdef"});
auto f = filter([](char c) { return c != 'a' && c != 'e'; }, c);
using IterT = decltype(std::begin(f));
REQUIRE(std::is_same<typename std::iterator_traits<IterT>::iterator_category,
std::input_iterator_tag>::value);
REQUIRE(!itertest::IsForwardIterator<IterT>::value);
}

TEST_CASE("filter: iterator is forward if base is forward", "[filter]") {
using namespace itertest::archetypes;
using C = Container<std::string, ForwardIterator>;
C c(std::string{"abcdef"});
auto f = filter([](char c) { return c != 'a' && c != 'e'; }, c);
using IterT = decltype(std::begin(f));
REQUIRE(std::is_same<typename std::iterator_traits<IterT>::iterator_category,
std::forward_iterator_tag>::value);
REQUIRE(itertest::IsForwardIterator<IterT>::value);
}

template <typename T, typename U>
using ImpT = decltype(filter(std::declval<T>(), std::declval<U>()));
TEST_CASE("filter: has correct ctor and assign ops", "[filter]") {
22 changes: 22 additions & 0 deletions test/test_filterfalse.cpp
Original file line number Diff line number Diff line change
@@ -156,6 +156,28 @@ TEST_CASE("filterfalse: iterator meets requirements", "[filterfalse]") {
REQUIRE(itertest::IsIterator<decltype(std::begin(c))>::value);
}

TEST_CASE("filter: iterator is input if base is input", "[filter]") {
using namespace itertest::archetypes;
using C = Container<std::string, InputIterator>;
C c(std::string{"abcdef"});
auto f = filterfalse([](char c) { return c != 'a' && c != 'e'; }, c);
using IterT = decltype(std::begin(f));
REQUIRE(std::is_same<typename std::iterator_traits<IterT>::iterator_category,
std::input_iterator_tag>::value);
REQUIRE(!itertest::IsForwardIterator<IterT>::value);
}

TEST_CASE("filter: iterator is forward if base is forward", "[filter]") {
using namespace itertest::archetypes;
using C = Container<std::string, ForwardIterator>;
C c(std::string{"abcdef"});
auto f = filterfalse([](char c) { return c != 'a' && c != 'e'; }, c);
using IterT = decltype(std::begin(f));
REQUIRE(std::is_same<typename std::iterator_traits<IterT>::iterator_category,
std::forward_iterator_tag>::value);
REQUIRE(itertest::IsForwardIterator<IterT>::value);
}

template <typename T, typename U>
using ImpT = decltype(filterfalse(std::declval<T>(), std::declval<U>()));
TEST_CASE("filterfalse: has correct ctor and assign ops", "[filterfalse]") {