From 1f8b6e02ecdbf2ff6a02e72af85f21b5a9adf71b Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sun, 21 Apr 2024 09:41:50 +0200 Subject: [PATCH] Add enumerate to prepare for 17 and 23 --- .../buffer/buffered_piece_collection.hpp | 48 +++--- .../detail/overlay/assign_parents.hpp | 34 ++-- .../detail/overlay/check_enrich.hpp | 11 +- .../overlay/enrich_intersection_points.hpp | 38 +++-- .../detail/overlay/intersection_insert.hpp | 9 +- .../geometry/util/for_each_with_index.hpp | 49 ------ .../boost/geometry/views/enumerate_view.hpp | 159 ++++++++++++++++++ test/CMakeLists.txt | 1 + test/views/CMakeLists.txt | 16 ++ test/views/Jamfile | 1 + test/views/enumerate_view.cpp | 74 ++++++++ 11 files changed, 332 insertions(+), 108 deletions(-) delete mode 100644 include/boost/geometry/util/for_each_with_index.hpp create mode 100644 include/boost/geometry/views/enumerate_view.hpp create mode 100644 test/views/CMakeLists.txt create mode 100644 test/views/enumerate_view.cpp diff --git a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index bd39116fc4..1382b53a66 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -59,7 +59,7 @@ #include #include -#include +#include #include @@ -376,16 +376,18 @@ struct buffered_piece_collection inline void update_turn_administration() { - for_each_with_index(m_turns, [this](std::size_t index, auto& turn) + for (auto const& enumerated : util::enumerate(m_turns)) { - turn.turn_index = index; + // enumerated is const, but its value is a non-const reference + auto& turn = enumerated.value; + turn.turn_index = enumerated.index; // Verify if a turn is a linear endpoint if (! turn.is_linear_end_point) { this->check_linear_endpoints(turn); } - }); + } } // Calculate properties of piece borders which are not influenced @@ -1091,30 +1093,32 @@ struct buffered_piece_collection // Inner rings, for deflate, which do not have intersections, and // which are outside originals, are skipped // (other ones should be traversed) - for_each_with_index(offsetted_rings, [&](std::size_t index, auto const& ring) + for (auto const& enumerated : util::enumerate(offsetted_rings)) + { + auto const& ring = enumerated.value; + if (! ring.has_intersections() + && ! ring.is_untouched_outside_original) { - if (! ring.has_intersections() - && ! ring.is_untouched_outside_original) + properties const p = properties(ring, m_strategy); + if (p.valid) { - properties p = properties(ring, m_strategy); - if (p.valid) - { - ring_identifier id(0, index, -1); - selected[id] = p; - } + ring_identifier id(0, enumerated.index, -1); + selected[id] = p; } - }); + } + } // Select all created rings - for_each_with_index(traversed_rings, [&](std::size_t index, auto const& ring) + for (auto const& enumerated : util::enumerate(traversed_rings)) + { + auto const& ring = enumerated.value; + properties p = properties(ring, m_strategy); + if (p.valid) { - properties p = properties(ring, m_strategy); - if (p.valid) - { - ring_identifier id(2, index, -1); - selected[id] = p; - } - }); + ring_identifier id(2, enumerated.index, -1); + selected[id] = p; + } + } detail::overlay::assign_parents(offsetted_rings, traversed_rings, selected, m_strategy); diff --git a/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp index 578ba090cf..73e3bdb284 100644 --- a/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp @@ -27,10 +27,10 @@ #include #include #include +#include #include -#include namespace boost { namespace geometry { @@ -271,25 +271,28 @@ inline void assign_parents(Geometry1 const& geometry1, std::size_t index_positive = 0; // only used if count_positive>0 // Copy to vector (this might be obsolete, using the map directly) + // The index in the map is also the index in the vector. using helper = ring_info_helper; std::vector vector(count_total); - for_each_with_index(ring_map, [&](std::size_t index, auto const& pair) + for (auto const& enumerated : util::enumerate(ring_map)) { - vector[index] = helper(pair.first, pair.second.get_area()); - helper& item = vector[index]; - switch(pair.first.source_index) + auto const& ring_id = enumerated.value.first; + auto const& info = enumerated.value.second; + vector[enumerated.index] = helper(ring_id, info.get_area()); + helper& item = vector[enumerated.index]; + switch(ring_id.source_index) { case 0 : - geometry::envelope(get_ring::apply(pair.first, geometry1), + geometry::envelope(get_ring::apply(ring_id, geometry1), item.envelope, strategy); break; case 1 : - geometry::envelope(get_ring::apply(pair.first, geometry2), + geometry::envelope(get_ring::apply(ring_id, geometry2), item.envelope, strategy); break; case 2 : - geometry::envelope(get_ring::apply(pair.first, collection), + geometry::envelope(get_ring::apply(ring_id, collection), item.envelope, strategy); break; } @@ -300,9 +303,9 @@ inline void assign_parents(Geometry1 const& geometry1, if (item.real_area > 0) { count_positive++; - index_positive = index; + index_positive = enumerated.index; } - }); + } if (! check_for_orientation) { @@ -323,15 +326,16 @@ inline void assign_parents(Geometry1 const& geometry1, // located outside the outer ring, this cannot be done ring_identifier id_of_positive = vector[index_positive].id; ring_info_type& outer = ring_map[id_of_positive]; - for_each_with_index(vector, [&](std::size_t index, auto const& item) + for (auto const& item : util::enumerate(vector)) { - if (index != index_positive) + if (item.index != index_positive) { - ring_info_type& inner = ring_map[item.id]; + auto const id = item.value.id; + ring_info_type& inner = ring_map[id]; inner.parent = id_of_positive; - outer.children.push_back(item.id); + outer.children.push_back(id); } - }); + } return; } } diff --git a/include/boost/geometry/algorithms/detail/overlay/check_enrich.hpp b/include/boost/geometry/algorithms/detail/overlay/check_enrich.hpp index ad3bd6bda9..2195494184 100644 --- a/include/boost/geometry/algorithms/detail/overlay/check_enrich.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/check_enrich.hpp @@ -26,6 +26,7 @@ #include #include +#include namespace boost { namespace geometry @@ -122,17 +123,17 @@ inline void check_detailed(MetaTurns& meta_turns, MetaTurn const& meta_turn, template -inline bool check_graph(TurnPoints& turn_points, operation_type for_operation) +inline bool check_graph(TurnPoints const& turn_points, operation_type for_operation) { - typedef typename boost::range_value::type turn_point_type; + using turn_point_type = typename boost::range_value::type; bool error = false; std::vector > meta_turns; - for_each_with_index(turn_points, [&](std::size_t index, auto const& point) + for (auto const& item : util::enumerate(turn_points)) { - meta_turns.push_back(meta_turn(index, point)); - }); + meta_turns.push_back(meta_turn(item.index, item.value)); + } int cycle = 0; for (auto& meta_turn : meta_turns) diff --git a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index 2a6d62ef6c..69e0048af9 100644 --- a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -42,11 +43,8 @@ #include #include #include -#include +#include -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH -# include -#endif namespace boost { namespace geometry @@ -112,8 +110,10 @@ template inline void enrich_assign(Operations& operations, Turns& turns, bool check_consecutive_turns) { - for_each_with_index(operations, [&](std::size_t index, auto const& indexed) + for (auto const& item : util::enumerate(operations)) { + auto const& index = item.index; + auto const& indexed = item.value; auto& turn = turns[indexed.turn_index]; auto& op = turn.operations[indexed.operation_index]; @@ -185,7 +185,7 @@ inline void enrich_assign(Operations& operations, Turns& turns, // Next turn is located further on same segment: assign next_ip_index op.enriched.next_ip_index = static_cast(operations[next_index].turn_index); } - }); + } #ifdef BOOST_GEOMETRY_DEBUG_ENRICH for (auto const& indexed_op : operations) @@ -223,8 +223,10 @@ inline void enrich_adapt(Operations& operations, Turns& turns) bool next_phase = false; std::size_t previous_index = operations.size() - 1; - for_each_with_index(operations, [&](std::size_t index, auto const& indexed) + for (auto const& item : util::enumerate(operations)) { + auto const& index = item.index; + auto const& indexed = item.value; auto& turn = turns[indexed.turn_index]; auto& op = turn.operations[indexed.operation_index]; @@ -243,7 +245,7 @@ inline void enrich_adapt(Operations& operations, Turns& turns) } } previous_index = index; - }); + } if (! next_phase) { @@ -290,12 +292,16 @@ template inline void create_map(Turns const& turns, MappedVector& mapped_vector, IncludePolicy const& include_policy) { - for_each_with_index(turns, [&](std::size_t index, auto const& turn) + for (auto const& turn_item : util::enumerate(turns)) { + auto const& index = turn_item.index; + auto const& turn = turn_item.value; if (! turn.discarded) { - for_each_with_index(turn.operations, [&](std::size_t op_index, auto const& op) + for (auto const& op_item : util::enumerate(turn.operations)) { + auto const& op_index = op_item.index; + auto const& op = op_item.value; if (include_policy.include(op.operation)) { ring_identifier const ring_id @@ -309,9 +315,9 @@ inline void create_map(Turns const& turns, MappedVector& mapped_vector, index, op_index, op, turn.operations[1 - op_index].seg_id ); } - }); + } } - }); + } } template @@ -534,9 +540,15 @@ inline void enrich_intersection_points(Turns& turns, } #ifdef BOOST_GEOMETRY_DEBUG_ENRICH - //detail::overlay::check_graph(turns, for_operation); + constexpr bool do_check_graph = true; +#else + constexpr bool do_check_graph = false; #endif + if BOOST_GEOMETRY_CONSTEXPR (do_check_graph) + { + detail::overlay::check_graph(turns, target_operation); + } } }} // namespace boost::geometry diff --git a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index df3b70a456..36afaedea3 100644 --- a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -59,7 +59,7 @@ #if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) #include #include -#include +#include #endif namespace boost { namespace geometry @@ -379,10 +379,11 @@ struct intersection_of_linestring_with_areal } #if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) - for_each_with_index(turns, [](auto index, auto const& turn) + for (auto const& item : util::enumerate(turns)) { - debug_follow(turn, turn.operations[0], index); - }); + auto const& turn = item.value; + debug_follow(turn, turn.operations[0], item.index); + } #endif return follower::apply diff --git a/include/boost/geometry/util/for_each_with_index.hpp b/include/boost/geometry/util/for_each_with_index.hpp deleted file mode 100644 index 425f2a497c..0000000000 --- a/include/boost/geometry/util/for_each_with_index.hpp +++ /dev/null @@ -1,49 +0,0 @@ -// Boost.Geometry - -// Copyright (c) 2023 Barend Gehrels, Amsterdam, the Netherlands. - -// Use, modification and distribution is subject to the Boost Software License, -// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_GEOMETRY_UTIL_FOR_EACH_WITH_INDEX_HPP -#define BOOST_GEOMETRY_UTIL_FOR_EACH_WITH_INDEX_HPP - -#include -#include -#include - -namespace boost { namespace geometry -{ - -#ifndef DOXYGEN_NO_DETAIL -namespace detail -{ - -// Utility function to implement a Kotlin like range based for loop -template -inline void for_each_with_index(Container const& container, Function func) -{ - typename boost::range_size::type index = 0; - for (auto it = boost::begin(container); it != boost::end(container); ++it, ++index) - { - func(index, *it); - } -} - -template -inline void for_each_with_index(Container& container, Function func) -{ - typename boost::range_size::type index = 0; - for (auto it = boost::begin(container); it != boost::end(container); ++it, ++index) - { - func(index, *it); - } -} - -} // namespace detail -#endif // DOXYGEN_NO_DETAIL - -}} // namespace boost::geometry - -#endif // BOOST_GEOMETRY_UTIL_FOR_EACH_WITH_INDEX_HPP diff --git a/include/boost/geometry/views/enumerate_view.hpp b/include/boost/geometry/views/enumerate_view.hpp new file mode 100644 index 0000000000..65f3beed90 --- /dev/null +++ b/include/boost/geometry/views/enumerate_view.hpp @@ -0,0 +1,159 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2024 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_VIEWS_ENUMERATE_VIEW_HPP +#define BOOST_GEOMETRY_VIEWS_ENUMERATE_VIEW_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { namespace geometry +{ + +namespace util +{ + +// This view is a range of values, each with an index +// It is used to iterate over a range, and to get the index of the value +// It is used in the enumerate function +// The typename Range can either be const or non-const +template +struct enumerated_view +{ + // The return value of the iterator + struct value_with_index + { + using type = util::transcribe_const_t + < + Range, + typename boost::range_value::type + >; + + // Member variable index contains the zero-based index of the value in the range + std::size_t const index; + + // Member variable value contains a const or non-const reference to the value itself + type& value; + }; + +private: + // Iterator implementation, not exposed. + struct enumerating_iterator + : public boost::iterator_facade + < + enumerating_iterator, + value_with_index const, + boost::random_access_traversal_tag, + value_with_index const, + typename boost::range_difference::type + > + { + using reference = value_with_index; + using difference_type = typename boost::range_difference::type; + + // Constructor with the range it handles + explicit inline enumerating_iterator(Range& range) + : m_begin(boost::begin(range)) + , m_end(boost::end(range)) + , m_iterator(boost::begin(range)) + {} + + // Constructor to indicate the end of a range + explicit inline enumerating_iterator(Range& range, bool) + : m_begin(boost::begin(range)) + , m_end(boost::end(range)) + , m_iterator(boost::end(range)) + {} + + // There is no default constructor + enumerating_iterator() = delete; + + inline reference dereference() const + { + constexpr difference_type zero = 0; + const std::size_t index = (std::max)(zero, std::distance(m_begin, m_iterator)); + const value_with_index result{index, *m_iterator}; + return result; + } + + inline difference_type distance_to(enumerating_iterator const& other) const + { + return std::distance(other.m_iterator, m_iterator); + } + + inline bool equal(enumerating_iterator const& other) const + { + return + m_begin == other.m_begin + && m_end == other.m_end + && m_iterator == other.m_iterator; + } + + inline void increment() + { + ++m_iterator; + } + + inline void decrement() + { + --m_iterator; + } + + inline void advance(difference_type n) + { + std::advance(m_iterator, n); + } + + const typename boost::range_iterator::type m_begin; + const typename boost::range_iterator::type m_end; + + typename boost::range_iterator::type m_iterator; + }; + +public: + using iterator = enumerating_iterator; + using const_iterator = enumerating_iterator; + + explicit inline enumerated_view(Range& range) + : m_begin(range) + , m_end(range, true) + {} + + inline iterator begin() const { return m_begin; } + inline iterator end() const { return m_end; } + +private: + const iterator m_begin; + const iterator m_end; +}; + +// Helper function to create the enumerated view, for a const range +template +inline auto enumerate(Range const& range) +{ + return util::enumerated_view(range); +} + +// Helper function to create the enumerated view, for a non-const range +template +inline auto enumerate(Range& range) +{ + return util::enumerated_view(range); +} + +}}} // boost::geometry::util + + +#endif // BOOST_GEOMETRY_VIEWS_ENUMERATE_VIEW_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 11287a29ce..b80bd7d483 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -49,3 +49,4 @@ endif() add_subdirectory(algorithms) add_subdirectory(util) +add_subdirectory(views) diff --git a/test/views/CMakeLists.txt b/test/views/CMakeLists.txt new file mode 100644 index 0000000000..292a11a69e --- /dev/null +++ b/test/views/CMakeLists.txt @@ -0,0 +1,16 @@ +# Boost.Geometry +# Copyright (c) 2024 Barend Gehrels, Amsterdam, the Netherlands. +# Use, modification and distribution is subject to the Boost Software License, +# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +foreach(item IN ITEMS + box_view + closeable_view + enumerate_view + random_access_view + reversible_closeable + reversible_view + segment_view) + boost_geometry_add_unit_test("views" ${item}) +endforeach() diff --git a/test/views/Jamfile b/test/views/Jamfile index ef716392c3..415d07a387 100644 --- a/test/views/Jamfile +++ b/test/views/Jamfile @@ -17,6 +17,7 @@ test-suite boost-geometry-views : [ run box_view.cpp : : : : views_box_view ] [ run closeable_view.cpp : : : : views_closeable_view ] + [ run enumerate_view.cpp : : : : views_enumerate_view ] [ run random_access_view.cpp : : : : views_random_access_view ] [ run reversible_closeable.cpp : : : : views_reversible_closeable ] [ run reversible_view.cpp : : : : views_reversible_view ] diff --git a/test/views/enumerate_view.cpp b/test/views/enumerate_view.cpp new file mode 100644 index 0000000000..8acdd286ce --- /dev/null +++ b/test/views/enumerate_view.cpp @@ -0,0 +1,74 @@ +// Boost.Geometry +// Unit Test + +// Copyright (c) 2024 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include +#include + +void test_const() +{ + const std::vector vec{"cat", "mouse", "squirrel"}; + std::size_t test_index = 0; + for (auto const& item : boost::geometry::util::enumerate(vec)) + { + BOOST_CHECK_EQUAL(item.index, test_index++); + switch(item.index) + { + case 0 : BOOST_CHECK_EQUAL(item.value, "cat"); break; + case 1 : BOOST_CHECK_EQUAL(item.value, "mouse"); break; + case 2 : BOOST_CHECK_EQUAL(item.value, "squirrel"); break; + } + } +} + +void test_non_const() +{ + std::vector vec{"Amsterdam", "London", "Paris"}; + std::size_t index_sum = 0; + for (auto const& item : boost::geometry::util::enumerate(vec)) + { + item.value += " is a city"; + index_sum += item.index; + } + BOOST_CHECK_EQUAL(vec[0], "Amsterdam is a city"); + BOOST_CHECK_EQUAL(vec[1], "London is a city"); + BOOST_CHECK_EQUAL(vec[2], "Paris is a city"); + BOOST_CHECK_EQUAL(index_sum, 3); +} + +// Verifies the usage of the enumerate_view with C++17 structured bindings +// See https://en.cppreference.com/w/cpp/ranges/enumerate_view +void test_cpp17() +{ +#if __cplusplus >= 201703L + std::vector numbers{1, 3, 5, 7}; + std::size_t sum_indexes = 0; + int sum_numbers = 0; + for (auto const [index, num] : boost::geometry::util::enumerate(numbers)) + { + sum_indexes += index; + sum_numbers += num; + // num is mutable even with const, which does not propagate to reference + num++; + } + BOOST_CHECK_EQUAL(sum_indexes, 6); + BOOST_CHECK_EQUAL(sum_numbers, 16); + BOOST_CHECK_EQUAL(numbers[0], 2); +#endif +} + +int test_main(int, char* []) +{ + test_const(); + test_non_const(); + test_cpp17(); + return 0; +}