diff --git a/include/boost/multi/array.hpp b/include/boost/multi/array.hpp index 742f90e3e..dd4403833 100644 --- a/include/boost/multi/array.hpp +++ b/include/boost/multi/array.hpp @@ -228,9 +228,9 @@ struct static_array // NOLINT(fuchsia-multiple-inheritance) : multiple inherita template>::difference_type> constexpr explicit static_array(It first, It last, allocator_type const& alloc) - : array_alloc{alloc}, ref{ + : array_alloc{alloc}, ref( array_alloc::allocate(static_cast::size_type>(layout_type{index_extension(adl_distance(first, last)) * multi::extensions(*first)}.num_elements())), - index_extension(adl_distance(first, last)) * multi::extensions(*first)} { + index_extension(adl_distance(first, last)) * multi::extensions(*first)) { #if defined(__clang__) && defined(__CUDACC__) // TODO(correaa) add workaround for non-default constructible type and use adl_alloc_uninitialized_default_construct_n if constexpr(! std::is_trivially_default_constructible_v && ! multi::force_element_trivial_default_construction ) { diff --git a/include/boost/multi/array_ref.hpp b/include/boost/multi/array_ref.hpp index 71d1f5adb..4ca25ef77 100644 --- a/include/boost/multi/array_ref.hpp +++ b/include/boost/multi/array_ref.hpp @@ -87,10 +87,10 @@ namespace boost::multi { template> struct const_subarray; -template::difference_type>> +template::difference_type>> class subarray; -template::difference_type>> +template::difference_type>> class move_subarray; template @@ -2698,7 +2698,8 @@ struct const_subarray // NOLINT(fuchsia-multiple-inhe public: constexpr auto broadcasted() const& { - multi::layout_t<2> const new_layout{this->layout(), 0, 0, (std::numeric_limits::max)()}; + multi::layout_t<1> const self_layout{this->layout()}; + multi::layout_t<2> const new_layout{self_layout, 0, 0, (std::numeric_limits::max)()}; return const_subarray{new_layout, types::base_}; } @@ -2814,13 +2815,14 @@ struct const_subarray // NOLINT(fuchsia-multiple-inhe #endif BOOST_MULTI_HD constexpr auto sliced_aux_(index first, index last) const { - typename types::layout_t new_layout = this->layout(); - if(this->is_empty()) { - assert(first == last); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay,hicpp-no-array-decay) : normal in a constexpr function - new_layout.nelems() = 0; // TODO(correaa) : don't use mutation - } else { - (new_layout.nelems() /= this->size())*=(last - first); - } + auto const new_layout = typename types::layout_t{ + this->layout().sub(), + this->layout().stride(), + this->layout().offset(), + (this->is_empty())? + 0: + this->layout().nelems() / this->size() * (last - first) + }; return const_subarray{new_layout, this->base_ + (first*this->layout().stride() - this->layout().offset())}; } @@ -3156,11 +3158,11 @@ struct const_subarray // NOLINT(fuchsia-multiple-inhe public: template::template rebind> - constexpr auto static_array_cast() const -> subarray { // name taken from std::static_pointer_cast + constexpr auto static_array_cast() const -> subarray { // name taken from std::static_pointer_cast return {this->layout(), static_cast(this->base_)}; } template::template rebind, class... Args> - constexpr auto static_array_cast(Args&&... args) const -> subarray { // name taken from std::static_pointer_cast + constexpr auto static_array_cast(Args&&... args) const -> subarray { // name taken from std::static_pointer_cast return {this->layout(), P2{this->base_, std::forward(args)...}}; } @@ -3278,16 +3280,30 @@ constexpr auto static_array_cast(Array&& self, Args&&... args) -> decltype(auto) } template -struct array_ref // TODO(correaa) : inheredit from multi::partially_ordered2, void>? -: subarray +class array_ref : public subarray< + T, D, ElementPtr, + std::conditional_t<(D == 1), + // continuous_layout<1, typename std::pointer_traits::difference_type>, + layout_t::difference_type>, + layout_t::difference_type> + > +> { - ~array_ref() = default; // lints(cppcoreguidelines-special-member-functions) + using subarray_base = subarray< + T, D, ElementPtr, + std::conditional_t<(D == 1), + // continuous_layout<1, typename std::pointer_traits::difference_type>, + layout_t::difference_type>, + layout_t::difference_type> + > + >; - using layout_type = typename array_ref::types::layout_t; + public: + ~array_ref() = default; // lints(cppcoreguidelines-special-member-functions) - using iterator = typename subarray::iterator; + using layout_type = typename subarray_base::layout_t; + using iterator = typename subarray_base::iterator; - public: constexpr // attempt for MSVC array_ref() = delete; // because reference cannot be unbound @@ -3315,21 +3331,21 @@ struct array_ref // TODO(correaa) : inheredit from multi::partially_ordered2{}>, decltype(multi::detail::explicit_cast(std::declval()))* = nullptr> constexpr explicit array_ref(array_ref&& other) - : subarray{other.layout(), ElementPtr{std::move(other).base()}} {} // cppcheck-suppress internalAstError ; bug in cppcheck 2.13.0 + : subarray_base(other.layout(), ElementPtr{std::move(other).base()}) {} // cppcheck-suppress internalAstError ; bug in cppcheck 2.13.0 template{}>, decltype(multi::detail::implicit_cast(std::declval()))* = nullptr> // cppcheck-suppress noExplicitConstructor ; to allow terse syntax constexpr /*implicit*/ array_ref(array_ref&& other) // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) - : subarray{other.layout(), ElementPtr{std::move(other).base()}} {} + : subarray_base(other.layout(), ElementPtr{std::move(other).base()}) {} constexpr array_ref(ElementPtr dat, ::boost::multi::extensions_t const& xs) /*noexcept*/ // TODO(correa) eliminate this ctor - : subarray{typename subarray::types::layout_t(xs), dat} {} + : subarray_base(typename subarray_base::types::layout_t(xs), dat) {} // constexpr array_ref(typename array_ref::extensions_type extensions, typename array_ref::element_ptr dat) noexcept // : subarray{typename array_ref::types::layout_t{extensions}, dat} {} constexpr array_ref(::boost::multi::extensions_t exts, ElementPtr dat) noexcept - : subarray{typename array_ref::types::layout_t(exts), dat} {} + : subarray_base{typename array_ref::types::layout_t(exts), dat} {} template< class Array, @@ -3373,7 +3389,7 @@ struct array_ref // TODO(correaa) : inheredit from multi::partially_ordered2 il) : array_ref(il.begin(), typename array_ref::extensions_type{static_cast(il.size())}) {} - using subarray::operator=; + using subarray_base::operator=; private: template constexpr auto copy_elements_(It first) { @@ -3418,10 +3434,8 @@ struct array_ref // TODO(correaa) : inheredit from multi::partially_ordered2 -// constexpr auto operator=(array_ref const& other)& -> array_ref& { assert( this->extensions() == other.extensions() ); - // MULTI_MARK_SCOPE(std::string{"multi::operator= D="}+std::to_string(D)+" from "+typeid(TT).name()+" to "+typeid(T).name() ); adl_copy_n(other.data_elements(), other.num_elements(), this->data_elements()); return *this; } @@ -3563,7 +3577,7 @@ struct array_ref // TODO(correaa) : inheredit from multi::partially_ordered2 auto serialize_structured_(Ar& arxiv, unsigned int const version) { - subarray::serialize(arxiv, version); + subarray_base::serialize(arxiv, version); } template auto serialize_flat_(Archive& arxiv, unsigned int const /*version*/) { @@ -3618,7 +3632,7 @@ struct array_ptr using basic_ptr = subarray_ptr::layout_t>; constexpr array_ptr(Ptr data, multi::extensions_t extensions) - : basic_ptr{data, multi::layout_t{extensions}} {} + : basic_ptr{data, typename array_ref::layout_t(extensions)} {} constexpr explicit array_ptr(std::nullptr_t nil) : array_ptr{nil, multi::extensions_t{}} {} diff --git a/include/boost/multi/detail/layout.hpp b/include/boost/multi/detail/layout.hpp index 5aa94da0e..468cc91b0 100644 --- a/include/boost/multi/detail/layout.hpp +++ b/include/boost/multi/detail/layout.hpp @@ -503,6 +503,78 @@ struct monostate : equality_comparable { friend BOOST_MULTI_HD constexpr auto operator==(monostate const& /*self*/, monostate const& /*other*/) {return true;} }; +template +class continuous_layout; + +template +class continuous_layout<1, SSize> { + public: + continuous_layout() = default; + + using dimensionality_type = multi::dimensionality_t; + using rank = std::integral_constant; + static constexpr dimensionality_type rank_v = rank::value; + static constexpr dimensionality_type dimensionality = rank_v; // TODO(correaa) : consider deprecation + + using difference_type = SSize; + using size_type = difference_type; + + using index = difference_type; + using index_range = multi::range; + using index_extension = multi::extension_t; + using extension_type = index_extension; + + constexpr auto extension() const { return extension_type{offset_, offset_ + nelems_}; } + + using extensions_type = multi::index_extensions<1>; + auto extensions() const -> extensions_type { return extensions_type{extension()}; }; + + constexpr auto is_empty() const -> bool { return false; } + constexpr auto empty() const -> bool { return is_empty(); } + + auto is_compact() const = delete; + + auto sub() const { return layout_t<0, SSize>{}; } + + using stride_type = std::integral_constant; + auto stride() const -> stride_type { return {}; } + + using num_elements_type = size_type; + auto num_elements() const -> num_elements_type { return nelems_; } + + using offset_type = difference_type; + auto offset() const -> offset_type { return offset_; } + + using offsets_type = void; + auto offsets() const -> offsets_type; + + using strides_type = multi::tuple >; + auto strides() const -> stride_type; + + auto size() const -> size_type { return nelems_; } + + using nelems_type = size_type; + constexpr auto nelems() const -> nelems_type { return nelems_; } + + using sizes_type = void; + auto sizes() const -> sizes_type = delete; + + constexpr auto operator()(index const& idx) const -> difference_type { return idx - offset_; } + + private: + // BOOST_MULTI_NO_UNIQUE_ADDRESS sub_type sub_ ; + // BOOST_MULTI_NO_UNIQUE_ADDRESS stride_type stride_; // = {}; + offset_type offset_; + nelems_type nelems_; + + public: + constexpr explicit continuous_layout(extensions_type const& xs) + : offset_{xs.get<0>().front()}, + nelems_{xs.get<0>().size()} {} + + constexpr auto reindex(index idx) -> auto& { offset_ = idx * stride(); return *this; } +}; + template struct layout_t<0, SSize> : multi::equality_comparable > @@ -567,7 +639,7 @@ struct layout_t<0, SSize> [[nodiscard]] constexpr auto offsets() const {return offsets_type{};} [[nodiscard]] constexpr auto nelemss() const {return nelemss_type{};} - constexpr auto operator()() const {return offset_;} + constexpr auto operator()() const { return offset_; } // constexpr explicit operator offset_type() const {return offset_;} constexpr auto stride() const -> stride_type = delete; @@ -666,6 +738,20 @@ struct layout_t public: layout_t() = default; + template< + class OtherLayout, + class = decltype(sub_type {std::declval().sub()}), + class = decltype(stride_type{std::declval().stride()}), + class = decltype(offset_type{std::declval().offset()}), + class = decltype(nelems_type{std::declval().nelems()}) + > + BOOST_MULTI_HD constexpr explicit layout_t(OtherLayout const& other) : + sub_{other.sub()}, + stride_{other.stride()}, + offset_{other.offset()}, + nelems_{other.nelems()} + {} + BOOST_MULTI_HD constexpr explicit layout_t(extensions_type const& extensions) : sub_{ std::apply( @@ -705,9 +791,9 @@ struct layout_t constexpr auto operator[](index idx) const {return at_aux_(idx);} template - constexpr auto operator()(index idx, Indices... rest) const {return operator[](idx)(rest...);} - constexpr auto operator()(index idx) const {return at_aux_(idx);} - constexpr auto operator()() const {return *this;} + constexpr auto operator()(index idx, Indices... rest) const { return operator[](idx)(rest...); } + constexpr auto operator()(index idx) const { return at_aux_(idx); } + constexpr auto operator()() const { return *this; } BOOST_MULTI_HD constexpr auto sub() & -> sub_type & {return sub_ ;} BOOST_MULTI_HD constexpr auto sub() const& -> sub_type const& {return sub_ ;} diff --git a/include/boost/multi/detail/tuple_zip.hpp b/include/boost/multi/detail/tuple_zip.hpp index 44c83369b..80c4fcffb 100644 --- a/include/boost/multi/detail/tuple_zip.hpp +++ b/include/boost/multi/detail/tuple_zip.hpp @@ -14,7 +14,8 @@ namespace boost::multi { // NOLINT(modernize-concat-nested-namespaces) keep c++14 compat namespace detail { -template class tuple; +// we need a custom tuple type, so some fundamental types (e.g. iterators) are trivially constructible +template class tuple; // TODO(correaa) consider renaming it to `tpl` template<> class tuple<> { // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) public: @@ -57,6 +58,10 @@ template class tuple : tuple { // NOLI // cppcheck-suppress noExplicitConstructor ; allow bracket init in function argument // NOLINTNEXTLINE(runtime/explicit) constexpr tuple(T0 head, Ts... tail) : tail_type{tail...}, head_{head} {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) to allow bracket function calls + // cppcheck-suppress noExplicitConstructor ; allow bracket init in function argument // NOLINTNEXTLINE(runtime/explicit) + template =0> + constexpr tuple(TT0 head) : tail_type{}, head_{head} {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) to allow bracket function calls + // cppcheck-suppress noExplicitConstructor ; allow bracket init in function argument // NOLINTNEXTLINE(runtime/explicit) constexpr explicit tuple(::std::tuple other) : tuple(::std::apply([](auto... es) {return tuple(es...);}, other)) {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) diff --git a/include/boost/multi/detail/types.hpp b/include/boost/multi/detail/types.hpp index 952a38bad..5a71bb454 100644 --- a/include/boost/multi/detail/types.hpp +++ b/include/boost/multi/detail/types.hpp @@ -1,4 +1,4 @@ -// Copyright 2018-2024 Alfredo A. Correa +// Copyright 2018-2025 Alfredo A. Correa // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -16,7 +16,8 @@ using size_type = std::make_signed_t; using index = std::make_signed_t; using difference_type = std::make_signed_t; -using dimensionality_type = index; +using dimensionality_t = index; +using dimensionality_type = dimensionality_t; } // end namespace boost::multi #endif // BOOST_MULTI_DETAIL_TYPES_HPP diff --git a/test/array_ref.cpp b/test/array_ref.cpp index 96c5c85f7..4b9282d11 100644 --- a/test/array_ref.cpp +++ b/test/array_ref.cpp @@ -136,6 +136,22 @@ inline auto trace_separate_sub(multi::subarray const& arr) -> int { } // end unnamed namespace auto main() -> int { // NOLINT(readability-function-cognitive-complexity,bugprone-exception-escape) + +{ + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) for test + int icarr[5] = {}; + multi::array_ref const iarrr(std::data(icarr), 5); + static_assert( std::is_base_of_v< + std::random_access_iterator_tag, + std::iterator_traits::iterator>::iterator_category + >); + + // static_assert( std::is_base_of_v< + // std::contiguous_iterator_tag, + // std::iterator_traits::iterator>::iterator_category + // >); +} + BOOST_AUTO_TEST_CASE(array_ref_from_carray) { #if defined(__clang__) #pragma clang diagnostic push @@ -143,7 +159,7 @@ BOOST_AUTO_TEST_CASE(array_ref_from_carray) { #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" #endif - // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): test + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) for test int arr[4][5] = { { 0, 10, 20, 30, 40}, { 50, 60, 70, 80, 90}, diff --git a/test/element_access.cpp b/test/element_access.cpp index 651cf533a..c70a2823f 100644 --- a/test/element_access.cpp +++ b/test/element_access.cpp @@ -148,7 +148,7 @@ auto main() -> int { // NOLINT(readability-function-cognitive-complexity,bugpro BOOST_TEST( end1 == beg1 ); for(; end1 != end2; ++end1) { // NOLINT(altera-id-dependent-backward-branch,altera-unroll-loops) - } + } BOOST_TEST( end1 == end2 ); }