diff --git a/CHANGELOG.md b/CHANGELOG.md index 49eb223575b27dbab80c7216b3bffbf4ddc6d1f6..039133630f7c9e7a9467a1be2ec8cb6923ef4481 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,9 @@ SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception ## C++: Changelog +- `Dune::IteratorRange` now supports different types for begin and end iterator + to model C++20's sentinel terminated ranges. + - Add preprocessor macro `DUNE_FORCE_INLINE` as a portable attribute to force inlining of functions (if supported). - Add `bit_width` and `countl_zero` overloads for `bigunsignedint` objects. diff --git a/dune/common/iteratorrange.hh b/dune/common/iteratorrange.hh index b897128156e9a9e8e46a8d43911d7c501f03321b..268ba411bc85c38a95b5fa2e155c981da256f647 100644 --- a/dune/common/iteratorrange.hh +++ b/dune/common/iteratorrange.hh @@ -14,10 +14,14 @@ namespace Dune { * existing containers that lack a standard begin(), end() * pair of member functions. * + * This supports to use a different type for the end iterator + * (a so called sentinel). + * * \tparam Iterator The type of iterator + * \tparam Sentinel The type of the end iterator (default=Iterator) * \ingroup CxxUtilities */ - template<typename Iterator> + template<typename Iterator, typename Sentinel=Iterator> class IteratorRange { @@ -26,6 +30,9 @@ namespace Dune { //! The iterator belonging to this range. typedef Iterator iterator; + //! The iterator belonging to this range. + typedef Sentinel sentinel; + //! The iterator belonging to this range. /** * This typedef is here mainly for compatibility reasons. @@ -33,9 +40,9 @@ namespace Dune { typedef Iterator const_iterator; //! Constructs an iterator range on [begin,end). - IteratorRange(const Iterator& begin, const Iterator& end) - : _begin(begin) - , _end(end) + IteratorRange(const Iterator& begin, const Sentinel& end) + : begin_(begin) + , end_(end) {} //! Default constructor, relies on iterators being default-constructible. @@ -45,19 +52,19 @@ namespace Dune { //! Returns an iterator pointing to the begin of the range. iterator begin() const { - return _begin; + return begin_; } //! Returns an iterator pointing past the end of the range. - iterator end() const + sentinel end() const { - return _end; + return end_; } private: - Iterator _begin; - Iterator _end; + Iterator begin_; + Sentinel end_; }; diff --git a/dune/common/test/rangeutilitiestest.cc b/dune/common/test/rangeutilitiestest.cc index 729a6ed8ad6453a7c3acd4b11f6ef7d5bc708602..a4847f0bbf753afcc88d89783999a1967d49ecbb 100644 --- a/dune/common/test/rangeutilitiestest.cc +++ b/dune/common/test/rangeutilitiestest.cc @@ -92,7 +92,7 @@ auto checkSameRange(R1&& r1, BeginIt2&& it2, EndIt2&& end2) { auto it1 = r1.begin(); auto end1 = r1.end(); - for(; (it1 < end1) and (it2 < end2); ++it1, ++it2) + for(; (it1 != end1) and (it2 != end2); ++it1, ++it2) if (*it1 != *it2) return false; if ((it1 != end1) or (it2 != end2)) @@ -387,11 +387,64 @@ auto testSparseRange() +// An empty class tagging the end of a range +class SentinelIterator +{}; + +// Classical use case for sentinels: An iterator for +// a null-terminated string. Using a sentinel allows +// to implement a range without a linear scan to find +// the end or storing an additional isEnd flag in the +// iterator. +class NullTerminatedStringIterator +{ +public: + NullTerminatedStringIterator(char* p) + : p_(p) + {} + + NullTerminatedStringIterator& operator++() + { + ++p_; + return *this; + } + + char& operator*() + { + return *p_; + } + + friend bool operator==(const NullTerminatedStringIterator& it, const SentinelIterator& end) + { + return (*it.p_) == 0; + } + +private: + char* p_; +}; + + + +Dune::TestSuite testIteratorRange() +{ + Dune::TestSuite suite("Check IteratorRange with sentinel"); + + // Check sentinel terminated range for null-terminated string + char testStr[] = "FooBar"; + auto range_sentinel = Dune::IteratorRange(NullTerminatedStringIterator(testStr), SentinelIterator()); + suite.check(checkSameRange(range_sentinel, std::string_view(testStr))); + + return suite; +} + + + int main() { // Check IsIterable<> for https://gitlab.dune-project.org/core/dune-common/issues/58 static_assert(Dune::IsIterable< std::array<int, 3> >::value, "std::array<int> must be a range"); static_assert(Dune::IsIterable< Dune::IteratorRange<int*> >::value, "IteratorRange must be a range"); +// static_assert(Dune::IsIterable< Dune::IteratorRange<int*, void*> >::value, "IteratorRange must be a range"); static_assert(!Dune::IsIterable< int >::value, "int must not be a range"); Dune::TestSuite suite; @@ -505,6 +558,8 @@ int main() suite.subTest(testSparseRange()); + suite.subTest(testIteratorRange()); + return suite.exit(); }