diff --git a/include/range/v3/istream_range.hpp b/include/range/v3/istream_range.hpp index 01e30ee651..67d655aa33 100644 --- a/include/range/v3/istream_range.hpp +++ b/include/range/v3/istream_range.hpp @@ -33,7 +33,7 @@ namespace ranges private: friend range_access; std::istream *sin_; - semiregular_t obj_; + movesemiregular_t obj_; struct cursor { private: diff --git a/include/range/v3/range_concepts.hpp b/include/range/v3/range_concepts.hpp index 2612d8f26d..37c97775ae 100644 --- a/include/range/v3/range_concepts.hpp +++ b/include/range/v3/range_concepts.hpp @@ -204,12 +204,11 @@ namespace ranges /// struct View - : refines + : refines { template auto requires_() -> decltype( concepts::valid_expr( - concepts::model_of(), concepts::is_true(detail::view_predicate_()) )); }; @@ -223,7 +222,7 @@ namespace ranges {}; struct ForwardView - : refines + : refines {}; struct BidirectionalView diff --git a/include/range/v3/utility/memory.hpp b/include/range/v3/utility/memory.hpp index cf2b64e13b..cb01cc6fe9 100644 --- a/include/range/v3/utility/memory.hpp +++ b/include/range/v3/utility/memory.hpp @@ -23,10 +23,11 @@ #include #include -#include #include +#include #include #include +#include namespace ranges { @@ -44,6 +45,13 @@ namespace ranges std::return_temporary_buffer(p); } }; + + template::value)> + std::unique_ptr make_unique(Args &&... args) + { + return std::unique_ptr{new T(static_cast(args)...)}; + } } /// \endcond diff --git a/include/range/v3/utility/polymorphic_cast.hpp b/include/range/v3/utility/polymorphic_cast.hpp index 836a2649ba..65b8005ddc 100644 --- a/include/range/v3/utility/polymorphic_cast.hpp +++ b/include/range/v3/utility/polymorphic_cast.hpp @@ -5,6 +5,10 @@ #ifndef RANGES_V3_UTILITY_POLYMORPHIC_CAST_HPP #define RANGES_V3_UTILITY_POLYMORPHIC_CAST_HPP + +#include +#include +#include #include namespace ranges @@ -12,10 +16,26 @@ namespace ranges inline namespace v3 { template - inline Target polymorphic_downcast(Source* x) + auto polymorphic_downcast(Source *x) noexcept -> + meta::if_, + decltype((static_cast(x), dynamic_cast(x)))> + { + auto result = static_cast(x); + RANGES_ASSERT(dynamic_cast(x) == result); + return result; + } + template + auto polymorphic_downcast(Source &&x) noexcept -> + meta::if_, + decltype((static_cast(std::declval()), + dynamic_cast(std::declval())))> { - RANGES_ASSERT(dynamic_cast(x) == x); - return static_cast(x); + auto &&result = static_cast(static_cast(x)); +#ifndef NDEBUG + auto &&dresult = dynamic_cast(static_cast(x)); + RANGES_ASSERT(std::addressof(dresult) == std::addressof(result)); +#endif + return static_cast(result); } } } diff --git a/include/range/v3/utility/semiregular.hpp b/include/range/v3/utility/semiregular.hpp index 329e33247d..f8368dd55f 100644 --- a/include/range/v3/utility/semiregular.hpp +++ b/include/range/v3/utility/semiregular.hpp @@ -195,6 +195,13 @@ namespace ranges using semiregular_t = meta::if_, T, semiregular>; + template + using movesemiregular_t = + meta::if_c< + Movable() && DefaultConstructible(), + T, + semiregular>; + template using semiregular_ref_or_val_t = meta::if_< diff --git a/include/range/v3/view/any_view.hpp b/include/range/v3/view/any_view.hpp index 6e08d9f069..c8209f1411 100644 --- a/include/range/v3/view/any_view.hpp +++ b/include/range/v3/view/any_view.hpp @@ -2,6 +2,7 @@ // Range v3 library // // Copyright Eric Niebler 2014 +// Copyright Casey Carter 2017 // // Use, modification and distribution is subject to the // Boost Software License, Version 1.0. (See accompanying @@ -14,17 +15,17 @@ #ifndef RANGES_V3_VIEW_ANY_VIEW_HPP #define RANGES_V3_VIEW_ANY_VIEW_HPP -#include -#include +#include #include -#include -#include +#include #include -#include #include +#include #include +#include +#include +#include #include -#include namespace ranges { @@ -41,48 +42,165 @@ namespace ranges /// \cond namespace detail { - struct any_object + // workaround the fact that typeid ignores cv-qualifiers + template struct rtti_tag {}; + + struct any_ref + { + any_ref() = default; + template + constexpr any_ref(T &obj) noexcept + : obj_{std::addressof(obj)} +#ifndef NDEBUG + , info_{&typeid(rtti_tag)} +#endif + {} + template + T &get() const noexcept + { + RANGES_ASSERT(obj_ && info_ && *info_ == typeid(rtti_tag)); + return *const_cast(static_cast(obj_)); + } + private: + void const volatile *obj_ = nullptr; +#ifndef NDEBUG + std::type_info const *info_ = nullptr; +#endif + }; + + template + struct cloneable + : Base { - virtual ~any_object() = default; + using Base::Base; + virtual ~cloneable() = default; + cloneable() = default; + cloneable(cloneable const &) = delete; + cloneable &operator=(cloneable const &) = delete; + virtual std::unique_ptr clone() const = 0; }; - template - struct object : any_object + constexpr category to_cat_(concepts::InputRange *) { return category::input; } + constexpr category to_cat_(concepts::ForwardRange *) { return category::forward; } + constexpr category to_cat_(concepts::BidirectionalRange *) { return category::bidirectional; } + constexpr category to_cat_(concepts::RandomAccessRange *) { return category::random_access; } + + template + using AnyCompatibleRange = ConvertibleTo, Ref>; + + template + struct any_view_sentinel_impl + : private box, any_view_sentinel_impl> { + any_view_sentinel_impl() = default; + any_view_sentinel_impl(Rng &rng) + noexcept(noexcept(box_t(ranges::end(rng)))) + : box_t(ranges::end(rng)) + {} + void init(Rng &rng) noexcept + { + box_t::get() = ranges::end(rng); + } + sentinel_t const &get(Rng const &) const noexcept + { + return box_t::get(); + } private: - T obj; - public: - object() = default; - object(T o) - : obj(std::move(o)) + using box_t = typename any_view_sentinel_impl::box; + }; + + template + struct any_view_sentinel_impl()))>> + { + any_view_sentinel_impl() = default; + any_view_sentinel_impl(Rng &) noexcept + {} + void init(Rng &) noexcept {} - T &get() { return obj; } - T const &get() const { return obj; } + sentinel_t get(Rng const &rng) const noexcept + { + return ranges::end(rng); + } }; - template - struct any_cursor_interface + template + struct any_input_view_interface { - virtual ~any_cursor_interface() = default; - virtual any_object const &iter() const = 0; + virtual ~any_input_view_interface() = default; + virtual void init() = 0; + virtual bool done() const = 0; virtual Ref read() const = 0; - virtual bool equal(any_cursor_interface const &) const = 0; virtual void next() = 0; - virtual any_cursor_interface *clone_() const = 0; - any_cursor_interface *clone() const + }; + + template + struct any_input_cursor + { + using single_pass = std::true_type; + + any_input_cursor() = default; + constexpr any_input_cursor(any_input_view_interface &view) noexcept + : view_{std::addressof(view)} + {} + Ref read() const { return view_->read(); } + void next() { view_->next(); } + bool equal(any_input_cursor const &) const noexcept + { + return true; + } + bool equal(default_sentinel) const { - return this->clone_(); + return !view_ || view_->done(); } + private: + any_input_view_interface *view_ = nullptr; }; - template - struct any_cursor_interface - : any_cursor_interface + template + struct any_input_view_impl + : any_input_view_interface + , private tagged_compressed_tuple)> + , private any_view_sentinel_impl { - any_cursor_interface *clone() const + CONCEPT_ASSERT(AnyCompatibleRange()); + + explicit any_input_view_impl(Rng rng_) + : tagged_t{std::move(rng_), iterator_t{}} + {} + any_input_view_impl(any_input_view_impl const &) = delete; + any_input_view_impl &operator=(any_input_view_impl const &) = delete; + + private: + using tagged_t = tagged_compressed_tuple)>; + using tagged_t::range; + using tagged_t::current; + using sentinel_box_t = any_view_sentinel_impl; + + virtual void init() override + { + auto &rng = range(); + sentinel_box_t::init(rng); + current() = ranges::begin(rng); + } + virtual bool done() const override { - return static_cast(this->clone_()); + return current() == sentinel_box_t::get(range()); } + virtual Ref read() const override { return *current(); } + virtual void next() override { ++current(); } + }; + + template + struct any_cursor_interface + { + virtual ~any_cursor_interface() = default; + virtual any_ref iter() const = 0; // returns a const ref to the cursor's wrapped iterator + virtual Ref read() const = 0; + virtual bool equal(any_cursor_interface const &) const = 0; + virtual void next() = 0; }; template @@ -90,10 +208,6 @@ namespace ranges : any_cursor_interface { virtual void prev() = 0; - any_cursor_interface *clone() const - { - return static_cast(this->clone_()); - } }; template @@ -102,155 +216,103 @@ namespace ranges { virtual void advance(std::ptrdiff_t) = 0; virtual std::ptrdiff_t distance_to(any_cursor_interface const &) const = 0; - any_cursor_interface *clone() const - { - return static_cast(this->clone_()); - } }; - template - struct any_sentinel_impl; + template + using any_cloneable_cursor_interface = + cloneable>; template struct any_cursor_impl - : any_cursor_interface + : any_cloneable_cursor_interface { - private: - template - friend struct any_sentinel_impl; CONCEPT_ASSERT(ConvertibleTo, Ref>()); - using Input = any_cursor_interface; - object it_; + CONCEPT_ASSERT(Cat >= category::forward); - CONCEPT_REQUIRES(EqualityComparable()) - bool equal_(Input const &that) const - { - any_cursor_impl const *pthat = - polymorphic_downcast(&that); - return pthat->it_.get() == it_.get(); - } - CONCEPT_REQUIRES(!EqualityComparable()) - bool equal_(Input const &) const - { - return true; - } - - public: any_cursor_impl() = default; any_cursor_impl(I it) : it_{std::move(it)} {} - object const &iter() const // override + private: + using Forward = any_cursor_interface; + + I it_; + + any_ref iter() const // override { return it_; } Ref read() const // override { - return *it_.get(); + return *it_; } - bool equal(Input const &that) const // override + bool equal(Forward const &that_) const // override { - return equal_(that); + auto &that = polymorphic_downcast(that_); + return that.it_ == it_; } void next() // override { - ++it_.get(); + ++it_; } - any_cursor_impl *clone_() const // override + std::unique_ptr> clone() const // override { - return new any_cursor_impl{it_.get()}; + return detail::make_unique(it_); } void prev() // override (sometimes; it's complicated) { - --it_.get(); + --it_; } void advance(std::ptrdiff_t n) // override-ish { - it_.get() += n; + it_ += n; } std::ptrdiff_t distance_to( - any_cursor_interface const &that) const // override-ish + any_cursor_interface const &that_) const // override-ish { - any_cursor_impl const *pthat = - polymorphic_downcast(&that); - return static_cast(pthat->it_.get() - it_.get()); + auto &that = polymorphic_downcast(that_); + return static_cast(that.it_ - it_); } }; - struct any_sentinel_interface + struct fully_erased_view { - virtual ~any_sentinel_interface() = default; - virtual bool equal(any_object const &) const = 0; - virtual any_sentinel_interface *clone() const = 0; + virtual bool at_end(any_ref) const = 0; // any_ref is a const ref to a wrapped iterator + // to be compared to the erased view's end sentinel + protected: + ~fully_erased_view() = default; }; - template - struct any_sentinel_impl - : any_sentinel_interface - { - private: - S s_; - public: - any_sentinel_impl() = default; - any_sentinel_impl(S s) - : s_(std::move(s)) - {} - bool equal(any_object const &that) const override - { - object const *pthat = polymorphic_downcast const *>(&that); - return s_ == pthat->get(); - } - any_sentinel_impl *clone() const override - { - return new any_sentinel_impl{s_}; - } - }; - - template - struct any_cursor; - struct any_sentinel { - private: - template - friend struct any_cursor; - std::unique_ptr ptr_; - public: any_sentinel() = default; - template, any_sentinel>()), - CONCEPT_REQUIRES_(InputRange())> - any_sentinel(Rng &&rng, end_tag) - : ptr_{new any_sentinel_impl, iterator_t>{ - end(rng)}} + constexpr explicit any_sentinel(fully_erased_view const &view) noexcept + : view_{&view} {} - any_sentinel(any_sentinel &&) = default; - any_sentinel(any_sentinel const &that) - : ptr_{that.ptr_ ? that.ptr_->clone() : nullptr} - {} - any_sentinel &operator=(any_sentinel &&) = default; - any_sentinel &operator=(any_sentinel const &that) - { - ptr_.reset(that.ptr_ ? that.ptr_->clone() : nullptr); - return *this; - } + private: + template friend struct any_cursor; + + fully_erased_view const *view_ = nullptr; }; template struct any_cursor { private: - friend struct any_sentinel; - std::unique_ptr> ptr_; + CONCEPT_ASSERT(Cat >= category::forward); + + std::unique_ptr> ptr_; + + template + using impl_t = any_cursor_impl, Ref, Cat>; public: - using single_pass = meta::bool_; any_cursor() = default; template, any_cursor>()), - CONCEPT_REQUIRES_(InputRange() && - ConvertibleTo, Ref>())> - any_cursor(Rng &&rng, begin_tag) - : ptr_{new any_cursor_impl, Ref, Cat>{begin(rng)}} + CONCEPT_REQUIRES_(ForwardRange() && + AnyCompatibleRange())> + explicit any_cursor(Rng &&rng) + : ptr_{detail::make_unique>(begin(rng))} {} any_cursor(any_cursor &&) = default; any_cursor(any_cursor const &that) @@ -259,7 +321,7 @@ namespace ranges any_cursor &operator=(any_cursor &&) = default; any_cursor &operator=(any_cursor const &that) { - ptr_.reset(that.ptr_ ? that.ptr_->clone() : nullptr); + ptr_ = (that.ptr_ ? that.ptr_->clone() : nullptr); return *this; } Ref read() const @@ -270,12 +332,12 @@ namespace ranges bool equal(any_cursor const &that) const { RANGES_EXPECT(!ptr_ == !that.ptr_); - return (!ptr_ && !that.ptr_) || ptr_->equal(*that.ptr_); + return !ptr_ || ptr_->equal(*that.ptr_); } bool equal(any_sentinel const &that) const { - RANGES_EXPECT(!ptr_ == !that.ptr_); - return (!ptr_ && !that.ptr_) || that.ptr_->equal(ptr_->iter()); + RANGES_EXPECT(!ptr_ == !that.view_); + return !ptr_ || that.view_->at_end(ptr_->iter()); } void next() { @@ -304,43 +366,50 @@ namespace ranges template struct any_view_interface + : fully_erased_view { + CONCEPT_ASSERT(Cat >= category::forward); + virtual ~any_view_interface() = default; virtual any_cursor begin_cursor() = 0; - virtual any_sentinel end_cursor() = 0; - virtual any_view_interface *clone() const = 0; }; + template + using any_cloneable_view_interface = + cloneable>; + template struct any_view_impl - : any_view_interface + : any_cloneable_view_interface + , private box> + , private any_view_sentinel_impl { - private: - CONCEPT_ASSERT(ConvertibleTo, Ref>()); - Rng rng_; - public: + CONCEPT_ASSERT(Cat >= category::forward); + CONCEPT_ASSERT(AnyCompatibleRange()); + any_view_impl() = default; any_view_impl(Rng rng) - : rng_(std::move(rng)) + : range_box_t{std::move(rng)} + , sentinel_box_t{range_box_t::get()} // NB: initialization order dependence {} + private: + using range_box_t = box; + using sentinel_box_t = any_view_sentinel_impl; + any_cursor begin_cursor() override { - return {rng_, begin_tag{}}; + return any_cursor{range_box_t::get()}; } - any_sentinel end_cursor() override + bool at_end(any_ref it_) const override { - return {rng_, end_tag{}}; + auto &it = it_.get const>(); + return it == sentinel_box_t::get(range_box_t::get()); } - any_view_interface *clone() const override + std::unique_ptr> clone() const override { - return new any_view_impl{rng_}; + return detail::make_unique(range_box_t::get()); } }; - - constexpr category to_cat_(concepts::InputRange *) { return category::input; } - constexpr category to_cat_(concepts::ForwardRange *) { return category::forward; } - constexpr category to_cat_(concepts::BidirectionalRange *) { return category::bidirectional; } - constexpr category to_cat_(concepts::RandomAccessRange *) { return category::random_access; } } /// \endcond @@ -350,21 +419,35 @@ namespace ranges struct any_view : view_facade, unknown> { - private: friend range_access; - std::unique_ptr> ptr_; - detail::any_cursor begin_cursor() - { - return ptr_ ? ptr_->begin_cursor() : detail::value_init{}; - } - detail::any_sentinel end_cursor() + CONCEPT_ASSERT(Cat >= category::forward); + + any_view() = default; + template, any_view>>, + InputRange, + meta::defer>::value)> + any_view(Rng &&rng) + : any_view(static_cast(rng), + meta::bool_{}) >= Cat>{}) + {} + any_view(any_view &&) = default; + any_view(any_view const &that) + : ptr_{that.ptr_ ? that.ptr_->clone() : nullptr} + {} + any_view &operator=(any_view &&) = default; + any_view &operator=(any_view const &that) { - return ptr_ ? ptr_->end_cursor() : detail::value_init{}; + ptr_ = (that.ptr_ ? that.ptr_->clone() : nullptr); + return *this; } + private: + template + using impl_t = detail::any_view_impl, Ref, Cat>; template - any_view(Rng && rng, std::true_type) - : ptr_{new detail::any_view_impl, Ref, Cat>{ - view::all(static_cast(rng))}} + any_view(Rng &&rng, std::true_type) + : ptr_{detail::make_unique>(view::all(static_cast(rng)))} {} template any_view(Rng &&, std::false_type) @@ -372,29 +455,48 @@ namespace ranges static_assert(detail::to_cat_(range_concept{}) >= Cat, "The range passed to any_view() does not model the requested category"); } - template - using CompatibleRange = ConvertibleTo, Ref>; - public: + + detail::any_cursor begin_cursor() + { + return ptr_ ? ptr_->begin_cursor() : detail::value_init{}; + } + detail::any_sentinel end_cursor() const noexcept + { + return detail::any_sentinel{*ptr_}; + } + + std::unique_ptr> ptr_; + }; + + template + struct any_view + : view_facade, unknown> + { + friend range_access; + any_view() = default; template, any_view>>, InputRange, - meta::defer>::value)> - any_view(Rng && rng) - : any_view(static_cast(rng), - meta::bool_{}) >= Cat>{}) + meta::defer>::value)> + any_view(Rng &&rng) + : ptr_{std::make_shared>(view::all(static_cast(rng)))} {} - any_view(any_view &&) = default; - any_view(any_view const &that) - : ptr_{that.ptr_ ? that.ptr_->clone() : nullptr} - {} - any_view &operator=(any_view &&) = default; - any_view &operator=(any_view const &that) + private: + template + using impl_t = detail::any_input_view_impl, Ref>; + + detail::any_input_cursor begin_cursor() { - ptr_.reset(that.ptr_ ? that.ptr_->clone() : nullptr); - return *this; + if (!ptr_) + return {}; + + ptr_->init(); + return detail::any_input_cursor{*ptr_}; } + + std::shared_ptr> ptr_; }; template diff --git a/include/range/v3/view/generate.hpp b/include/range/v3/view/generate.hpp index a700e50c7f..8e98ec34cf 100644 --- a/include/range/v3/view/generate.hpp +++ b/include/range/v3/view/generate.hpp @@ -39,18 +39,17 @@ namespace ranges : view_facade, infinite> { private: - friend struct ranges::range_access; + friend range_access; using result_t = result_of_t; - semiregular_t gen_; - semiregular_t val_; + movesemiregular_t gen_; + movesemiregular_t val_; struct cursor { private: generate_view *view_; public: - using single_pass = std::true_type; cursor() = default; - cursor(generate_view &view) + explicit cursor(generate_view &view) : view_(&view) {} result_t read() const @@ -68,7 +67,7 @@ namespace ranges } cursor begin_cursor() { - return {*this}; + return cursor{*this}; } unreachable end_cursor() const { @@ -92,7 +91,7 @@ namespace ranges template using Concept = meta::and_< Invocable, - CopyConstructible, + MoveConstructible, std::is_object>>, Constructible>, result_of_t>, Assignable>&, result_of_t>>; @@ -115,8 +114,8 @@ namespace ranges { CONCEPT_ASSERT_MSG(Invocable(), "The function object G must be callable with no arguments."); - CONCEPT_ASSERT_MSG(CopyConstructible(), - "The function object G must be CopyConstructible."); + CONCEPT_ASSERT_MSG(MoveConstructible(), + "The function object G must be MoveConstructible."); using T = result_of_t; using D = detail::decay_t; CONCEPT_ASSERT_MSG(std::is_object(), diff --git a/include/range/v3/view/generate_n.hpp b/include/range/v3/view/generate_n.hpp index b00fa74e5b..7f34af2dbb 100644 --- a/include/range/v3/view/generate_n.hpp +++ b/include/range/v3/view/generate_n.hpp @@ -38,19 +38,18 @@ namespace ranges : view_facade, finite> { private: - friend struct ranges::range_access; + friend range_access; using result_t = result_of_t; - semiregular_t gen_; - semiregular_t val_; + movesemiregular_t gen_; + movesemiregular_t val_; std::size_t n_; struct cursor { private: generate_n_view *rng_; public: - using single_pass = std::true_type; cursor() = default; - cursor(generate_n_view &rng) + explicit cursor(generate_n_view &rng) : rng_(&rng) {} bool equal(default_sentinel) const @@ -74,7 +73,7 @@ namespace ranges } cursor begin_cursor() { - return {*this}; + return cursor{*this}; } public: generate_n_view() = default; diff --git a/include/range/v3/view_interface.hpp b/include/range/v3/view_interface.hpp index 2c0ba90fed..d8deae62e2 100644 --- a/include/range/v3/view_interface.hpp +++ b/include/range/v3/view_interface.hpp @@ -170,7 +170,7 @@ namespace ranges // rng[{4,6}] template())> - auto operator[](detail::slice_bounds> offs) -> + auto operator[](detail::slice_bounds> offs) & -> decltype(std::declval()(std::declval(), offs.from, offs.to)) { return Slice{}(derived(), offs.from, offs.to); @@ -178,17 +178,25 @@ namespace ranges /// \overload template())> - auto operator[](detail::slice_bounds> offs) const -> + auto operator[](detail::slice_bounds> offs) const & -> decltype(std::declval()(std::declval(), offs.from, offs.to)) { return Slice{}(derived(), offs.from, offs.to); } + /// \overload + template())> + auto operator[](detail::slice_bounds> offs) && -> + decltype(std::declval()(std::declval(), offs.from, offs.to)) + { + return Slice{}(detail::move(derived()), offs.from, offs.to); + } // rng[{4,end-2}] /// \overload template())> auto operator[](detail::slice_bounds, - detail::from_end_>> offs) -> + detail::from_end_>> offs) & -> decltype(std::declval()(std::declval(), offs.from, offs.to)) { return Slice{}(derived(), offs.from, offs.to); @@ -197,17 +205,26 @@ namespace ranges template())> auto operator[](detail::slice_bounds, - detail::from_end_>> offs) const -> + detail::from_end_>> offs) const & -> decltype(std::declval()(std::declval(), offs.from, offs.to)) { return Slice{}(derived(), offs.from, offs.to); } + /// \overload + template())> + auto operator[](detail::slice_bounds, + detail::from_end_>> offs) && -> + decltype(std::declval()(std::declval(), offs.from, offs.to)) + { + return Slice{}(detail::move(derived()), offs.from, offs.to); + } // rng[{end-4,end-2}] /// \overload template())> auto operator[](detail::slice_bounds>, - detail::from_end_>> offs) -> + detail::from_end_>> offs) & -> decltype(std::declval()(std::declval(), offs.from, offs.to)) { return Slice{}(derived(), offs.from, offs.to); @@ -216,16 +233,25 @@ namespace ranges template())> auto operator[](detail::slice_bounds>, - detail::from_end_>> offs) const -> + detail::from_end_>> offs) const & -> decltype(std::declval()(std::declval(), offs.from, offs.to)) { return Slice{}(derived(), offs.from, offs.to); } + /// \overload + template())> + auto operator[](detail::slice_bounds>, + detail::from_end_>> offs) && -> + decltype(std::declval()(std::declval(), offs.from, offs.to)) + { + return Slice{}(detail::move(derived()), offs.from, offs.to); + } // rng[{4,end}] /// \overload template())> - auto operator[](detail::slice_bounds, end_fn> offs) -> + auto operator[](detail::slice_bounds, end_fn> offs) & -> decltype(std::declval()(std::declval(), offs.from, offs.to)) { return Slice{}(derived(), offs.from, offs.to); @@ -233,16 +259,24 @@ namespace ranges /// \overload template())> - auto operator[](detail::slice_bounds, end_fn> offs) const -> + auto operator[](detail::slice_bounds, end_fn> offs) const & -> decltype(std::declval()(std::declval(), offs.from, offs.to)) { return Slice{}(derived(), offs.from, offs.to); } + /// \overload + template())> + auto operator[](detail::slice_bounds, end_fn> offs) && -> + decltype(std::declval()(std::declval(), offs.from, offs.to)) + { + return Slice{}(detail::move(derived()), offs.from, offs.to); + } // rng[{end-4,end}] /// \overload template())> - auto operator[](detail::slice_bounds>, end_fn> offs) -> + auto operator[](detail::slice_bounds>, end_fn> offs) & -> decltype(std::declval()(std::declval(), offs.from, offs.to)) { return Slice{}(derived(), offs.from, offs.to); @@ -250,11 +284,19 @@ namespace ranges /// \overload template())> - auto operator[](detail::slice_bounds>, end_fn> offs) const -> + auto operator[](detail::slice_bounds>, end_fn> offs) const & -> decltype(std::declval()(std::declval(), offs.from, offs.to)) { return Slice{}(derived(), offs.from, offs.to); } + /// \overload + template())> + auto operator[](detail::slice_bounds>, end_fn> offs) && -> + decltype(std::declval()(std::declval(), offs.from, offs.to)) + { + return Slice{}(detail::move(derived()), offs.from, offs.to); + } /// Implicit conversion to something that looks like a container. template +#include +#include + +template +struct debug_input_view +{ + CONCEPT_ASSERT(std::is_object::value); + + using index_t = std::ptrdiff_t; + using version_t = unsigned long; + + T *data_ = nullptr; + T *last_ = nullptr; + version_t version_ = 0; + bool valid_ = false; + bool begin_called_ = false; + + debug_input_view() = default; + constexpr debug_input_view(T* data, index_t size) noexcept + : data_{data} + , last_{(RANGES_ENSURE(data || !size), data + size)} + , valid_{true} + {} + template + constexpr debug_input_view(T (&data)[N]) noexcept + : debug_input_view{data, N} + {} + RANGES_CXX14_CONSTEXPR + debug_input_view(debug_input_view &&that) noexcept + : data_{ranges::exchange(that.data_, nullptr)} + , last_{ranges::exchange(that.last_, nullptr)} + , valid_{ranges::exchange(that.valid_, false)} + { + ++that.version_; + if (that.begin_called_) + { + data_ = last_ = nullptr; + valid_ = false; + } + } + RANGES_CXX14_CONSTEXPR + debug_input_view &operator=(debug_input_view &&that) noexcept + { + ++that.version_; + data_ = ranges::exchange(that.data_, nullptr); + last_ = ranges::exchange(that.last_, nullptr); + valid_ = ranges::exchange(that.valid_, false); + begin_called_ = false; + if (that.begin_called_) + { + data_ = last_ = nullptr; + valid_ = false; + } + ++version_; + return *this; + } + + struct sentinel + { + debug_input_view *view_ = nullptr; + + sentinel() = default; + explicit constexpr sentinel(debug_input_view &view) noexcept + : view_{&view} + {} + }; + struct iterator + { + using iterator_category = std::input_iterator_tag; + using value_type = meta::_t>; + using difference_type = index_t; + using reference = T &; + using pointer = T *; + + debug_input_view *view_ = nullptr; + version_t version_ = 0; + + iterator() = default; + explicit constexpr iterator(debug_input_view &view) noexcept + : view_{&view}, version_{view.version_} + {} + + RANGES_CXX14_CONSTEXPR void check_current() const noexcept + { + RANGES_ENSURE(view_), RANGES_ENSURE(view_->version_ == version_); + } + + RANGES_CXX14_CONSTEXPR void check_dereferenceable() const noexcept + { + check_current(), RANGES_ENSURE(view_->data_ < view_->last_); + } + + RANGES_CXX14_CONSTEXPR + reference operator*() const noexcept + { + check_dereferenceable(); + return *view_->data_; + } + RANGES_CXX14_CONSTEXPR + iterator &operator++() noexcept + { + check_dereferenceable(); + ++view_->data_; + version_ = ++view_->version_; + return *this; + } + RANGES_CXX14_CONSTEXPR + void operator++(int) noexcept + { + ++*this; + } + + RANGES_CXX14_CONSTEXPR + friend bool operator==(iterator const &i, sentinel const &s) + { + RANGES_ENSURE(i.view_ == s.view_); + i.check_current(); + return i.view_->data_ == i.view_->last_; + } + RANGES_CXX14_CONSTEXPR + friend bool operator==(sentinel const &s, iterator const &i) + { + return i == s; + } + RANGES_CXX14_CONSTEXPR + friend bool operator!=(iterator const &i, sentinel const &s) + { + return !(i == s); + } + RANGES_CXX14_CONSTEXPR + friend bool operator!=(sentinel const &s, iterator const &i) + { + return !(i == s); + } + CONCEPT_REQUIRES(Sized) + RANGES_CXX14_CONSTEXPR + friend difference_type operator-(sentinel const& s, iterator const& i) + { + RANGES_ENSURE(i.view_ == s.view_); + i.check_current(); + return i.view_->last_ - i.view_->data_; + } + CONCEPT_REQUIRES(Sized) + RANGES_CXX14_CONSTEXPR + friend difference_type operator-(iterator const& i, sentinel const& s) + { + return -(s - i); + } + }; + RANGES_CXX14_CONSTEXPR iterator begin() noexcept + { + RANGES_ENSURE(valid_); + RANGES_ENSURE(!begin_called_); + begin_called_ = true; + return iterator{*this}; + } + RANGES_CXX14_CONSTEXPR sentinel end() noexcept + { + RANGES_ENSURE(valid_); + return sentinel{*this}; + } + CONCEPT_REQUIRES(Sized) + RANGES_CXX14_CONSTEXPR + std::size_t size() const noexcept + { + RANGES_ENSURE(valid_); + RANGES_ENSURE(!begin_called_); + return static_cast(last_ - data_); + } +}; + +#endif diff --git a/test/generator.cpp b/test/generator.cpp index c8c653e12a..e6db8849ae 100644 --- a/test/generator.cpp +++ b/test/generator.cpp @@ -191,25 +191,26 @@ int main() { using namespace ranges; + auto even = [](int i){ return i % 2 == 0; }; + { auto rng = ::iota_generator(0, 10); ::models(rng); CHECK(size(rng) == 10u); - CHECK(equal(rng, {0,1,2,3,4,5,6,7,8,9})); + ::check_equal(rng, {0,1,2,3,4,5,6,7,8,9}); } { auto rng = ::coro(::coro(::coro(::iota_generator(0, 10)))); ::has_type(rng); ::models(rng); CHECK(size(rng) == 10u); - CHECK(equal(rng, {0,1,2,3,4,5,6,7,8,9})); + ::check_equal(rng, {0,1,2,3,4,5,6,7,8,9}); } { - auto even = [](int i){ return i % 2 == 0; }; auto rng = ::coro(view::ints | view::filter(even) | view::take_exactly(10)); ::models(rng); CHECK(size(rng) == 10u); - CHECK(equal(rng, {0,2,4,6,8,10,12,14,16,18})); + ::check_equal(rng, {0,2,4,6,8,10,12,14,16,18}); } { auto const control = {1, 2, 3}; @@ -247,31 +248,28 @@ int main() auto rng = ::coro(vec); ::models(rng); CHECK(size(rng) == 3u); - CHECK(equal(rng, {false,false,false})); + ::check_equal(rng, {false,false,false}); } - CHECK(equal(f(42), g(42))); - CHECK(equal(f(42), h(42))); - // Since generator is a range, but not a view, it must be an lvalue to - // participate in view composition. + ::check_equal(f(42), g(42)); + ::check_equal(f(42), h(42)); + { -#if 0 auto rng = h(20) | view::transform([](int &x) { return ++x; }); -#else - auto gen = h(20); - auto rng = gen | view::transform([](int &x) { return ++x; }); -#endif ::check_equal(rng, {1,3,5,7,9,11,13,15,17,19}); } { - auto even = [](int i) { return i % 2 == 0; }; + auto rng = f(20) | view::filter(even); + ::check_equal(rng, {0,2,4,6,8,10,12,14,16,18}); + } + + { auto square = [](int i) { return i * i; }; int const some_ints[] = {0,1,2,3,4,5,6,7}; - auto rng1 = ::filter(view::all(some_ints), even); - auto rng2 = ::transform(view::all(rng1), square); - ::check_equal(rng2, {0,4,16,36}); + auto rng = ::transform(::filter(debug_input_view{some_ints}, even), square); + ::check_equal(rng, {0,4,16,36}); } std::cout << f(8) << '\n'; diff --git a/test/test_utils.hpp b/test/test_utils.hpp index 955e04e05c..0568904254 100644 --- a/test/test_utils.hpp +++ b/test/test_utils.hpp @@ -23,6 +23,7 @@ #include #include #include +#include "./debug_view.hpp" #include "./simple_test.hpp" #include "./test_iterators.hpp" diff --git a/test/view/all.cpp b/test/view/all.cpp index 7e1681ada1..a5302d607c 100644 --- a/test/view/all.cpp +++ b/test/view/all.cpp @@ -41,5 +41,16 @@ int main() rrgi = view::all(stdref); rrgi = view::all(static_cast const &>(stdref)); + { + auto v = view::all(debug_input_view{rgi}); + CHECK(v.size() == size(rgi)); + CHECK(v.data_ == rgi); + auto v2 = view::all(view::all(view::all(std::move(v)))); + CONCEPT_ASSERT(Same()); + CHECK(!v.valid_); + CHECK(v2.size() == size(rgi)); + CHECK(v2.data_ == rgi); + } + return test_result(); } diff --git a/test/view/any_view.cpp b/test/view/any_view.cpp index 3d3570824e..34b91e4fae 100644 --- a/test/view/any_view.cpp +++ b/test/view/any_view.cpp @@ -19,6 +19,58 @@ #include "../simple_test.hpp" #include "../test_utils.hpp" +namespace +{ + template + struct can_convert_to : std::false_type {}; + template + struct can_convert_to(std::declval()))>> + : std::true_type {}; + + void test_polymorphic_downcast() + { + struct A { virtual ~A() = default; }; + struct B : A {}; + struct unrelated {}; + struct incomplete; + + CONCEPT_ASSERT(can_convert_to()); + CONCEPT_ASSERT(can_convert_to()); + CONCEPT_ASSERT(can_convert_to()); + CONCEPT_ASSERT(can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + + CONCEPT_ASSERT(can_convert_to()); + CONCEPT_ASSERT(can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + +#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 + CONCEPT_ASSERT(can_convert_to()); + CONCEPT_ASSERT(can_convert_to()); +#endif // old GCC dynamic_cast bug + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + CONCEPT_ASSERT(!can_convert_to()); + } +} // unnamed namespace + int main() { using namespace ranges; @@ -26,15 +78,12 @@ int main() { any_view ints = view::ints; - ::models(aux::copy(ints)); - ::models_not(aux::copy(ints)); - ::check_equal(ints | view::take(10), ten_ints); - ::check_equal(ints | view::take(10), ten_ints); + CONCEPT_ASSERT(InputView()); + CONCEPT_ASSERT(!ForwardView()); + ::check_equal(std::move(ints) | view::take(10), ten_ints); } { any_view ints2 = view::ints | view::take(10); - ::models(aux::copy(ints2)); - ::models_not(aux::copy(ints2)); ::check_equal(ints2, ten_ints); ::check_equal(ints2, ten_ints); } @@ -65,7 +114,8 @@ int main() ::check_equal(any_view{vec}, ten_ints); ::check_equal(any_view{ranges::detail::as_const(vec)}, ten_ints); - struct Int { + struct Int + { int i_; Int(int i) : i_{i} {} @@ -75,5 +125,14 @@ int main() ::check_equal(any_view{vec2}, ten_ints); } + { + auto v = any_view{debug_input_view{ + ten_ints.begin(), std::ptrdiff_t(ten_ints.size()) + }}; + ::check_equal(v, ten_ints); + } + + test_polymorphic_downcast(); + return test_result(); } diff --git a/test/view/bounded.cpp b/test/view/bounded.cpp index 1d7dd49d3a..033e31c9f8 100644 --- a/test/view/bounded.cpp +++ b/test/view/bounded.cpp @@ -105,5 +105,15 @@ int main() ::check_equal(rng2, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); } + { + int const data[] = {1,2,3,4}; + auto rng = debug_input_view{data} | view::bounded; + using Rng = decltype(rng); + CONCEPT_ASSERT(InputView()); + CONCEPT_ASSERT(!ForwardRange()); + CONCEPT_ASSERT(BoundedRange()); + ::check_equal(rng, {1,2,3,4}); + } + return ::test_result(); } diff --git a/test/view/chunk.cpp b/test/view/chunk.cpp index f992540426..577aecd48d 100644 --- a/test/view/chunk.cpp +++ b/test/view/chunk.cpp @@ -34,16 +34,10 @@ namespace int const ints[] = {0,1,2,3,4}; constexpr auto N = ranges::size(ints); constexpr auto K = 2; - auto base = [&] - { - auto first = input_iterator(ranges::begin(ints)); - auto last = input_iterator(ranges::end(ints)); - return make_iterator_range(first, last, N); - }(); - auto make_range = [&]{ return view::chunk(decltype(base)(base), +K); }; + auto make_range = [&]{ return debug_input_view{ints} | view::chunk(K); }; auto rng = make_range(); using Rng = decltype(rng); - CONCEPT_ASSERT(InputRange()); + CONCEPT_ASSERT(InputView()); CONCEPT_ASSERT(!ForwardRange()); CONCEPT_ASSERT(SizedRange()); CHECK(ranges::size(rng) == (N + K - 1) / K); diff --git a/test/view/concat.cpp b/test/view/concat.cpp index 3dc86568fb..359d97a1dd 100644 --- a/test/view/concat.cpp +++ b/test/view/concat.cpp @@ -94,5 +94,12 @@ int main() CHECK(ranges::distance(ranges::begin(rng), ranges::end(rng)) == 30); } + { + int const data[] = {0,1,2,3}; + auto dv = [&]{ return debug_input_view{data}; }; + auto rng = view::concat(dv(), dv(), dv()); + ::check_equal(rng, {0,1,2,3,0,1,2,3,0,1,2,3}); + } + return test_result(); } diff --git a/test/view/const.cpp b/test/view/const.cpp index 2b9f9efa56..79e341d08a 100644 --- a/test/view/const.cpp +++ b/test/view/const.cpp @@ -77,5 +77,11 @@ int main() CHECK(&(*begin(rng4)).first == &rgi[0]); CHECK(rng4.size() == 4u); + { + auto rng = debug_input_view{rgi} | view::const_; + CONCEPT_ASSERT(Same>()); + ::check_equal(rng, rgi); + } + return test_result(); } diff --git a/test/view/delimit.cpp b/test/view/delimit.cpp index 57f0fae02d..8a7e35e7db 100644 --- a/test/view/delimit.cpp +++ b/test/view/delimit.cpp @@ -36,5 +36,11 @@ int main() auto rng2 = view::delimit(vi.begin(), 8); ::check_equal(rng2, {0, 1, 2, 3, 4, 5, 6, 7}); + { + int const some_ints[] = {1,2,3,0,4,5,6}; + auto rng = debug_input_view{some_ints} | view::delimit(0); + ::check_equal(rng, {1,2,3}); + } + return test_result(); } diff --git a/test/view/drop.cpp b/test/view/drop.cpp index db123a70d9..934c7a03ac 100644 --- a/test/view/drop.cpp +++ b/test/view/drop.cpp @@ -109,5 +109,16 @@ int main() ::check_equal(skipped[7], {8}); } } + + { + static int const some_ints[] = {0,1,2,3}; + auto rng = debug_input_view{some_ints} | view::drop(2); + using R = decltype(rng); + CONCEPT_ASSERT(InputView()); + CONCEPT_ASSERT(!ForwardRange()); + CONCEPT_ASSERT(Same>()); + ::check_equal(rng, {2,3}); + } + return test_result(); } diff --git a/test/view/drop_exactly.cpp b/test/view/drop_exactly.cpp index ad187df090..ab0fe37864 100644 --- a/test/view/drop_exactly.cpp +++ b/test/view/drop_exactly.cpp @@ -86,5 +86,13 @@ int main() (void)odds; } + { + auto rng = debug_input_view{rgi} | view::drop_exactly(5); + using Rng = decltype(rng); + CONCEPT_ASSERT(InputView()); + CONCEPT_ASSERT(SizedRange()); + ::check_equal(rng, {5,6,7,8,9,10}); + } + return test_result(); } diff --git a/test/view/drop_while.cpp b/test/view/drop_while.cpp index e202340644..d3cc7ed7ca 100644 --- a/test/view/drop_while.cpp +++ b/test/view/drop_while.cpp @@ -21,8 +21,9 @@ int main() { using namespace ranges; + auto rng0 = view::iota(10) | view::drop_while([](int i) { return i < 25; }); - static_assert(range_cardinality::value == unknown, ""); + CONCEPT_ASSERT(range_cardinality::value == unknown); ::models(aux::copy(rng0)); ::models_not(aux::copy(rng0)); ::models(rng0.begin()); @@ -33,19 +34,30 @@ int main() std::list vi{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; auto rng1 = vi | view::drop_while([](int i) { return i != 50; }); - static_assert(range_cardinality::value == ranges::finite, ""); + CONCEPT_ASSERT(range_cardinality::value == ranges::finite); ::models(aux::copy(rng1)); ::models(aux::copy(rng1)); ::models(rng1.begin()); CHECK(rng1.begin() == rng1.end()); // Check with a mutable predicate - int rgi[] = {0,1,2,3,4,5,6,7,8,9}; + static int const rgi[] = {0,1,2,3,4,5,6,7,8,9}; int cnt = 0; auto mutable_only = view::drop_while(rgi, [cnt](int) mutable { return ++cnt <= 5;}); ::check_equal(mutable_only, {5,6,7,8,9}); CONCEPT_ASSERT(View()); CONCEPT_ASSERT(!View()); + { + // Check with move-only subview + auto rng = debug_input_view{rgi} | view::drop_while([](int i){ return i < 4; }); + using R = decltype(rng); + CONCEPT_ASSERT(InputView()); + CONCEPT_ASSERT(!ForwardRange()); + CONCEPT_ASSERT(!BoundedRange()); + CONCEPT_ASSERT(Same>()); + ::check_equal(rng, {4,5,6,7,8,9}); + } + return test_result(); } diff --git a/test/view/generate.cpp b/test/view/generate.cpp index cfa7c17b2f..e96ecea4f9 100644 --- a/test/view/generate.cpp +++ b/test/view/generate.cpp @@ -11,12 +11,24 @@ #include #include -#include +#include #include "../simple_test.hpp" #include "../test_utils.hpp" namespace view = ranges::view; + +struct MoveOnlyFunction +{ + MoveOnlyString str_; + int i_; + + char operator()() + { + return str_.sz_[i_++]; + } +}; + int main() { // Test for constant generator functions @@ -24,7 +36,7 @@ int main() int i = 0, j = 1; auto fib = view::generate([&]()->int{int tmp = i; i += j; std::swap(i, j); return tmp;}); CONCEPT_ASSERT(ranges::InputView()); - check_equal(fib | view::take(10), {0,1,1,2,3,5,8,13,21,34}); + check_equal(fib | view::take_exactly(10), {0,1,1,2,3,5,8,13,21,34}); } // Test for mutable-only generator functions @@ -32,11 +44,18 @@ int main() int i = 0, j = 1; auto fib = view::generate([=]()mutable->int{int tmp = i; i += j; std::swap(i, j); return tmp;}); CONCEPT_ASSERT(ranges::InputView()); - check_equal(fib | view::take(10), {0,1,1,2,3,5,8,13,21,34}); + check_equal(fib | view::take_exactly(10), {0,1,1,2,3,5,8,13,21,34}); // The generator cannot be called when it's const-qualified, so "fib const" // does not model View. CONCEPT_ASSERT(!ranges::View()); } + // Test for move-only generator functions + { + auto rng = view::generate(MoveOnlyFunction{"Hello, world!", 0}) | view::take_exactly(5); + CONCEPT_ASSERT(ranges::InputView()); + check_equal(rng, {'H', 'e', 'l', 'l', 'o'}); + } + return test_result(); } diff --git a/test/view/generate_n.cpp b/test/view/generate_n.cpp index 07f26fb5e7..1096aa50eb 100644 --- a/test/view/generate_n.cpp +++ b/test/view/generate_n.cpp @@ -16,6 +16,17 @@ namespace view = ranges::view; +struct MoveOnlyFunction +{ + MoveOnlyString str_; + int i_; + + char operator()() + { + return str_.sz_[i_++]; + } +}; + int main() { // Test for constant generator functions @@ -37,5 +48,12 @@ int main() CONCEPT_ASSERT(!ranges::View()); } + // Test for move-only generator functions + { + auto rng = view::generate_n(MoveOnlyFunction{"Hello, world!", 0}, 5); + CONCEPT_ASSERT(ranges::InputView()); + check_equal(rng, {'H', 'e', 'l', 'l', 'o'}); + } + return test_result(); } diff --git a/test/view/indirect.cpp b/test/view/indirect.cpp index 3d989b8ecf..a15e704806 100644 --- a/test/view/indirect.cpp +++ b/test/view/indirect.cpp @@ -26,5 +26,19 @@ int main() CHECK(&*begin(rng) == vp[0].get()); ::check_equal(rng, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + { + int const some_ints[] = {0,1,2,3}; + int const *some_int_pointers[] = { + some_ints + 0, some_ints + 1, some_ints + 2, some_ints + 3 + }; + auto make_range = [&]{ + return debug_input_view{some_int_pointers} | view::indirect; + }; + auto rng = make_range(); + ::check_equal(rng, some_ints); + rng = make_range(); + CHECK(&*begin(rng) == some_ints + 0); + } + return test_result(); } diff --git a/test/view/intersperse.cpp b/test/view/intersperse.cpp index 97dd55937f..97f6128232 100644 --- a/test/view/intersperse.cpp +++ b/test/view/intersperse.cpp @@ -148,5 +148,11 @@ int main() check_equal(r0, {1,42,2,42,3,42,4,42,5}); } + { + int const some_ints[] = {1,2,3,4,5}; + auto rng = debug_input_view{some_ints} | view::intersperse(42); + check_equal(rng, {1,42,2,42,3,42,4,42,5}); + } + return test_result(); } diff --git a/test/view/join.cpp b/test/view/join.cpp index cb1b28a098..1f30a2d199 100644 --- a/test/view/join.cpp +++ b/test/view/join.cpp @@ -142,5 +142,11 @@ int main() models_not(some_strings | view::join); } + { + int const some_int_pairs[3][2] = {{0,1},{2,3},{4,5}}; + auto rng = debug_input_view{some_int_pairs} | view::join; + check_equal(rng, {0,1,2,3,4,5}); + } + return ::test_result(); } diff --git a/test/view/keys_value.cpp b/test/view/keys_value.cpp index 4b783acba5..5869468ba9 100644 --- a/test/view/keys_value.cpp +++ b/test/view/keys_value.cpp @@ -58,5 +58,13 @@ int main() ::check_equal(view::keys(exs), {0, 1, 2}); } + { + std::pair const data[] = {{0, 2}, {1, 1}, {2, 0}}; + auto key_range = debug_input_view const>{data} | view::keys; + check_equal(key_range, {0,1,2}); + auto value_range = debug_input_view const>{data} | view::values; + check_equal(value_range, {2,1,0}); + } + return test_result(); } diff --git a/test/view/move.cpp b/test/view/move.cpp index 122e4ef726..d3025594ef 100644 --- a/test/view/move.cpp +++ b/test/view/move.cpp @@ -23,10 +23,8 @@ int main() { using namespace ranges; - std::vector vs; - vs.emplace_back("'allo"); - vs.emplace_back("'allo"); - vs.emplace_back("???"); + static const char * const data[] = {"'allo", "'allo", "???"}; + std::vector vs(begin(data), end(data)); auto x = vs | view::move; CONCEPT_ASSERT(Same, concepts::BoundedView>()); @@ -45,5 +43,14 @@ int main() ::check_equal(vs2, {"'allo", "'allo", "???"}); ::check_equal(vs, {"", "", ""}); + { + MoveOnlyString data[] = {"can", "you", "hear", "me", "now?"}; + auto rng = debug_input_view{data} | view::move; + MoveOnlyString target[sizeof(data) / sizeof(data[0])]; + copy(rng, target); + ::check_equal(data, {"", "", "", "", ""}); + ::check_equal(target, {"can", "you", "hear", "me", "now?"}); + } + return test_result(); } diff --git a/test/view/partial_sum.cpp b/test/view/partial_sum.cpp index 9023d543d6..2edf178fa1 100644 --- a/test/view/partial_sum.cpp +++ b/test/view/partial_sum.cpp @@ -19,14 +19,6 @@ #include "../simple_test.hpp" #include "../test_utils.hpp" -struct is_odd -{ - bool operator()(int i) const - { - return (i % 2) == 1; - } -}; - int main() { using namespace ranges; @@ -59,5 +51,10 @@ int main() CONCEPT_ASSERT(View()); CONCEPT_ASSERT(!View()); + { + auto rng = debug_input_view{rgi} | view::partial_sum(); + ::check_equal(rng, {1, 3, 6, 10, 15, 21, 28, 36, 45, 55}); + } + return test_result(); } diff --git a/test/view/remove_if.cpp b/test/view/remove_if.cpp index ee08813a44..eb5fbd87f3 100644 --- a/test/view/remove_if.cpp +++ b/test/view/remove_if.cpp @@ -93,5 +93,10 @@ int main() ::check_equal(r2, {1,5}); } + { + auto rng = debug_input_view{rgi} | view::remove_if(is_even{}); + ::check_equal(rng, {1,3,5,7,9}); + } + return test_result(); } diff --git a/test/view/replace.cpp b/test/view/replace.cpp index 424345d7a1..8a0e215f44 100644 --- a/test/view/replace.cpp +++ b/test/view/replace.cpp @@ -80,5 +80,11 @@ int main() models(begin(rng4)); ::check_equal(rng4, {0,1,2,3,4,42,6,7,8,9}); + { + int const some_ints[] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9}; + auto rng = debug_input_view{some_ints} | view::replace(1, 42); + ::check_equal(rng, {42,2,3,4,5,6,7,8,9,42,2,3,4,5,6,7,8,9,42,2,3,4,5,6,7,8,9}); + } + return test_result(); } diff --git a/test/view/replace_if.cpp b/test/view/replace_if.cpp index 8db7cf396d..15d0e3a93a 100644 --- a/test/view/replace_if.cpp +++ b/test/view/replace_if.cpp @@ -94,5 +94,13 @@ int main() CONCEPT_ASSERT(View()); CONCEPT_ASSERT(!View()); + { + int const some_ints[] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9}; + + auto rng = debug_input_view{some_ints} | + view::replace_if([](int i){ return i == 1; }, 42); + ::check_equal(rng, {42,2,3,4,5,6,7,8,9,42,2,3,4,5,6,7,8,9,42,2,3,4,5,6,7,8,9}); + } + return test_result(); } diff --git a/test/view/sample.cpp b/test/view/sample.cpp index 379ad673bb..1c2a15b1aa 100644 --- a/test/view/sample.cpp +++ b/test/view/sample.cpp @@ -10,15 +10,17 @@ using namespace ranges; int main () { + std::mt19937 engine; + std::vector pop(100); std::iota(std::begin(pop), std::end(pop), 0); { constexpr std::size_t N = 32; std::array tmp; - std::mt19937 engine; auto rng = pop | view::sample(N, engine); - CONCEPT_ASSERT(InputRange()); - CONCEPT_ASSERT(View()); + using Rng = decltype(rng); + CONCEPT_ASSERT(InputView()); + CONCEPT_ASSERT(!ForwardRange()); ranges::copy(rng, tmp.begin()); rng = pop | view::sample(N, engine); CHECK(!ranges::equal(rng, tmp)); @@ -27,5 +29,14 @@ int main () CHECK(ranges::equal(rng, tmp)); } + { + int const some_ints[] = {0,1,2,3,4,5,6,7,8}; + auto rng = debug_input_view{some_ints} | view::sample(4, engine); + using Rng = decltype(rng); + CONCEPT_ASSERT(InputView()); + CONCEPT_ASSERT(!ForwardRange()); + CHECK(ranges::distance(rng) == 4); + } + return ::test_result(); } diff --git a/test/view/set_difference.cpp b/test/view/set_difference.cpp index 3e4fd3265d..aa6095fb92 100644 --- a/test/view/set_difference.cpp +++ b/test/view/set_difference.cpp @@ -32,8 +32,6 @@ #include "../simple_test.hpp" #include "../test_utils.hpp" - - int main() { using namespace ranges; @@ -47,7 +45,6 @@ int main() return x * x; }); - // difference between two finite ranges/sets { auto res = view::set_difference(i1_finite, i2_finite); @@ -55,7 +52,7 @@ int main() models(aux::copy(res)); models_not(aux::copy(res)); models_not(aux::copy(res)); - + using R = decltype(res); CONCEPT_ASSERT(Same, int>()); @@ -65,16 +62,15 @@ int main() static_assert(range_cardinality::value == ranges::finite, "Cardinality of difference between two finite ranges should be finite!"); ::check_equal(res, {1, 2, 3, 3, 3, 4, 4}); - + // check if the final result agrees with the greedy algorithm std::vector diff; set_difference(i1_finite, i2_finite, back_inserter(diff)); ::check_equal(res, diff); - + CHECK(&*begin(res) == &*(begin(i1_finite))); } - // difference between two infinite ranges { auto res = view::set_difference(i1_infinite, i2_infinite); @@ -82,7 +78,7 @@ int main() models(aux::copy(res)); models_not(aux::copy(res)); models_not(aux::copy(res)); - + using R = decltype(res); CONCEPT_ASSERT(Same, int>()); @@ -100,7 +96,6 @@ int main() } - // difference between a finite and an infinite range { auto res = view::set_difference(i1_finite, i2_infinite); @@ -108,7 +103,7 @@ int main() models(aux::copy(res)); models_not(aux::copy(res)); models_not(aux::copy(res)); - + using R = decltype(res); CONCEPT_ASSERT(Same, int>()); @@ -127,7 +122,7 @@ int main() models(aux::copy(res)); models_not(aux::copy(res)); models_not(aux::copy(res)); - + using R = decltype(res); CONCEPT_ASSERT(Same, int>()); @@ -139,7 +134,6 @@ int main() ::check_equal(res | view::take(5), {0, 3, 12, 15, 18}); } - // differences involving unknown cardinalities { auto rng0 = view::iota(10) | view::drop_while([](int i) @@ -150,18 +144,17 @@ int main() auto res1 = view::set_difference(i2_finite, rng0); static_assert(range_cardinality::value == ranges::finite, "Difference between a finite and unknown cardinality set should have finite cardinality!"); - + auto res2 = view::set_difference(rng0, i2_finite); static_assert(range_cardinality::value == ranges::unknown, "Difference between an unknown cardinality and finite set should have unknown cardinality!"); - + auto res3 = view::set_difference(i1_infinite, rng0); static_assert(range_cardinality::value == ranges::unknown, "Difference between an unknown cardinality and finite set should have unknown cardinality!"); - + auto res4 = view::set_difference(rng0, i1_infinite); static_assert(range_cardinality::value == ranges::unknown, "Difference between an unknown and infinite cardinality set should have unknown cardinality!"); - - } + } // test const ranges { @@ -170,7 +163,7 @@ int main() CONCEPT_ASSERT(Same, int>()); CONCEPT_ASSERT(Same, const int&>()); CONCEPT_ASSERT(Same, const int&&> ()); - + auto res2 = view::set_difference(view::const_(i1_finite), i2_finite); using R2 = decltype(res2); CONCEPT_ASSERT(Same, int>()); @@ -178,7 +171,6 @@ int main() CONCEPT_ASSERT(Same, const int&&> ()); } - // test different orderings { auto res = view::set_difference(view::reverse(i1_finite), view::reverse(i2_finite), [](int a, int b) @@ -188,7 +180,6 @@ int main() ::check_equal(res, {4, 4, 3, 3, 3, 2, 1}); } - // test projections and sets with different element types struct S { @@ -200,7 +191,7 @@ int main() }; S s_finite[] = {S{-20}, S{-10}, S{1}, S{3}, S{3}, S{6}, S{8}, S{20}}; - + { auto res1 = view::set_difference(s_finite, view::ints(-2, 10), ordered_less(), @@ -213,7 +204,6 @@ int main() CONCEPT_ASSERT(Same, S&&> ()); ::check_equal(res1, {S{-20}, S{-10}, S{3}, S{20}}); - auto res2 = view::set_difference(view::ints(-2, 10), s_finite, ordered_less(), ident(), @@ -225,8 +215,7 @@ int main() CONCEPT_ASSERT(Same, int> ()); ::check_equal(res2, {-2, -1, 0, 2, 4, 5, 7, 9}); } - - + // move { auto v0 = to_>({"a","b","b","c","x","x"}); @@ -240,7 +229,6 @@ int main() ::check_equal(v1, {"b","x","y","z"}); ::check_equal(v0, {"","b","","","x",""}); - auto v0_greedy = to_>({"a","b","b","c","x","x"}); auto v1_greedy = to_>({"b","x","y","z"}); std::vector expected_greedy; @@ -251,19 +239,26 @@ int main() ::check_equal(v0_greedy, v0); ::check_equal(v1_greedy, v1); - using R = decltype(res); CONCEPT_ASSERT(Same, MoveOnlyString>()); CONCEPT_ASSERT(Same, MoveOnlyString &>()); CONCEPT_ASSERT(Same, MoveOnlyString &&>()); } - + // WARNING: set_difference between two infinite ranges can create infinite loops! // { // auto empty_range = view::set_difference(view::ints, view::ints); // begin(empty_range); // infinite loop! // } + { + auto rng = view::set_difference( + debug_input_view{i1_finite}, + debug_input_view{i2_finite} + ); + ::check_equal(rng, {1, 2, 3, 3, 3, 4, 4}); + } + return test_result(); } diff --git a/test/view/set_intersection.cpp b/test/view/set_intersection.cpp index cd377271c1..ed6333168e 100644 --- a/test/view/set_intersection.cpp +++ b/test/view/set_intersection.cpp @@ -31,8 +31,6 @@ #include "../simple_test.hpp" #include "../test_utils.hpp" - - int main() { using namespace ranges; @@ -46,17 +44,16 @@ int main() return x * x; }); - // intersection of two finite ranges { auto res = view::set_intersection(i1_finite, i2_finite); - + models(aux::copy(res)); models_not(aux::copy(res)); models_not(aux::copy(res)); using R = decltype(res); - + CONCEPT_ASSERT(Same, int>()); CONCEPT_ASSERT(Same, int&>()); CONCEPT_ASSERT(Same ()); @@ -64,11 +61,10 @@ int main() static_assert(range_cardinality::value == ranges::finite, "Cardinality of intersection with a finite range should be finite!"); ::check_equal(res, {2, 4, 4}); - + CHECK(&*begin(res) == &*(begin(i1_finite) + 1)); } - // intersection of two infinite ranges { auto res = view::set_intersection(i1_infinite, i2_infinite); @@ -78,7 +74,7 @@ int main() models_not(aux::copy(res)); using R = decltype(res); - + CONCEPT_ASSERT(Same, int>()); CONCEPT_ASSERT(Same, range_reference_t>()); CONCEPT_ASSERT(Same>()); @@ -88,7 +84,6 @@ int main() ::check_equal(res | view::take(5), {0, 9, 36, 81, 144}); } - // intersection of a finite and infinite range { auto res = view::set_intersection(i1_finite, i2_infinite); @@ -96,7 +91,7 @@ int main() models(aux::copy(res)); models_not(aux::copy(res)); models_not(aux::copy(res)); - + using R = decltype(res); CONCEPT_ASSERT(Same, int>()); @@ -107,14 +102,12 @@ int main() ::check_equal(res | view::take(500), {1, 4}); - - auto res2 = view::set_intersection(i1_infinite, i2_finite); models(aux::copy(res2)); models_not(aux::copy(res2)); models_not(aux::copy(res2)); - + using R2 = decltype(res2); CONCEPT_ASSERT(Same, int>()); @@ -126,7 +119,6 @@ int main() ::check_equal(res2 | view::take(500), {6, 9}); } - // intersection of a set of unknown cardinality { auto rng0 = view::iota(10) | view::drop_while([](int i) @@ -139,7 +131,6 @@ int main() static_assert(range_cardinality::value == ranges::unknown, "Intersection with a set of unknown cardinality should have unknown cardinality!"); } - // test const ranges { auto res1 = view::set_intersection(view::const_(i1_finite), view::const_(i2_finite)); @@ -147,7 +138,7 @@ int main() CONCEPT_ASSERT(Same, int>()); CONCEPT_ASSERT(Same, const int&>()); CONCEPT_ASSERT(Same, const int&&> ()); - + auto res2 = view::set_intersection(view::const_(i1_finite), i2_finite); using R2 = decltype(res2); CONCEPT_ASSERT(Same, int>()); @@ -155,7 +146,6 @@ int main() CONCEPT_ASSERT(Same, const int&&> ()); } - // test different orderings { auto res = view::set_intersection(view::reverse(i1_finite), view::reverse(i2_finite), [](int a, int b) @@ -165,7 +155,6 @@ int main() ::check_equal(res, {4, 4, 2}); } - // test projections and sets with different element types struct S { @@ -177,7 +166,7 @@ int main() }; S s_finite[] = {S{-20}, S{-10}, S{1}, S{3}, S{3}, S{6}, S{8}, S{20}}; - + { auto res1 = view::set_intersection(s_finite, view::ints(-2, 10), ordered_less(), @@ -190,7 +179,6 @@ int main() CONCEPT_ASSERT(Same, S&&> ()); ::check_equal(res1, {S{1}, S{3}, S{6}, S{8}}); - auto res2 = view::set_intersection(view::ints(-2, 10), s_finite, ordered_less(), ident(), @@ -203,7 +191,6 @@ int main() ::check_equal(res2, {1, 3, 6, 8}); } - // move { auto v0 = to_>({"a","b","b","c","x","x"}); @@ -216,7 +203,7 @@ int main() ::check_equal(expected, {"b","x"}); ::check_equal(v0, {"a","","b","c","","x"}); ::check_equal(v1, {"b","x","y","z"}); - + using R = decltype(res); CONCEPT_ASSERT(Same, MoveOnlyString>()); @@ -224,6 +211,13 @@ int main() CONCEPT_ASSERT(Same, MoveOnlyString &&>()); } + { + auto rng = view::set_intersection( + debug_input_view{i1_finite}, + debug_input_view{i2_finite} + ); + ::check_equal(rng, {2, 4, 4}); + } return test_result(); } diff --git a/test/view/set_symmetric_difference.cpp b/test/view/set_symmetric_difference.cpp index 2f24fae8cd..15c510b87c 100644 --- a/test/view/set_symmetric_difference.cpp +++ b/test/view/set_symmetric_difference.cpp @@ -47,7 +47,6 @@ int main() return x * x; }); - // symmetric difference between two finite ranges/sets { auto res = view::set_symmetric_difference(i1_finite, i2_finite); @@ -55,7 +54,7 @@ int main() models(aux::copy(res)); models_not(aux::copy(res)); models_not(aux::copy(res)); - + using R = decltype(res); CONCEPT_ASSERT(Same, int>()); @@ -65,19 +64,18 @@ int main() static_assert(range_cardinality::value == ranges::finite, "Cardinality of symmetric difference of finite ranges should be finite!"); ::check_equal(res, {-3, 1, 2, 3, 3, 3, 4, 4, 6, 9}); - + // check if the final result agrees with the greedy algorithm std::vector greedy_sd; set_symmetric_difference(i1_finite, i2_finite, back_inserter(greedy_sd)); ::check_equal(res, greedy_sd); - + auto it = begin(res); CHECK(&*it == &*(begin(i2_finite))); ++it; CHECK(&*it == &*(begin(i1_finite))); } - // symmetric difference between two infinite ranges { auto res = view::set_symmetric_difference(i1_infinite, i2_infinite); @@ -85,7 +83,7 @@ int main() models(aux::copy(res)); models_not(aux::copy(res)); models_not(aux::copy(res)); - + using R = decltype(res); CONCEPT_ASSERT(Same, @@ -110,7 +108,6 @@ int main() ::check_equal(res | view::take(6), greedy_sd | view::take(6)); } - // symmetric difference between a finite and an infinite range { auto res1 = view::set_symmetric_difference(i1_finite, i2_infinite); @@ -118,7 +115,7 @@ int main() models(aux::copy(res1)); models_not(aux::copy(res1)); models_not(aux::copy(res1)); - + using R1 = decltype(res1); CONCEPT_ASSERT(Same, int>()); @@ -128,15 +125,14 @@ int main() static_assert(range_cardinality::value == ranges::infinite, "Cardinality of symmetric difference between a finite and an infinite range should be infinite!"); ::check_equal(res1 | view::take(10), {0, 2, 2, 3, 3, 3, 4, 4, 4, 9}); - - + // now swap the operands: auto res2 = view::set_symmetric_difference(i2_infinite, i1_finite); models(aux::copy(res2)); models_not(aux::copy(res2)); models_not(aux::copy(res2)); - + using R2 = decltype(res2); CONCEPT_ASSERT(Same, int>()); @@ -148,7 +144,6 @@ int main() ::check_equal(res1 | view::take(10), res2 | view::take(10)); } - // symmetric differences involving unknown cardinalities { auto rng0 = view::iota(10) | view::drop_while([](int i) @@ -159,21 +154,20 @@ int main() auto res1 = view::set_symmetric_difference(i2_finite, rng0); static_assert(range_cardinality::value == ranges::unknown, "Symmetric difference between a finite and unknown cardinality set should have unknown cardinality!"); - + auto res2 = view::set_symmetric_difference(rng0, i2_finite); static_assert(range_cardinality::value == ranges::unknown, "Symmetric difference between an unknown and finite cardinality set should have unknown cardinality!"); - + auto res3 = view::set_symmetric_difference(i1_infinite, rng0); static_assert(range_cardinality::value == ranges::unknown, "Symmetric difference between an infinite and unknown cardinality set should have unknown cardinality!"); - + auto res4 = view::set_symmetric_difference(rng0, i1_infinite); static_assert(range_cardinality::value == ranges::unknown, "Symmetric difference between an unknown and infinite cardinality set should have infinite cardinality!"); - + auto res5 = view::set_symmetric_difference(rng0, rng0); static_assert(range_cardinality::value == ranges::unknown, "Symmetric difference between two unknown cardinality sets should have unknown cardinality!"); } - // test const ranges { auto res1 = view::set_symmetric_difference(view::const_(i1_finite), view::const_(i2_finite)); @@ -181,7 +175,7 @@ int main() CONCEPT_ASSERT(Same, int>()); CONCEPT_ASSERT(Same, const int&>()); CONCEPT_ASSERT(Same, const int&&>()); - + auto res2 = view::set_symmetric_difference(view::const_(i1_finite), i2_finite); using R2 = decltype(res2); CONCEPT_ASSERT(Same, int>()); @@ -189,7 +183,6 @@ int main() CONCEPT_ASSERT(Same, const int&&>()); } - // test different orderings { auto res = view::set_symmetric_difference(view::reverse(i1_finite), view::reverse(i2_finite), [](int a, int b) @@ -200,7 +193,6 @@ int main() CHECK(&*begin(res) == &*(begin(i2_finite) + 5)); } - struct B { int val; @@ -210,18 +202,18 @@ int main() return val == other.val; } }; - + struct D: public B { D(int i): B{i} {} D(B b): B{std::move(b)} {} }; - + B b_finite[] = {B{-20}, B{-10}, B{1}, B{3}, B{3}, B{6}, B{8}, B{20}}; D d_finite[] = {D{0}, D{2}, D{4}, D{6}}; - + // sets with different element types, custom orderings - { + { auto res = view::set_symmetric_difference(b_finite, d_finite, [](const B& a, const D& b){ return a.val < b.val; }); CONCEPT_ASSERT(Same, B>()); CONCEPT_ASSERT(Same, B&>()); @@ -232,9 +224,9 @@ int main() advance(it, 2); CHECK(&*it == &*begin(d_finite)); } - + // projections - { + { auto res1 = view::set_symmetric_difference(b_finite, d_finite, ordered_less(), &B::val, @@ -255,8 +247,7 @@ int main() CONCEPT_ASSERT(Same, B>()); ::check_equal(res2, {B{-20}, B{-10}, B{-2}, B{-1}, B{0}, B{2}, B{3}, B{4}, B{5}, B{7}, B{9}, B{20}}); } - - + // move { auto v0 = to_>({"a","b","b","c","x","x"}); @@ -269,8 +260,7 @@ int main() ::check_equal(expected, {"a","b","c","x","y","z"}); ::check_equal(v1, {"b","x","",""}); ::check_equal(v0, {"","b","","","x",""}); - - + auto v0_greedy = to_>({"a","b","b","c","x","x"}); auto v1_greedy = to_>({"b","x","y","z"}); std::vector expected_greedy; @@ -281,7 +271,6 @@ int main() ::check_equal(v0_greedy, v0); ::check_equal(v1_greedy, v1); - using R = decltype(res); CONCEPT_ASSERT(Same, MoveOnlyString>()); @@ -289,35 +278,41 @@ int main() CONCEPT_ASSERT(Same, MoveOnlyString &&>()); } - // WARNING: set_symmetric_difference between two infinite ranges can create infinite loops! // { // auto empty_range = view::set_symmetric_difference(view::ints, view::ints); // begin(empty_range); // infinite loop! // } - + // iterator (in)equality { int r1[] = {1, 2, 3}; int r2[] = { 2, 3, 4, 5}; auto res = view::set_symmetric_difference(r1, r2); // 1, 4, 5 - + auto it1 = ranges::next(res.begin()); // *it1 == 4, member iterator into r1 points to r1.end() auto it2 = ranges::next(it1); // *it2 == 5, member iterator into r1 also points to r1.end() auto sentinel = res.end(); - + CHECK(*it1 == 4); CHECK(*it2 == 5); - + CHECK(it1 != it2); // should be different even though member iterators into r1 are the same - + CHECK(it1 != sentinel); CHECK(ranges::next(it1, 2) == sentinel); - + CHECK(it2 != sentinel); CHECK(ranges::next(it2, 1) == sentinel); } - - + + { + auto rng = view::set_symmetric_difference( + debug_input_view{i1_finite}, + debug_input_view{i2_finite} + ); + ::check_equal(rng, {-3, 1, 2, 3, 3, 3, 4, 4, 6, 9}); + } + return test_result(); } diff --git a/test/view/set_union.cpp b/test/view/set_union.cpp index 8296ce6f13..a280374453 100644 --- a/test/view/set_union.cpp +++ b/test/view/set_union.cpp @@ -45,14 +45,12 @@ int main() { return x * x; }); - - + // simple identity check { ::check_equal(view::set_union(i1_infinite, i1_infinite) | view::take(100), i1_infinite | view::take(100)); } - // union of two finite ranges/sets { auto res = view::set_union(i1_finite, i2_finite); @@ -60,7 +58,7 @@ int main() models(aux::copy(res)); models_not(aux::copy(res)); models_not(aux::copy(res)); - + using R = decltype(res); CONCEPT_ASSERT(Same, int>()); @@ -70,19 +68,18 @@ int main() static_assert(range_cardinality::value == ranges::finite, "Cardinality of union of finite ranges should be finite!"); ::check_equal(res, {-3, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 6, 9}); - + // check if the final result agrees with the greedy algorithm std::vector greedy_union; set_union(i1_finite, i2_finite, back_inserter(greedy_union)); ::check_equal(res, greedy_union); - + auto it = begin(res); CHECK(&*it == &*(begin(i2_finite))); ++it; CHECK(&*it == &*(begin(i1_finite))); } - // union of two infinite ranges { auto res = view::set_union(i1_infinite, i2_infinite); @@ -90,7 +87,7 @@ int main() models(aux::copy(res)); models_not(aux::copy(res)); models_not(aux::copy(res)); - + using R = decltype(res); CONCEPT_ASSERT(Same, @@ -115,7 +112,6 @@ int main() ::check_equal(res | view::take(6), greedy_union | view::take(6)); } - // union of a finite and an infinite range { auto res = view::set_union(i1_finite, i2_infinite); @@ -123,7 +119,7 @@ int main() models(aux::copy(res)); models_not(aux::copy(res)); models_not(aux::copy(res)); - + using R = decltype(res); CONCEPT_ASSERT(Same, int>()); @@ -142,7 +138,7 @@ int main() models(aux::copy(res)); models_not(aux::copy(res)); models_not(aux::copy(res)); - + using R = decltype(res); CONCEPT_ASSERT(Same, int>()); @@ -154,7 +150,6 @@ int main() ::check_equal(res | view::take(7), {-3, 0, 2, 3, 4, 4, 6}); } - // unions involving unknown cardinalities { auto rng0 = view::iota(10) | view::drop_while([](int i) @@ -165,22 +160,21 @@ int main() auto res1 = view::set_union(i2_finite, rng0); static_assert(range_cardinality::value == ranges::unknown, "Union of a finite and unknown cardinality set should have unknown cardinality!"); - + auto res2 = view::set_union(rng0, i2_finite); static_assert(range_cardinality::value == ranges::unknown, "Union of an unknown and finite cardinality set should have unknown cardinality!"); - + auto res3 = view::set_union(i1_infinite, rng0); static_assert(range_cardinality::value == ranges::infinite, "Union of an infinite and unknown cardinality set should have infinite cardinality!"); - + auto res4 = view::set_union(rng0, i1_infinite); static_assert(range_cardinality::value == ranges::infinite, "Union of an unknown and infinite cardinality set should have infinite cardinality!"); - + auto res5 = view::set_union(rng0, rng0); static_assert(range_cardinality::value == ranges::unknown, "Union of two unknown cardinality sets should have unknown cardinality!"); ::check_equal(res5 | view::take(100), rng0 | view::take(100)); } - // test const ranges { auto res1 = view::set_union(view::const_(i1_finite), view::const_(i2_finite)); @@ -188,7 +182,7 @@ int main() CONCEPT_ASSERT(Same, int>()); CONCEPT_ASSERT(Same, const int&>()); CONCEPT_ASSERT(Same, const int&&>()); - + auto res2 = view::set_union(view::const_(i1_finite), i2_finite); using R2 = decltype(res2); CONCEPT_ASSERT(Same, int>()); @@ -196,7 +190,6 @@ int main() CONCEPT_ASSERT(Same, const int&&>()); } - // test different orderings { auto res = view::set_union(view::reverse(i1_finite), view::reverse(i2_finite), [](int a, int b) @@ -206,7 +199,6 @@ int main() ::check_equal(res, {9, 6, 4, 4, 4, 4, 3, 3, 3, 2, 2, 1, -3}); } - struct B { int val; @@ -216,18 +208,18 @@ int main() return val == other.val; } }; - + struct D: public B { D(int i): B{i} {} D(B b): B{std::move(b)} {} }; - + B b_finite[] = {B{-20}, B{-10}, B{1}, B{3}, B{3}, B{6}, B{8}, B{20}}; D d_finite[] = {D{0}, D{2}, D{4}, D{6}}; - + // sets with different element types, custom orderings - { + { auto res = view::set_union(b_finite, d_finite, [](const B& a, const D& b){ return a.val < b.val; }); using R = decltype(res); CONCEPT_ASSERT(Same, B>()); @@ -239,9 +231,9 @@ int main() advance(it, 2); CHECK(&*it == &*begin(d_finite)); } - + // projections - { + { auto res1 = view::set_union(b_finite, d_finite, ordered_less(), &B::val, @@ -253,7 +245,6 @@ int main() CONCEPT_ASSERT(Same, B&&>()); ::check_equal(res1, {B{-20}, B{-10}, B{0}, B{1}, B{2}, B{3}, B{3}, B{4}, B{6}, B{8}, B{20}}); - auto res2 = view::set_union(view::ints(-2, 10), b_finite, ordered_less(), ident(), @@ -266,49 +257,55 @@ int main() ::check_equal(res2, {B{-20}, B{-10}, B{-2}, B{-1}, B{0}, B{1}, B{2}, B{3}, B{3}, B{4}, B{5}, B{6}, B{7}, B{8}, B{9}, B{20}}); } - // move { auto v0 = to_>({"a","b","c","x"}); auto v1 = to_>({"b","x","y","z"}); auto res = view::set_union(v0, v1, [](const MoveOnlyString& a, const MoveOnlyString& b){return a expected; move(res, back_inserter(expected)); ::check_equal(expected, {"a","b","c","x","y","z"}); ::check_equal(v0, {"","","",""}); ::check_equal(v1, {"b","x","",""}); - + using R = decltype(res); CONCEPT_ASSERT(Same, MoveOnlyString>()); CONCEPT_ASSERT(Same, MoveOnlyString &>()); CONCEPT_ASSERT(Same, MoveOnlyString &&>()); } - + // iterator (in)equality { int r1[] = {1, 2, 3}; int r2[] = { 2, 3, 4, 5}; auto res = view::set_union(r1, r2); // 1, 2, 3, 4, 5 - + auto it1 = ranges::next(res.begin(), 3); // *it1 == 4, member iterator into r1 points to r1.end() auto it2 = ranges::next(it1); // *it2 == 5, member iterator into r1 also points to r1.end() auto sentinel = res.end(); - + CHECK(*it1 == 4); CHECK(*it2 == 5); - + CHECK(it1 != it2); // should be different even though member iterators into r1 are the same - + CHECK(it1 != sentinel); CHECK(ranges::next(it1, 2) == sentinel); - + CHECK(it2 != sentinel); CHECK(ranges::next(it2, 1) == sentinel); } - + + { + auto rng = view::set_union( + debug_input_view{i1_finite}, + debug_input_view{i2_finite} + ); + ::check_equal(rng, {-3, 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 6, 9}); + } return test_result(); } diff --git a/test/view/slice.cpp b/test/view/slice.cpp index 9c732f8ff2..a85755675a 100644 --- a/test/view/slice.cpp +++ b/test/view/slice.cpp @@ -123,5 +123,11 @@ int main() ::check_equal(letters[{2,end-2}], {'c','d','e'}); } + { + int const some_ints[] = {0,1,2,3,4,5,6,7,8,9}; + auto rng = debug_input_view{some_ints} | view::slice(3,10); + ::check_equal(rng, {3, 4, 5, 6, 7, 8, 9}); + } + return test_result(); } diff --git a/test/view/stride.cpp b/test/view/stride.cpp index 38bc135c06..4821369d76 100644 --- a/test/view/stride.cpp +++ b/test/view/stride.cpp @@ -80,5 +80,11 @@ int main() (void)ranges::view::stride(n); } + { + int const some_ints[] = {0,1,2,3,4,5,6,7}; + auto rng = debug_input_view{some_ints} | view::stride(2); + ::check_equal(rng, {0,2,4,6}); + } + return ::test_result(); } diff --git a/test/view/take.cpp b/test/view/take.cpp index d9a4c8d3e9..1775caf4ff 100644 --- a/test/view/take.cpp +++ b/test/view/take.cpp @@ -122,5 +122,10 @@ int main() ::models_not(aux::copy(rng9)); check_equal(rng9, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); + { + auto rng = debug_input_view{rgi} | view::take(6); + ::check_equal(rng, {0, 1, 2, 3, 4, 5}); + } + return test_result(); } diff --git a/test/view/take_exactly.cpp b/test/view/take_exactly.cpp index d66d2b88fa..7667598d69 100644 --- a/test/view/take_exactly.cpp +++ b/test/view/take_exactly.cpp @@ -72,5 +72,10 @@ int main() ::check_equal(rng5, {19, 18, 17, 16, 15, 14, 13, 12, 11, 10}); CHECK(size(rng5) == 10u); + { + auto rng = debug_input_view{rgi} | view::take_exactly(6); + ::check_equal(rng, {0, 1, 2, 3, 4, 5}); + } + return test_result(); } diff --git a/test/view/take_while.cpp b/test/view/take_while.cpp index 584cce1dd0..1018fc77ae 100644 --- a/test/view/take_while.cpp +++ b/test/view/take_while.cpp @@ -58,5 +58,12 @@ int main() ::check_equal(rng, {1,2,3,4}); } + { + auto rng = debug_input_view{rgi} | view::take_while([](int i) { + return i != 5; + }); + ::check_equal(rng, {0,1,2,3,4}); + } + return test_result(); } diff --git a/test/view/transform.cpp b/test/view/transform.cpp index d82890b20b..09f6468e67 100644 --- a/test/view/transform.cpp +++ b/test/view/transform.cpp @@ -154,5 +154,29 @@ int main() ::check_equal(rng, {T{"a","x"}, T{"b","y"}, T{"c","z"}}); } + // move-only input view transform + { + auto rng = debug_input_view{rgi} | view::transform(is_odd{}); + ::check_equal(rng, {true, false, true, false, true, false, true, false, true, false}); + } + + // two move-only input view transform + { + auto v0 = to_>({"a","b","c"}); + auto v1 = to_>({"x","y","z"}); + + auto r0 = debug_input_view{v0.data(), distance(v0)}; + auto r1 = debug_input_view{v1.data(), distance(v1)}; + auto rng = view::transform(std::move(r0), std::move(r1), + [](std::string &s0, std::string &s1){ return std::tie(s0, s1); }); + using R = decltype(rng); + CONCEPT_ASSERT(Same, std::tuple>()); + CONCEPT_ASSERT(Same, std::tuple>()); + CONCEPT_ASSERT(Same, std::tuple>()); + + using T = std::tuple; + ::check_equal(rng, {T{"a","x"}, T{"b","y"}, T{"c","z"}}); + } + return test_result(); } diff --git a/test/view/zip.cpp b/test/view/zip.cpp index b88017539a..1d0ccbe9f0 100644 --- a/test/view/zip.cpp +++ b/test/view/zip.cpp @@ -224,5 +224,16 @@ int main() ranges::distance(view::zip(view::ints(0), rng) | view::bounded); } + { + int const i1[] = {0,1,2,3}; + int const i2[] = {4,5,6,7}; + auto rng = view::zip( + debug_input_view{i1}, + debug_input_view{i2} + ); + using P = std::pair; + ::check_equal(rng, {P{0,4},P{1,5}, P{2,6}, P{3,7}}); + } + return test_result(); }