diff --git a/include/range/v3/utility/polymorphic_cast.hpp b/include/range/v3/utility/polymorphic_cast.hpp index e616c35cd9..65b8005ddc 100644 --- a/include/range/v3/utility/polymorphic_cast.hpp +++ b/include/range/v3/utility/polymorphic_cast.hpp @@ -16,19 +16,26 @@ namespace ranges inline namespace v3 { template - meta::if_, Target> - polymorphic_downcast(Source *x) noexcept + auto polymorphic_downcast(Source *x) noexcept -> + meta::if_, + decltype((static_cast(x), dynamic_cast(x)))> { - RANGES_ASSERT(dynamic_cast(x) == x); - return static_cast(x); + auto result = static_cast(x); + RANGES_ASSERT(dynamic_cast(x) == result); + return result; } template - meta::if_, Target> - polymorphic_downcast(Source &&x) noexcept + auto polymorphic_downcast(Source &&x) noexcept -> + meta::if_, + decltype((static_cast(std::declval()), + dynamic_cast(std::declval())))> { - using PTarget = meta::_t>; - auto ptr = polymorphic_downcast(std::addressof(x)); - return static_cast(*ptr); + 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/view/any_view.hpp b/include/range/v3/view/any_view.hpp index 9208f1f155..c8209f1411 100644 --- a/include/range/v3/view/any_view.hpp +++ b/include/range/v3/view/any_view.hpp @@ -88,6 +88,42 @@ namespace ranges 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: + 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 + {} + sentinel_t get(Rng const &rng) const noexcept + { + return ranges::end(rng); + } + }; + template struct any_input_view_interface { @@ -99,17 +135,17 @@ namespace ranges }; template - struct any_input_view_cursor + struct any_input_cursor { using single_pass = std::true_type; - any_input_view_cursor() = default; - constexpr any_input_view_cursor(any_input_view_interface &view) noexcept + 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_view_cursor const &) const noexcept + bool equal(any_input_cursor const &) const noexcept { return true; } @@ -124,40 +160,44 @@ namespace ranges template struct any_input_view_impl : any_input_view_interface - , tagged_compressed_tuple), tag::end(sentinel_t)> + , private tagged_compressed_tuple)> + , private any_view_sentinel_impl { - private: 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), tag::end(sentinel_t)>; + tag::current(iterator_t)>; using tagged_t::range; using tagged_t::current; - using tagged_t::end; + using sentinel_box_t = any_view_sentinel_impl; virtual void init() override { auto &rng = range(); - end() = ranges::end(rng); + sentinel_box_t::init(rng); current() = ranges::begin(rng); } - virtual bool done() const override { return current() == end(); } + virtual bool done() const override + { + return current() == sentinel_box_t::get(range()); + } virtual Ref read() const override { return *current(); } virtual void next() override { ++current(); } - public: - explicit any_input_view_impl(Rng rng_) - : tagged_t{std::move(rng_), iterator_t{}, sentinel_t{}} - {} - any_input_view_impl(any_input_view_impl const &) = delete; - any_input_view_impl &operator=(any_input_view_impl const &) = delete; }; template struct any_cursor_interface { virtual ~any_cursor_interface() = default; - virtual any_ref iter() const = 0; + 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; @@ -186,18 +226,18 @@ namespace ranges struct any_cursor_impl : any_cloneable_cursor_interface { - private: CONCEPT_ASSERT(ConvertibleTo, Ref>()); CONCEPT_ASSERT(Cat >= category::forward); - using Forward = any_cursor_interface; - - I it_; - public: any_cursor_impl() = default; any_cursor_impl(I it) : it_{std::move(it)} {} + private: + using Forward = any_cursor_interface; + + I it_; + any_ref iter() const // override { return it_; @@ -235,27 +275,24 @@ namespace ranges } }; - struct any_sentinel_interface + struct fully_erased_view { - virtual bool at_end(any_ref) 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: - ~any_sentinel_interface() = default; + ~fully_erased_view() = default; }; struct any_sentinel { any_sentinel() = default; - constexpr explicit any_sentinel(any_sentinel_interface const &view) noexcept + constexpr explicit any_sentinel(fully_erased_view const &view) noexcept : view_{&view} {} - - any_sentinel_interface const &view() const noexcept - { - RANGES_EXPECT(view_); - return *view_; - } private: - any_sentinel_interface const *view_ = nullptr; + template friend struct any_cursor; + + fully_erased_view const *view_ = nullptr; }; template @@ -299,7 +336,8 @@ namespace ranges } bool equal(any_sentinel const &that) const { - return !ptr_ || that.view().at_end(ptr_->iter()); + RANGES_EXPECT(!ptr_ == !that.view_); + return !ptr_ || that.view_->at_end(ptr_->iter()); } void next() { @@ -328,7 +366,7 @@ namespace ranges template struct any_view_interface - : any_sentinel_interface + : fully_erased_view { CONCEPT_ASSERT(Cat >= category::forward); @@ -343,36 +381,33 @@ namespace ranges template struct any_view_impl : any_cloneable_view_interface - , tagged_compressed_tuple)> + , private box> + , private any_view_sentinel_impl { - private: CONCEPT_ASSERT(Cat >= category::forward); CONCEPT_ASSERT(AnyCompatibleRange()); - using tagged_t = tagged_compressed_tuple< - tag::range(Rng), tag::end(sentinel_t)>; - using tagged_t::range; - using tagged_t::end; - - public: any_view_impl() = default; any_view_impl(Rng rng) - : tagged_t{std::move(rng), sentinel_t{}} - { - end() = ranges::end(range()); - } + : 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 any_cursor{range()}; + return any_cursor{range_box_t::get()}; } bool at_end(any_ref it_) const override { auto &it = it_.get const>(); - return it == end(); + return it == sentinel_box_t::get(range_box_t::get()); } std::unique_ptr> clone() const override { - return detail::make_unique(range()); + return detail::make_unique(range_box_t::get()); } }; } @@ -384,33 +419,9 @@ namespace ranges struct any_view : view_facade, unknown> { - private: friend range_access; CONCEPT_ASSERT(Cat >= category::forward); - std::unique_ptr> ptr_; - - 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_}; - } - template - using impl_t = detail::any_view_impl, Ref, Cat>; - template - any_view(Rng &&rng, std::true_type) - : ptr_{detail::make_unique>(view::all(static_cast(rng)))} - {} - template - any_view(Rng &&, std::false_type) - { - static_assert(detail::to_cat_(range_concept{}) >= Cat, - "The range passed to any_view() does not model the requested category"); - } - public: any_view() = default; templateclone() : nullptr); return *this; } + private: + template + using impl_t = detail::any_view_impl, Ref, Cat>; + template + any_view(Rng &&rng, std::true_type) + : ptr_{detail::make_unique>(view::all(static_cast(rng)))} + {} + template + any_view(Rng &&, std::false_type) + { + static_assert(detail::to_cat_(range_concept{}) >= Cat, + "The range passed to any_view() does not model the requested category"); + } + + 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> { - private: friend range_access; - template - using impl_t = detail::any_input_view_impl, Ref>; - - std::shared_ptr> ptr_; - detail::any_input_view_cursor begin_cursor() - { - if (!ptr_) - return {}; - - ptr_->init(); - return detail::any_input_view_cursor{*ptr_}; - } - public: any_view() = default; template>(view::all(static_cast(rng)))} {} + private: + template + using impl_t = detail::any_input_view_impl, Ref>; + + detail::any_input_cursor begin_cursor() + { + if (!ptr_) + return {}; + + ptr_->init(); + return detail::any_input_cursor{*ptr_}; + } + + std::shared_ptr> ptr_; }; template diff --git a/test/view/any_view.cpp b/test/view/any_view.cpp index 37c9315826..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; @@ -80,5 +132,7 @@ int main() ::check_equal(v, ten_ints); } + test_polymorphic_downcast(); + return test_result(); }