Skip to content
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

Attempt to make filter return a forward iterator #54

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
Expand Up @@ -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)},
Expand Down
29 changes: 29 additions & 0 deletions internal/iterbase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
134 changes: 132 additions & 2 deletions test/helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions test/test_filter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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]") {
Expand Down
22 changes: 22 additions & 0 deletions test/test_filterfalse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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]") {
Expand Down