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

Adding view::remove_when. #1219

Open
wants to merge 5 commits 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
2 changes: 2 additions & 0 deletions doc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,8 @@ provides, and a blurb about how each is intended to be used.
<DD>Given a source range and a value, filter out those elements that do not equal value.</DD>
<DT>\link ranges::view::remove_if_fn `view::remove_if`\endlink</DT>
<DD>Given a source range and a unary predicate, filter out those elements that do not satisfy the predicate. (For users of Boost.Range, this is like the `filter` adaptor with the predicate negated.)</DD>
<DT>\link ranges::view::remove_when_fn `view::remove_when`\endlink</DT>
<DD>Given a source range and a delimiter specifier, filter out those elements specified by the delimiter specifier. The delimiter specifier can be a predicate or a function. The predicate should take a single argument of the range's reference type and return `true` if and only if the element is part of a delimiter. The function should accept an iterator and sentinel indicating the current position and end of the source range and return `std::make_pair(true, iterator_past_the_delimiter)` if the current position is a boundary; otherwise `std::make_pair(false, ignored_iterator_value)`.</DD>
<DT>\link ranges::view::repeat_fn `view::repeat`\endlink</DT>
<DD>Given a value, create a range that is that value repeated infinitely.</DD>
<DT>\link ranges::view::repeat_n_fn `view::repeat_n`\endlink</DT>
Expand Down
42 changes: 42 additions & 0 deletions include/range/v3/detail/delimiter_specifier.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/// \file
// Range v3 library
//
// Copyright Barry Revzin 2019-present
//
// Use, modification and distribution is subject to the
// Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// Project home: https://github.com/ericniebler/range-v3
//

#ifndef RANGES_V3_DETAIL_DELIMITER_SPECIFIER_HPP
#define RANGES_V3_DETAIL_DELIMITER_SPECIFIER_HPP

#include <range/v3/algorithm/find_if_not.hpp>
#include <range/v3/range/concepts.hpp>
#include <range/v3/utility/semiregular.hpp>

namespace ranges
{
namespace detail
{
template<typename Pred>
struct delimiter_specifier
{
semiregular_t<Pred> pred_;

template<typename I, typename S>
auto operator()(I cur, S end) const -> CPP_ret(std::pair<bool, I>)( //
requires Sentinel<S, I>)
{
auto where = ranges::find_if_not(cur, end, std::ref(pred_));
return {cur != where, where};
}
};
} // namespace detail
} // namespace ranges

#endif

181 changes: 181 additions & 0 deletions include/range/v3/view/remove_when.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/// \file
// Range v3 library
//
// Copyright Barry Revzin 2019-present
//
// Use, modification and distribution is subject to the
// Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// Project home: https://github.com/ericniebler/range-v3
//

#ifndef RANGES_V3_VIEW_REMOVE_WHEN_HPP
#define RANGES_V3_VIEW_REMOVE_WHEN_HPP

#include <type_traits>
#include <utility>

#include <meta/meta.hpp>

#include <range/v3/range_fwd.hpp>

#include <range/v3/functional/compose.hpp>
#include <range/v3/functional/invoke.hpp>
#include <range/v3/range/access.hpp>
#include <range/v3/range/concepts.hpp>
#include <range/v3/range/traits.hpp>
#include <range/v3/utility/box.hpp>
#include <range/v3/utility/optional.hpp>
#include <range/v3/utility/semiregular.hpp>
#include <range/v3/utility/static_const.hpp>
#include <range/v3/view/adaptor.hpp>
#include <range/v3/view/view.hpp>
#include <range/v3/detail/delimiter_specifier.hpp>

RANGES_DISABLE_WARNINGS

