diff --git a/filter.hpp b/filter.hpp index 10a793c6..c8e35a55 100644 --- a/filter.hpp +++ b/filter.hpp @@ -72,12 +72,16 @@ class iter::impl::Filtered { } public: - using iterator_category = std::input_iterator_tag; + using iterator_category = select_input_or_forward_iter; using value_type = iterator_traits_deref; using difference_type = std::ptrdiff_t; using pointer = value_type*; using reference = value_type&; + template >> + Iterator() : filter_func_(nullptr) {} + Iterator(IteratorWrapper&& sub_iter, IteratorWrapper&& sub_end, FilterFunc& filter_func) : sub_iter_{std::move(sub_iter)}, diff --git a/internal/iterbase.hpp b/internal/iterbase.hpp index 7b460bf5..1c1bd5a7 100644 --- a/internal/iterbase.hpp +++ b/internal/iterbase.hpp @@ -75,6 +75,14 @@ namespace iter { using get_iters::get_begin; using get_iters::get_end; + template + struct DependentTypeImpl { + using type = T; + }; + + template + using DependentType = typename DependentTypeImpl::type; + template struct type_is { using type = T; @@ -196,6 +204,27 @@ namespace iter { template using has_random_access_iter = is_random_access_iter>; + + template + using is_forward_iter = std::integral_constant::iterator_category, + std::forward_iterator_tag>::value + && std::is_default_constructible::value>; + + template + using has_forward_iter = is_forward_iter>; + + template + using select_input_or_forward_iter = + typename std::conditional::value, + std::forward_iterator_tag, std::input_iterator_tag>::type; + + template + using enable_if_has_forward_iter_t = + typename std::enable_if::value, + ResultT>::type; + // because std::advance assumes a lot and is actually smart, I need a dumb // version that will work with most things diff --git a/test/helpers.hpp b/test/helpers.hpp index aa80cd3c..9f2f9139 100644 --- a/test/helpers.hpp +++ b/test/helpers.hpp @@ -12,6 +12,123 @@ namespace itertest { + namespace archetypes { + template + class InputIterator { + using Traits = std::iterator_traits; + 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 + class ForwardIterator { + using Traits = std::iterator_traits; + 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 IterWrap> + class Container { + ContainerT container_; + + public: + using iterator = IterWrap; + using const_iterator = IterWrap; + + 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()), // prefix ++ decltype(std::declval()++), // postfix ++ decltype( - std::declval() != std::declval()), // != - decltype(std::declval() == std::declval()) // == + std::declval() != std::declval()), // != + decltype( + std::declval() == std::declval()) // ==, >> : std::true_type {}; template @@ -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 diff --git a/test/test_filter.cpp b/test/test_filter.cpp index 73f684ee..26463de4 100644 --- a/test/test_filter.cpp +++ b/test/test_filter.cpp @@ -192,6 +192,28 @@ TEST_CASE("filter: iterator meets requirements", "[filter]") { REQUIRE(itertest::IsIterator::value); } +TEST_CASE("filter: iterator is input if base is input", "[filter]") { + using namespace itertest::archetypes; + using C = Container; + 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::iterator_category, + std::input_iterator_tag>::value); + REQUIRE(!itertest::IsForwardIterator::value); +} + +TEST_CASE("filter: iterator is forward if base is forward", "[filter]") { + using namespace itertest::archetypes; + using C = Container; + 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::iterator_category, + std::forward_iterator_tag>::value); + REQUIRE(itertest::IsForwardIterator::value); +} + template using ImpT = decltype(filter(std::declval(), std::declval())); TEST_CASE("filter: has correct ctor and assign ops", "[filter]") { diff --git a/test/test_filterfalse.cpp b/test/test_filterfalse.cpp index 7a74a0df..b6dc815c 100644 --- a/test/test_filterfalse.cpp +++ b/test/test_filterfalse.cpp @@ -156,6 +156,28 @@ TEST_CASE("filterfalse: iterator meets requirements", "[filterfalse]") { REQUIRE(itertest::IsIterator::value); } +TEST_CASE("filter: iterator is input if base is input", "[filter]") { + using namespace itertest::archetypes; + using C = Container; + 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::iterator_category, + std::input_iterator_tag>::value); + REQUIRE(!itertest::IsForwardIterator::value); +} + +TEST_CASE("filter: iterator is forward if base is forward", "[filter]") { + using namespace itertest::archetypes; + using C = Container; + 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::iterator_category, + std::forward_iterator_tag>::value); + REQUIRE(itertest::IsForwardIterator::value); +} + template using ImpT = decltype(filterfalse(std::declval(), std::declval())); TEST_CASE("filterfalse: has correct ctor and assign ops", "[filterfalse]") {