visit.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. // Boost.Geometry
  2. // Copyright (c) 2021, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  4. // Licensed under the Boost Software License version 1.0.
  5. // http://www.boost.org/users/license.html
  6. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_VISIT_HPP
  7. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_VISIT_HPP
  8. #include <deque>
  9. #include <iterator>
  10. #include <utility>
  11. #include <boost/range/begin.hpp>
  12. #include <boost/range/end.hpp>
  13. #include <boost/geometry/core/static_assert.hpp>
  14. #include <boost/geometry/core/tag.hpp>
  15. #include <boost/geometry/core/tags.hpp>
  16. #include <boost/geometry/core/visit.hpp>
  17. #include <boost/geometry/util/type_traits.hpp>
  18. namespace boost { namespace geometry
  19. {
  20. #ifndef DOXYGEN_NO_DISPATCH
  21. namespace dispatch
  22. {
  23. template <typename Geometry, typename Tag = typename tag<Geometry>::type>
  24. struct visit_one
  25. {
  26. template <typename F, typename G>
  27. static void apply(F && f, G && g)
  28. {
  29. f(std::forward<G>(g));
  30. }
  31. };
  32. template <typename Geometry>
  33. struct visit_one<Geometry, void>
  34. {
  35. BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
  36. "Not implemented for this Geometry type.",
  37. Geometry);
  38. };
  39. template <typename Geometry>
  40. struct visit_one<Geometry, dynamic_geometry_tag>
  41. {
  42. template <typename F, typename Geom>
  43. static void apply(F && function, Geom && geom)
  44. {
  45. traits::visit
  46. <
  47. util::remove_cref_t<Geom>
  48. >::apply(std::forward<F>(function), std::forward<Geom>(geom));
  49. }
  50. };
  51. template
  52. <
  53. typename Geometry1, typename Geometry2,
  54. typename Tag1 = typename tag<Geometry1>::type,
  55. typename Tag2 = typename tag<Geometry2>::type
  56. >
  57. struct visit_two
  58. {
  59. template <typename F, typename G1, typename G2>
  60. static void apply(F && f, G1 && geom1, G2 && geom2)
  61. {
  62. f(std::forward<G1>(geom1), std::forward<G2>(geom2));
  63. }
  64. };
  65. template <typename Geometry1, typename Geometry2, typename Tag2>
  66. struct visit_two<Geometry1, Geometry2, void, Tag2>
  67. {
  68. BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
  69. "Not implemented for this Geometry1 type.",
  70. Geometry1);
  71. };
  72. template <typename Geometry1, typename Geometry2, typename Tag1>
  73. struct visit_two<Geometry1, Geometry2, Tag1, void>
  74. {
  75. BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
  76. "Not implemented for this Geometry2 type.",
  77. Geometry2);
  78. };
  79. template <typename Geometry1, typename Geometry2>
  80. struct visit_two<Geometry1, Geometry2, void, void>
  81. {
  82. BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
  83. "Not implemented for these types.",
  84. Geometry1, Geometry2);
  85. };
  86. template <typename Geometry1, typename Geometry2, typename Tag2>
  87. struct visit_two<Geometry1, Geometry2, dynamic_geometry_tag, Tag2>
  88. {
  89. template <typename F, typename G1, typename G2>
  90. static void apply(F && f, G1 && geom1, G2 && geom2)
  91. {
  92. traits::visit<util::remove_cref_t<G1>>::apply([&](auto && g1)
  93. {
  94. f(std::forward<decltype(g1)>(g1), std::forward<G2>(geom2));
  95. }, std::forward<G1>(geom1));
  96. }
  97. };
  98. template <typename Geometry1, typename Geometry2, typename Tag1>
  99. struct visit_two<Geometry1, Geometry2, Tag1, dynamic_geometry_tag>
  100. {
  101. template <typename F, typename G1, typename G2>
  102. static void apply(F && f, G1 && geom1, G2 && geom2)
  103. {
  104. traits::visit<util::remove_cref_t<G2>>::apply([&](auto && g2)
  105. {
  106. f(std::forward<G1>(geom1), std::forward<decltype(g2)>(g2));
  107. }, std::forward<G2>(geom2));
  108. }
  109. };
  110. template <typename Geometry1, typename Geometry2>
  111. struct visit_two<Geometry1, Geometry2, dynamic_geometry_tag, dynamic_geometry_tag>
  112. {
  113. template <typename F, typename G1, typename G2>
  114. static void apply(F && f, G1 && geom1, G2 && geom2)
  115. {
  116. traits::visit
  117. <
  118. util::remove_cref_t<G1>, util::remove_cref_t<G2>
  119. >::apply([&](auto && g1, auto && g2)
  120. {
  121. f(std::forward<decltype(g1)>(g1), std::forward<decltype(g2)>(g2));
  122. }, std::forward<G1>(geom1), std::forward<G2>(geom2));
  123. }
  124. };
  125. template <typename Geometry, typename Tag = typename tag<Geometry>::type>
  126. struct visit_breadth_first
  127. {
  128. template <typename F, typename G>
  129. static bool apply(F f, G && g)
  130. {
  131. return f(std::forward<G>(g));
  132. }
  133. };
  134. template <typename Geometry>
  135. struct visit_breadth_first<Geometry, void>
  136. {
  137. BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
  138. "Not implemented for this Geometry type.",
  139. Geometry);
  140. };
  141. template <typename Geometry>
  142. struct visit_breadth_first<Geometry, dynamic_geometry_tag>
  143. {
  144. template <typename Geom, typename F>
  145. static bool apply(F function, Geom && geom)
  146. {
  147. bool result = true;
  148. traits::visit<util::remove_cref_t<Geom>>::apply([&](auto && g)
  149. {
  150. result = visit_breadth_first
  151. <
  152. std::remove_reference_t<decltype(g)>
  153. >::apply(function,
  154. std::forward<decltype(g)>(g));
  155. }, std::forward<Geom>(geom));
  156. return result;
  157. }
  158. };
  159. } // namespace dispatch
  160. #endif // DOXYGEN_NO_DISPATCH
  161. #ifndef DOXYGEN_NO_DETAIL
  162. namespace detail
  163. {
  164. template <bool PassIterator = false>
  165. struct visit_breadth_first_impl
  166. {
  167. template <typename F, typename Geom>
  168. static bool apply(F function, Geom && geom)
  169. {
  170. using iter_t = std::conditional_t
  171. <
  172. std::is_rvalue_reference<decltype(geom)>::value,
  173. std::move_iterator<typename boost::range_iterator<Geom>::type>,
  174. typename boost::range_iterator<Geom>::type
  175. >;
  176. std::deque<iter_t> queue;
  177. iter_t it = iter_t{ boost::begin(geom) };
  178. iter_t end = iter_t{ boost::end(geom) };
  179. for(;;)
  180. {
  181. for (; it != end; ++it)
  182. {
  183. bool result = true;
  184. traits::iter_visit<util::remove_cref_t<Geom>>::apply([&](auto && g)
  185. {
  186. result = visit_breadth_first_impl::visit_or_enqueue<PassIterator>(
  187. function, std::forward<decltype(g)>(g), queue, it);
  188. }, it);
  189. if (! result)
  190. {
  191. return false;
  192. }
  193. }
  194. if (queue.empty())
  195. {
  196. break;
  197. }
  198. // Alternatively store a pointer to GeometryCollection
  199. // so this call can be avoided.
  200. traits::iter_visit<util::remove_cref_t<Geom>>::apply([&](auto && g)
  201. {
  202. visit_breadth_first_impl::set_iterators(std::forward<decltype(g)>(g), it, end);
  203. }, queue.front());
  204. queue.pop_front();
  205. }
  206. return true;
  207. }
  208. private:
  209. template
  210. <
  211. bool PassIter, typename F, typename Geom, typename Iterator,
  212. std::enable_if_t<util::is_geometry_collection<Geom>::value, int> = 0
  213. >
  214. static bool visit_or_enqueue(F &, Geom &&, std::deque<Iterator> & queue, Iterator iter)
  215. {
  216. queue.push_back(iter);
  217. return true;
  218. }
  219. template
  220. <
  221. bool PassIter, typename F, typename Geom, typename Iterator,
  222. std::enable_if_t<! util::is_geometry_collection<Geom>::value && ! PassIter, int> = 0
  223. >
  224. static bool visit_or_enqueue(F & f, Geom && g, std::deque<Iterator> & , Iterator)
  225. {
  226. return f(std::forward<Geom>(g));
  227. }
  228. template
  229. <
  230. bool PassIter, typename F, typename Geom, typename Iterator,
  231. std::enable_if_t<! util::is_geometry_collection<Geom>::value && PassIter, int> = 0
  232. >
  233. static bool visit_or_enqueue(F & f, Geom && g, std::deque<Iterator> & , Iterator iter)
  234. {
  235. return f(std::forward<Geom>(g), iter);
  236. }
  237. template
  238. <
  239. typename Geom, typename Iterator,
  240. std::enable_if_t<util::is_geometry_collection<Geom>::value, int> = 0
  241. >
  242. static void set_iterators(Geom && g, Iterator & first, Iterator & last)
  243. {
  244. first = Iterator{ boost::begin(g) };
  245. last = Iterator{ boost::end(g) };
  246. }
  247. template
  248. <
  249. typename Geom, typename Iterator,
  250. std::enable_if_t<! util::is_geometry_collection<Geom>::value, int> = 0
  251. >
  252. static void set_iterators(Geom &&, Iterator &, Iterator &)
  253. {}
  254. };
  255. } // namespace detail
  256. #endif // DOXYGEN_NO_DETAIL
  257. #ifndef DOXYGEN_NO_DISPATCH
  258. namespace dispatch
  259. {
  260. // NOTE: This specialization works partially like std::visit and partially like
  261. // std::ranges::for_each. If the argument is rvalue reference then the elements
  262. // are passed into the function as rvalue references as well. This is consistent
  263. // with std::visit but different than std::ranges::for_each. It's done this way
  264. // because visit_breadth_first is also specialized for static and dynamic geometries
  265. // and references for them has to be propagated like that. If this is not
  266. // desireable then the support for other kinds of geometries should be dropped and
  267. // this algorithm should work only for geometry collection. Or forwarding of rvalue
  268. // references should simply be dropped entirely.
  269. // This is not a problem right now because only non-rvalue references are passed
  270. // but in the future there might be some issues. Consider e.g. passing a temporary
  271. // mutable proxy range as geometry collection. In such case the elements would be
  272. // passed as rvalue references which would be incorrect.
  273. template <typename Geometry>
  274. struct visit_breadth_first<Geometry, geometry_collection_tag>
  275. : detail::visit_breadth_first_impl<>
  276. {};
  277. } // namespace dispatch
  278. #endif // DOXYGEN_NO_DISPATCH
  279. #ifndef DOXYGEN_NO_DETAIL
  280. namespace detail
  281. {
  282. template <typename UnaryFunction, typename Geometry>
  283. inline void visit(UnaryFunction && function, Geometry && geometry)
  284. {
  285. dispatch::visit_one
  286. <
  287. std::remove_reference_t<Geometry>
  288. >::apply(std::forward<UnaryFunction>(function), std::forward<Geometry>(geometry));
  289. }
  290. template <typename UnaryFunction, typename Geometry1, typename Geometry2>
  291. inline void visit(UnaryFunction && function, Geometry1 && geometry1, Geometry2 && geometry2)
  292. {
  293. dispatch::visit_two
  294. <
  295. std::remove_reference_t<Geometry1>,
  296. std::remove_reference_t<Geometry2>
  297. >::apply(std::forward<UnaryFunction>(function),
  298. std::forward<Geometry1>(geometry1),
  299. std::forward<Geometry2>(geometry2));
  300. }
  301. template <typename UnaryFunction, typename Geometry>
  302. inline void visit_breadth_first(UnaryFunction function, Geometry && geometry)
  303. {
  304. dispatch::visit_breadth_first
  305. <
  306. std::remove_reference_t<Geometry>
  307. >::apply(function, std::forward<Geometry>(geometry));
  308. }
  309. } // namespace detail
  310. #endif // DOXYGEN_NO_DETAIL
  311. }} // namespace boost::geometry
  312. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_VISIT_HPP