// Boost.Geometry // Copyright (c) 2021, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_VISIT_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_VISIT_HPP #include #include #include #include #include #include #include #include #include #include namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DISPATCH namespace dispatch { template ::type> struct visit_one { template static void apply(F && f, G && g) { f(std::forward(g)); } }; template struct visit_one { BOOST_GEOMETRY_STATIC_ASSERT_FALSE( "Not implemented for this Geometry type.", Geometry); }; template struct visit_one { template static void apply(F && function, Geom && geom) { traits::visit < util::remove_cref_t >::apply(std::forward(function), std::forward(geom)); } }; template < typename Geometry1, typename Geometry2, typename Tag1 = typename tag::type, typename Tag2 = typename tag::type > struct visit_two { template static void apply(F && f, G1 && geom1, G2 && geom2) { f(std::forward(geom1), std::forward(geom2)); } }; template struct visit_two { BOOST_GEOMETRY_STATIC_ASSERT_FALSE( "Not implemented for this Geometry1 type.", Geometry1); }; template struct visit_two { BOOST_GEOMETRY_STATIC_ASSERT_FALSE( "Not implemented for this Geometry2 type.", Geometry2); }; template struct visit_two { BOOST_GEOMETRY_STATIC_ASSERT_FALSE( "Not implemented for these types.", Geometry1, Geometry2); }; template struct visit_two { template static void apply(F && f, G1 && geom1, G2 && geom2) { traits::visit>::apply([&](auto && g1) { f(std::forward(g1), std::forward(geom2)); }, std::forward(geom1)); } }; template struct visit_two { template static void apply(F && f, G1 && geom1, G2 && geom2) { traits::visit>::apply([&](auto && g2) { f(std::forward(geom1), std::forward(g2)); }, std::forward(geom2)); } }; template struct visit_two { template static void apply(F && f, G1 && geom1, G2 && geom2) { traits::visit < util::remove_cref_t, util::remove_cref_t >::apply([&](auto && g1, auto && g2) { f(std::forward(g1), std::forward(g2)); }, std::forward(geom1), std::forward(geom2)); } }; template ::type> struct visit_breadth_first { template static bool apply(F f, G && g) { return f(std::forward(g)); } }; template struct visit_breadth_first { BOOST_GEOMETRY_STATIC_ASSERT_FALSE( "Not implemented for this Geometry type.", Geometry); }; template struct visit_breadth_first { template static bool apply(F function, Geom && geom) { bool result = true; traits::visit>::apply([&](auto && g) { result = visit_breadth_first < std::remove_reference_t >::apply(function, std::forward(g)); }, std::forward(geom)); return result; } }; } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH #ifndef DOXYGEN_NO_DETAIL namespace detail { template struct visit_breadth_first_impl { template static bool apply(F function, Geom && geom) { using iter_t = std::conditional_t < std::is_rvalue_reference::value, std::move_iterator::type>, typename boost::range_iterator::type >; std::deque queue; iter_t it = iter_t{ boost::begin(geom) }; iter_t end = iter_t{ boost::end(geom) }; for(;;) { for (; it != end; ++it) { bool result = true; traits::iter_visit>::apply([&](auto && g) { result = visit_breadth_first_impl::visit_or_enqueue( function, std::forward(g), queue, it); }, it); if (! result) { return false; } } if (queue.empty()) { break; } // Alternatively store a pointer to GeometryCollection // so this call can be avoided. traits::iter_visit>::apply([&](auto && g) { visit_breadth_first_impl::set_iterators(std::forward(g), it, end); }, queue.front()); queue.pop_front(); } return true; } private: template < bool PassIter, typename F, typename Geom, typename Iterator, std::enable_if_t::value, int> = 0 > static bool visit_or_enqueue(F &, Geom &&, std::deque & queue, Iterator iter) { queue.push_back(iter); return true; } template < bool PassIter, typename F, typename Geom, typename Iterator, std::enable_if_t::value && ! PassIter, int> = 0 > static bool visit_or_enqueue(F & f, Geom && g, std::deque & , Iterator) { return f(std::forward(g)); } template < bool PassIter, typename F, typename Geom, typename Iterator, std::enable_if_t::value && PassIter, int> = 0 > static bool visit_or_enqueue(F & f, Geom && g, std::deque & , Iterator iter) { return f(std::forward(g), iter); } template < typename Geom, typename Iterator, std::enable_if_t::value, int> = 0 > static void set_iterators(Geom && g, Iterator & first, Iterator & last) { first = Iterator{ boost::begin(g) }; last = Iterator{ boost::end(g) }; } template < typename Geom, typename Iterator, std::enable_if_t::value, int> = 0 > static void set_iterators(Geom &&, Iterator &, Iterator &) {} }; } // namespace detail #endif // DOXYGEN_NO_DETAIL #ifndef DOXYGEN_NO_DISPATCH namespace dispatch { // NOTE: This specialization works partially like std::visit and partially like // std::ranges::for_each. If the argument is rvalue reference then the elements // are passed into the function as rvalue references as well. This is consistent // with std::visit but different than std::ranges::for_each. It's done this way // because visit_breadth_first is also specialized for static and dynamic geometries // and references for them has to be propagated like that. If this is not // desireable then the support for other kinds of geometries should be dropped and // this algorithm should work only for geometry collection. Or forwarding of rvalue // references should simply be dropped entirely. // This is not a problem right now because only non-rvalue references are passed // but in the future there might be some issues. Consider e.g. passing a temporary // mutable proxy range as geometry collection. In such case the elements would be // passed as rvalue references which would be incorrect. template struct visit_breadth_first : detail::visit_breadth_first_impl<> {}; } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH #ifndef DOXYGEN_NO_DETAIL namespace detail { template inline void visit(UnaryFunction && function, Geometry && geometry) { dispatch::visit_one < std::remove_reference_t >::apply(std::forward(function), std::forward(geometry)); } template inline void visit(UnaryFunction && function, Geometry1 && geometry1, Geometry2 && geometry2) { dispatch::visit_two < std::remove_reference_t, std::remove_reference_t >::apply(std::forward(function), std::forward(geometry1), std::forward(geometry2)); } template inline void visit_breadth_first(UnaryFunction function, Geometry && geometry) { dispatch::visit_breadth_first < std::remove_reference_t >::apply(function, std::forward(geometry)); } } // namespace detail #endif // DOXYGEN_NO_DETAIL }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_VISIT_HPP