diff --git a/include/range/v3/view/successor.hpp b/include/range/v3/view/successor.hpp new file mode 100644 index 0000000000..41aac0523b --- /dev/null +++ b/include/range/v3/view/successor.hpp @@ -0,0 +1,116 @@ +/// \file +// Range v3 library +// +// Copyright Semir Vrana 2020-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_SUCCESSOR_HPP +#define RANGES_V3_VIEW_SUCCESSOR_HPP + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace ranges +{ + /// \addtogroup group-views + /// @{ + + /// Iteratively calls a function on an initial value + /// successor_view(f, x) -> [x, f(x), f(f(x)), ...] + template + struct successor_view : view_facade, infinite> + { + private: + friend range_access; + using result_t = T; + semiregular_box_t gen_; + semiregular_box_t val_; + struct cursor + { + private: + successor_view * view_; + + public: + cursor() = default; + explicit cursor(successor_view * view) + : view_(view) + {} + const result_t & read() const + { + return view_->val_; + } + void next() + { + view_->val_ = ranges::invoke(view_->gen_, std::move(view_->val_)); + } + }; + cursor begin_cursor() + { + return cursor{this}; + } + unreachable_sentinel_t end_cursor() const + { + return {}; + } + + public: + successor_view() = default; + explicit successor_view(G g, T x) + : gen_(std::move(g)) + , val_(std::move(x)) + {} + const result_t & cached() + { + return *val_; + } + }; + + namespace views + { + struct successor_fn + { + template + auto operator()(G g, T x) const -> CPP_ret(successor_view)( // + requires invocable && copy_constructible && + copy_constructible && + assignable_from>) + { + return successor_view{std::move(g), std::move(x)}; + } + }; + + /// \relates successor_fn + /// \ingroup group-views + RANGES_INLINE_VARIABLE(successor_fn, successor) + } // namespace views + /// \@} +} // namespace ranges + +#include +#include +RANGES_SATISFY_BOOST_RANGE(::ranges::successor_view) + +#endif diff --git a/test/view/CMakeLists.txt b/test/view/CMakeLists.txt index 338e9985b4..68036dee20 100644 --- a/test/view/CMakeLists.txt +++ b/test/view/CMakeLists.txt @@ -54,6 +54,7 @@ rv3_add_test(test.view.span view.span span.cpp) rv3_add_test(test.view.split view.split split.cpp) rv3_add_test(test.view.stride view.stride stride.cpp) rv3_add_test(test.view.subrange view.subrange subrange.cpp) +rv3_add_test(test.view.successor view.successor successor.cpp) rv3_add_test(test.view.tail view.tail tail.cpp) rv3_add_test(test.view.take view.take take.cpp) rv3_add_test(test.view.take_exactly view.take_exactly take_exactly.cpp) diff --git a/test/view/successor.cpp b/test/view/successor.cpp new file mode 100644 index 0000000000..bb4bcc4e09 --- /dev/null +++ b/test/view/successor.cpp @@ -0,0 +1,62 @@ +// Range v3 library +// +// Copyright Semir Vrana 2020-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 +#include +#include +#include + +#include "../simple_test.hpp" +#include "../test_utils.hpp" + +namespace views = ranges::views; + +int main() +{ + { + auto powers_of_10 = views::successor([](int x) { return x * 10; }, 1); + CPP_assert(ranges::input_range && + ranges::view_); + check_equal(powers_of_10 | views::take_exactly(5), {1, 10, 100, 1000, 10000}); + } + + // Test that we only call the function once for each dereferenceable position + { + int i = 0; + auto rng = views::successor( + [&i](int n) { + ++i; + return n + 1; + }, + 0); + auto rng2 = std::move(rng); + auto it = rng2.begin(); + CHECK(i == 0); + CHECK(*it == 0); + CHECK(i == 0); + ++it; + CHECK(i == 1); + CHECK(*it == 1); + CHECK(i == 1); + } + + // Test that skipping past positions works correctly + { + auto times_10 = [](int x) { return x * 10; }; + auto rng = ranges::views::successor(times_10, 1) // + | ranges::views::drop_exactly(3) // + | ranges::views::take_exactly(2); + + check_equal(rng, {1000, 10000}); + } + + return test_result(); +}