namespace ranges
{
/// \addtogroup group-views
/// @{
template<typename Rng, typename Fun>
struct RANGES_EMPTY_BASES remove_when_view
: view_adaptor<remove_when_view<Rng, Fun>, Rng,
is_finite<Rng>::value ? finite : range_cardinality<Rng>::value>
, private box<semiregular_t<Fun>>
{
remove_when_view() = default;
constexpr remove_when_view(Rng rng, Fun fun)
: remove_when_view::view_adaptor{detail::move(rng)}
, remove_when_view::box(detail::move(fun))
{}

private:
friend range_access;
bool zero_;

struct adaptor : adaptor_base
{
adaptor() = default;
constexpr adaptor(remove_when_view & rng) noexcept
: rng_(&rng)
{}
static constexpr iterator_t<Rng> begin(remove_when_view & rng)
{
return *rng.begin_;
}
constexpr void next(iterator_t<Rng> & it) const
{
RANGES_ASSERT(it != ranges::end(rng_->base()));
rng_->satisfy_forward(++it);
}
void advance() = delete;
void distance_to() = delete;

private:
remove_when_view * rng_;
};
constexpr adaptor begin_adaptor()
{
cache_begin();
return {*this};
}
constexpr adaptor end_adaptor()
{
return {*this};
}

constexpr void satisfy_forward(iterator_t<Rng> & it)
{
if(zero_)
{
// if the last match consumed zero elements, we already bumped the
// position so can stop here.
zero_ = false;
return;
}

auto const last = ranges::end(this->base());
auto & fun = this->remove_when_view::box::get();
if(it != last)
{
auto p = invoke(fun, it, last);
if(p.first)
{
zero_ = (it == p.second);
it = p.second;
}
}
}

constexpr void cache_begin()
{
if(begin_)
return;
auto it = ranges::begin(this->base());
zero_ = false;
satisfy_forward(it);
begin_.emplace(std::move(it));
}

detail::non_propagating_cache<iterator_t<Rng>> begin_;
};

#if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17
CPP_template(typename Rng, typename Fun)(requires CopyConstructible<Fun>)
remove_when_view(Rng &&, Fun)
->remove_when_view<view::all_t<Rng>, Fun>;
#endif

namespace view
{
struct remove_when_fn
{
private:
friend view_access;
template<typename Fun>
static auto bind(remove_when_fn remove_when, Fun && fun)
{
return make_pipeable(std::bind(
remove_when, std::placeholders::_1, static_cast<Fun &&>(fun)));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rebase on master and replace std::bind with bind_back, and make this function constexpr. (You should also #include <range/v3/functional/bind_back.hpp>.)

}

public:
template<typename Rng, typename Fun>
constexpr auto operator()(Rng && rng, Fun fun) const
-> CPP_ret(remove_when_view<all_t<Rng>, detail::delimiter_specifier<Fun>>)( //
requires ViewableRange<Rng> && InputRange<Rng> && Predicate<
Fun const &, range_reference_t<Rng>> && CopyConstructible<Fun>)
{
return {all(static_cast<Rng &&>(rng)),
detail::delimiter_specifier<Fun>{std::move(fun)}};
}
template<typename Rng, typename Fun>
auto operator()(Rng && rng, Fun fun) const -> CPP_ret(
remove_when_view<all_t<Rng>, Fun>)( //
requires ViewableRange<Rng> && ForwardRange<Rng> &&
Invocable<Fun &, iterator_t<Rng>, sentinel_t<Rng>> &&
Invocable<Fun &, iterator_t<Rng>, iterator_t<Rng>> &&
CopyConstructible<Fun> && ConvertibleTo<
invoke_result_t<Fun &, iterator_t<Rng>, sentinel_t<Rng>>,
std::pair<bool, iterator_t<Rng>>>)
{
return {all(static_cast<Rng &&>(rng)), std::move(fun)};
}
};

/// \relates remove_when_fn
/// \ingroup group-views
RANGES_INLINE_VARIABLE(view<remove_when_fn>, remove_when)
} // namespace view
/// @}
} // namespace ranges

RANGES_RE_ENABLE_WARNINGS

#include <range/v3/detail/satisfy_boost_range.hpp>
RANGES_SATISFY_BOOST_RANGE(::ranges::remove_when_view)

#endif
19 changes: 3 additions & 16 deletions include/range/v3/view/split_when.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

#include <range/v3/range_fwd.hpp>

#include <range/v3/algorithm/find_if_not.hpp>
#include <range/v3/functional/invoke.hpp>
#include <range/v3/iterator/default_sentinel.hpp>
#include <range/v3/iterator/operations.hpp>
Expand All @@ -35,6 +34,7 @@
#include <range/v3/view/iota.hpp>
#include <range/v3/view/take_while.hpp>
#include <range/v3/view/view.hpp>
#include <range/v3/detail/delimiter_specifier.hpp>

namespace ranges
{
Expand Down Expand Up @@ -168,19 +168,6 @@ namespace ranges
return make_pipeable(
std::bind(split_when, std::placeholders::_1, bind_forward<T>(t)));
}
template<typename Pred>
struct predicate_pred
{
semiregular_t<Pred> pred_;

template<typename I, typename S>
auto operator()(I cur, S end) const -> CPP_ret(std::pair<bool, I>)( //
requires Sentinel<S, I>)
{
auto where = ranges::find_if_not(cur, end, std::ref(pred_));
return {cur != where, where};
}
};

public:
template<typename Rng, typename Fun>
Expand All @@ -197,12 +184,12 @@ namespace ranges
}
template<typename Rng, typename Fun>
auto operator()(Rng && rng, Fun fun) const
-> CPP_ret(split_when_view<all_t<Rng>, predicate_pred<Fun>>)( //
-> CPP_ret(split_when_view<all_t<Rng>, detail::delimiter_specifier<Fun>>)( //
requires ViewableRange<Rng> && ForwardRange<Rng> && Predicate<
Fun const &, range_reference_t<Rng>> && CopyConstructible<Fun>)
{
return {all(static_cast<Rng &&>(rng)),
predicate_pred<Fun>{std::move(fun)}};
detail::delimiter_specifier<Fun>{std::move(fun)}};
}
};

