diff --git a/include/boost/multi/detail/index_range.hpp b/include/boost/multi/detail/index_range.hpp index efc52c0f0..21c0682a5 100644 --- a/include/boost/multi/detail/index_range.hpp +++ b/include/boost/multi/detail/index_range.hpp @@ -178,7 +178,7 @@ class range { ++curr_; return *this; } - constexpr auto operator--() -> const_iterator& { + constexpr auto operator--() noexcept(noexcept(--curr_)) -> const_iterator& { --curr_; return *this; } @@ -344,8 +344,17 @@ struct extension_t : public range { BOOST_MULTI_HD constexpr extension_t(IndexTypeLast last) noexcept // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) // NOSONAR(cpp:S1709) allow terse syntax : range(IndexType{}, IndexType{} + last) {} + extension_t(extension_t const&) = default; + extension_t(extension_t&&) = default; + + auto operator=(extension_t const&) -> extension_t& = default; + auto operator=(extension_t&&) -> extension_t& = default; + + ~extension_t() = default; + template< class OtherExtension, + std::enable_if_t, int> =0, // NOLINT(modernize-use-constraints,modernize-type-traits) decltype( detail::implicit_cast(std::declval().first()), detail::implicit_cast(std::declval().last()) @@ -365,7 +374,9 @@ struct extension_t : public range { // BOOST_MULTI_HD constexpr explicit extension_t(OtherExtension const& other) noexcept // : extension_t{other.first(), other.last()} {} - template + template, int> =0 // NOLINT(modernize-use-constraints,modernize-type-traits) + > BOOST_MULTI_HD constexpr auto operator=(OtherExtension const& other) -> extension_t& { (*this) = extension_t{other}; return *this; diff --git a/include/boost/multi/detail/layout.hpp b/include/boost/multi/detail/layout.hpp index 121a888f5..686571fe9 100644 --- a/include/boost/multi/detail/layout.hpp +++ b/include/boost/multi/detail/layout.hpp @@ -123,7 +123,7 @@ class f_extensions_t { } } - class iterator { + class iterator : public weakly_incrementable_facade, weakly_decrementable_facade { // NOLINT(fuchsia-multiple-inheritance) typename extensions_t::iterator it_; Proj proj_; @@ -132,14 +132,21 @@ class f_extensions_t { friend f_extensions_t; public: + iterator() = default; + using iterator_category = std::random_access_iterator_tag; - auto operator++() -> auto& { ++it_; return *this; } - auto operator--() -> auto& { --it_; return *this; } + constexpr auto operator++() -> auto& { ++it_; return *this; } + constexpr auto operator--() -> auto& { --it_; return *this; } constexpr auto operator+=(difference_type dd) -> auto& { it_+=dd; return *this; } constexpr auto operator-=(difference_type dd) -> auto& { it_-=dd; return *this; } + constexpr auto operator+(difference_type dd) const { return iterator{*this} += dd; } + constexpr auto operator-(difference_type dd) const { return iterator{*this} -= dd; } + + friend constexpr auto operator+(difference_type dd, iterator const& self) { return self + dd; } + friend constexpr auto operator-(iterator const& self, iterator const& other) { return self.it_ - other.it_; } friend constexpr auto operator==(iterator const& self, iterator const& other) -> bool { return self.it_ == other.it_; } @@ -147,10 +154,12 @@ class f_extensions_t { friend auto operator<=(iterator const& self, iterator const& other) -> bool { return self.it_ <= other.it_; } friend auto operator< (iterator const& self, iterator const& other) -> bool { return self.it_ < other.it_; } + friend auto operator> (iterator const& self, iterator const& other) -> bool { return self.it_ > other.it_; } + friend auto operator>=(iterator const& self, iterator const& other) -> bool { return self.it_ >= other.it_; } constexpr auto operator*() const -> decltype(auto) { using std::get; - if constexpr(D != 1) { + if constexpr(D > 1) { auto ll = [idx = get<0>(*it_), proj = proj_](auto... rest) { return proj(idx, rest...); }; return f_extensions_t(extensions_t(get<1>(*it_).base().tail()), ll); } else { @@ -158,6 +167,8 @@ class f_extensions_t { } } + using value_type = int; // decltype(*std::declval()); + auto operator[](difference_type dd) const { return *((*this) + dd); } // TODO(correaa) use ra_iterator_facade }; @@ -239,7 +250,7 @@ struct extensions_t : boost::multi::detail::tuple_prepend_t::element>; - extensions_t() = default; + constexpr extensions_t() = default; template = 0> // NOLINT(modernize-use-constraints) TODO(correaa) // cppcheck-suppress noExplicitConstructor ; to allow passing tuple // NOLINTNEXTLINE(runtime/explicit) @@ -387,12 +398,12 @@ struct extensions_t : boost::multi::detail::tuple_prepend_t BOOST_MULTI_HD constexpr auto operator()(index idx, Indices... rest) const { return to_linear(idx, rest...); } - class iterator { + class iterator : weakly_incrementable_facade, weakly_decrementable_facade { // NOLINT(fuchsia-multiple-inheritance,cppcoreguidelines-pro-type-member-init,hicpp-member-init) index idx_; extensions_t rest_; friend extensions_t; - iterator(index idx, extensions_t rest) : idx_{idx}, rest_{rest} {} + constexpr iterator(index idx, extensions_t rest) : idx_{idx}, rest_{rest} {} public: using difference_type = index; @@ -401,6 +412,11 @@ struct extensions_t : boost::multi::detail::tuple_prepend_t iterator& { idx_ += d; return *this; } + constexpr auto operator-=(difference_type d) -> iterator& { idx_ -= d; return *this; } + constexpr auto operator+(difference_type d) const { return iterator{idx_ + d, rest_}; } constexpr auto operator-(difference_type d) const { return iterator{idx_ - d, rest_}; } @@ -683,6 +699,10 @@ template<> struct extensions_t<0> : tuple<> { : base_{tup} {} extensions_t() = default; + // template + // cppcheck-suppress noExplicitConstructor ; to allow passing tuple // NOLINTNEXTLINE(runtime/explicit) + // BOOST_MULTI_HD explicit constexpr extensions_t(tuple<> /*extensions*/) // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) + // : base_{} {} BOOST_MULTI_HD constexpr auto base() const& -> base_ const& { return *this; } BOOST_MULTI_HD constexpr auto base() & -> base_& { return *this; } @@ -811,6 +831,45 @@ template<> struct extensions_t<1> : tuple { return elements_t{get<0>(static_cast const&>(*this))}; } + class iterator : weakly_incrementable_facade, weakly_decrementable_facade { // NOLINT(fuchsia-multiple-inheritance) + index idx_; + extensions_t<0> rest_; + friend extensions_t; + + constexpr iterator(index idx, extensions_t<0> rest) : idx_{idx}, rest_{rest} {} + + public: + using difference_type = index; + using value_type = decltype(ht_tuple(std::declval(), std::declval>().base())); + using pointer = void; + using reference = value_type; + using iterator_category = std::random_access_iterator_tag; + + iterator() = default; + + constexpr auto operator+=(difference_type d) { idx_+=d; return *this; } + constexpr auto operator-=(difference_type d) { idx_-=d; return *this; } + + constexpr auto operator+(difference_type d) const { return iterator{idx_ + d, rest_}; } + constexpr auto operator-(difference_type d) const { return iterator{idx_ - d, rest_}; } + + friend constexpr auto operator-(iterator const& self, iterator const& other) -> difference_type { return self.idx_ - other.idx_; } + + constexpr auto operator++() -> auto& { ++idx_; return *this; } + constexpr auto operator--() -> auto& { --idx_; return *this; } + + constexpr auto operator*() const { + // multi::detail::what(rest_); + return ht_tuple(idx_, rest_.base()); + } + + friend constexpr auto operator==(iterator const& self, iterator const& other) { assert( self.rest_ == other.rest_ ); return self.idx_ == other.idx_; } + friend constexpr auto operator!=(iterator const& self, iterator const& other) { assert( self.rest_ == other.rest_ ); return self.idx_ != other.idx_; } + }; + + constexpr auto begin() const noexcept { return iterator{this->base().head().first(), extensions_t<0>(this->base().tail())}; } + constexpr auto end() const noexcept { return iterator{this->base().head().last() , extensions_t<0>(this->base().tail())}; } + constexpr auto size() const { using std::get; return get<0>(static_cast const&>(*this)).size(); diff --git a/include/boost/multi/detail/operators.hpp b/include/boost/multi/detail/operators.hpp index f66fe2ee3..00e18ed88 100644 --- a/include/boost/multi/detail/operators.hpp +++ b/include/boost/multi/detail/operators.hpp @@ -121,6 +121,22 @@ struct weakly_incrementable { // friend T& operator++(weakly_incrementable& t){return ++static_cast(t);} }; +template class default_initializable_facade { + default_initializable_facade() = default; +}; + +template class weakly_incrementable_facade : selfable { + weakly_incrementable_facade() = default; + friend Self; + friend constexpr auto operator++(Self& self, int) -> Self { Self ret = self; ++self; return ret; } +}; + +template class weakly_decrementable_facade : selfable { + weakly_decrementable_facade() = default; + friend Self; + friend constexpr auto operator--(Self& self, int) -> Self { Self ret = self; --self; return ret; } +}; + template struct weakly_decrementable { protected: diff --git a/test/extensions.cpp b/test/extensions.cpp index 94c9c5973..83adf07cd 100644 --- a/test/extensions.cpp +++ b/test/extensions.cpp @@ -7,13 +7,16 @@ #include // IWYU pragma: keep -#include // IWYU pragma: keep // for std::equal +#include // IWYU pragma: keep // for std::equal +#include #include // IWYU pragma: keep #include // for std::is_same_v // IWYU pragma: no_include // for get, iwyu bug #if defined(__cplusplus) && (__cplusplus >= 202002L) -#include // IWYU pragma: keep // NOLINT(misc-include-cleaner) +#include // for default_initializable +#include // for reverse_iterator, input... +#include // IWYU pragma: keep // NOLINT(misc-include-cleaner) #endif namespace multi = boost::multi; @@ -448,18 +451,117 @@ auto main() -> int { // NOLINT(bugprone-exception-escape,readability-function-c } #endif } + { + auto xs2D = multi::extensions_t(10, 20); + BOOST_TEST( xs2D.size() == 10 ); + BOOST_TEST( xs2D.num_elements() == 200 ); + + using std::get; + BOOST_TEST( get<0>(xs2D[3][5]) == 3 ); + BOOST_TEST( get<0>(xs2D[4][5]) == 4 ); + + BOOST_TEST( get<0>((* xs2D.begin() )[5]) == 0 ); + BOOST_TEST( get<0>((*(xs2D.end()-1))[5]) == 9 ); + +#if !defined(__NVCC__) && !defined(__NVCOMPILER) // this CTAD gives a compile error in nvhpc 22.7 and nvcc 12.0 + multi::extensions_t const xs2D_copy(xs2D); +#else + multi::extensions_t<1> const xs2D_copy(xs2D); +#endif + BOOST_TEST( xs2D_copy == xs2D ); + + boost::multi::extensions_t<2>::iterator beg = xs2D.begin(); + beg++; + + static_assert(std::is_constructible_v::iterator, boost::multi::extensions_t<1>::iterator>); + +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) && !defined(_MSC_VER) + static_assert(std::weakly_incrementable::iterator>); + + BOOST_TEST( get<0>(*std::ranges::begin(xs2D)) == 0 ); + BOOST_TEST( get<0>(*(std::ranges::end(xs2D)-1)) == 9 ); + + static_assert(std::ranges::range&>>); + + BOOST_TEST( get<0>((*(xs2D.end()-1))[5]) == 9 ); + + static_assert(std::ranges::bidirectional_range>); + + auto rxs2D = xs2D | std::views::reverse; + + BOOST_TEST( get<0>((*std::ranges::begin(rxs2D))[5]) == 9 ); + BOOST_TEST( get<0>((*(std::ranges::end(rxs2D)-1))[5]) == 0 ); +#endif + } { auto xs1D = multi::extensions_t(10); BOOST_TEST( xs1D.size() == 10 ); using std::get; BOOST_TEST( get<0>(xs1D[3]) == 3 ); + BOOST_TEST( get<0>(xs1D[4]) == 4 ); + + std::cout << "line 500: lhs " << get<0>(*xs1D.begin()) << '\n'; + BOOST_TEST( get<0>(*xs1D.begin()) == 0 ); - auto v1D = [](auto ii) { return ii * ii; } ^ multi::extensions_t(10); + auto end = xs1D.end(); + auto endm1 = end - 1; + auto [ii] = *endm1; + std::cout << "line 501: lhs " <(*endm1) << '\n'; + BOOST_TEST( get<0>(*endm1) == 9 ); + + multi::extensions_t<1> const xs1D_copy(xs1D); + BOOST_TEST( xs1D_copy == xs1D ); + + multi::extensions_t<1>::iterator const copy(xs1D.begin()); + BOOST_TEST( copy == xs1D.begin() ); + + static_assert(std::is_constructible_v::iterator, boost::multi::extensions_t<1>::iterator>); + +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) && !defined(_MSC_VER) + BOOST_TEST( get<0>(*std::ranges::begin(xs1D)) == 0 ); + + BOOST_TEST( get<0>(*(std::ranges::end(xs1D)-1)) == 9 ); + + static_assert(std::ranges::range&>>); + + BOOST_TEST( get<0>(*(xs1D.end()-1)) == 9 ); + + static_assert(std::ranges::bidirectional_range>); + + auto rxs1D = xs1D | std::views::reverse; + + BOOST_TEST( get<0>(*std::ranges::begin(rxs1D)) == 9 ); + BOOST_TEST( get<0>(*(std::ranges::end(rxs1D)-1)) == 0 ); +#endif + + auto const v1D = [](auto idx) { return idx * idx; } ^ multi::extensions_t(10); BOOST_TEST( v1D.size() == 10 ); BOOST_TEST( v1D.elements().size() == 10 ); + BOOST_TEST( v1D[0] == 0); BOOST_TEST( v1D[4] == 16 ); + BOOST_TEST( v1D[9] == 81 ); + BOOST_TEST( *v1D.begin() == 0 ); + BOOST_TEST( *(v1D.end() - 1) == 81 ); + #if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) && !defined(_MSC_VER) +#if !defined(__NVCC__) && !defined(__NVCOMPILER) // produces an error: ‘boost::multi::f_extensions_t::proj_’ has incomplete type + static_assert(std::input_or_output_iterator); + static_assert(std::default_initializable); + static_assert(std::semiregular); +#endif + BOOST_TEST( std::ranges::begin(v1D) == v1D.begin() ); + BOOST_TEST( std::ranges::end(v1D) == v1D.end() ); + + static_assert(std::ranges::bidirectional_range); + static_assert(std::ranges::random_access_range); + auto rv1D = v1D | std::views::reverse; + + BOOST_TEST( rv1D[0] == 81); + BOOST_TEST( rv1D[9] == 0 ); #endif } return boost::report_errors(); diff --git a/test/index_range.cpp b/test/index_range.cpp index 7ba4a57bf..063de96eb 100644 --- a/test/index_range.cpp +++ b/test/index_range.cpp @@ -13,6 +13,10 @@ // IWYU pragma: no_include // for tuple_element<>::type #include +#if defined(__cplusplus) && (__cplusplus >= 202002L) +#include // IWYU pragma: keep // NOLINT(misc-include-cleaner) +#endif + namespace multi = boost::multi; auto main() -> int { // NOLINT(readability-function-cognitive-complexity,bugprone-exception-escape) @@ -259,5 +263,27 @@ auto main() -> int { // NOLINT(readability-function-cognitive-complexity,bugpro BOOST_TEST( sum == 5 + 6 + 7 + 8 + 9 + 10 + 11 ); } + { + multi::extension_t const ext(5); + + BOOST_TEST( *ext.begin() == 0 ); + BOOST_TEST( *(ext.end() - 1) == 4 ); +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) && !defined(_MSC_VER) + + BOOST_TEST( *std::ranges::begin(ext) == 0 ); + BOOST_TEST( *(std::ranges::end(ext)-1) == 4 ); + + // static_assert(std::ranges::range&>>); + + // BOOST_TEST( get<0>(*(xs1D.end()-1)) == 9 ); + + BOOST_TEST( ext[1] == 1 ); + + auto rext = ext | std::views::reverse; + + BOOST_TEST( rext[1] == 3 ); +#endif + } + return boost::report_errors(); }