Expand Down
1 change: 1 addition & 0 deletions test/view/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ rv3_add_test(test.view.partial_sum view.partial_sum partial_sum.cpp)
rv3_add_test(test.view.repeat view.repeat repeat.cpp)
rv3_add_test(test.view.remove view.remove remove.cpp)
rv3_add_test(test.view.remove_if view.remove_if remove_if.cpp)
rv3_add_test(test.view.remove_when view.remove_when remove_when.cpp)
rv3_add_test(test.view.replace view.replace replace.cpp)
rv3_add_test(test.view.replace_if view.replace_if replace_if.cpp)
rv3_add_test(test.view.reverse view.reverse reverse.cpp)
Expand Down
40 changes: 40 additions & 0 deletions test/view/remove_when.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Range v3 library
//
// Copyright Barry Revzin 2019-present
//
// Use, modification and distribution is subject to the
// Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// Project home: https://github.com/ericniebler/range-v3

#include <list>
#include <string>

#include <range/v3/utility/copy.hpp>
#include <range/v3/view/remove_when.hpp>

#include "../simple_test.hpp"
#include "../test_utils.hpp"

int main()
{
using namespace ranges;

// a simple predicate behaves just like a worse remove_if
std::string str = "The quick brown fox";
auto rng0 = view::remove_when(str, [](char c) { return c == ' '; });
::check_equal(rng0, std::string("Thequickbrownfox"));

// a silly predicate that actually doesn't remove anything
std::list<int> lst = {1, 2, 3, 4, 5};
auto rng1 =
view::remove_when(lst, [](auto i, auto) { return std::make_pair(true, i); });
::check_equal(rng1, {1, 2, 3, 4, 5});

// remove 3s and what's after 3
auto rng2 = view::remove_when(
lst, [](auto i, auto) { return std::make_pair(*i == 3, next(i, 2)); });
::check_equal(rng2, {1, 2, 5});
}