Skip to content

Commit

Permalink
Merge branch 'layout_compact' into 'master'
Browse files Browse the repository at this point in the history
introduce continuous_layout

See merge request correaa/boost-multi!1313
  • Loading branch information
correaa committed Jan 3, 2025
2 parents 92a7426 + 91346e8 commit d49bd5a
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 38 deletions.
4 changes: 2 additions & 2 deletions include/boost/multi/array.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,9 @@ struct static_array // NOLINT(fuchsia-multiple-inheritance) : multiple inherita

template<class It, class = typename std::iterator_traits<std::decay_t<It>>::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<typename multi::allocator_traits<allocator_type>::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<typename static_array::element_type> && ! multi::force_element_trivial_default_construction<typename static_array::element_type> ) {
Expand Down
68 changes: 41 additions & 27 deletions include/boost/multi/array_ref.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ namespace boost::multi {
template<typename T, dimensionality_type D, typename ElementPtr = T*, class Layout = layout_t<D>>
struct const_subarray;

template<typename T, multi::dimensionality_type D, typename ElementPtr = T*, class Layout = layout_t<D, typename std::pointer_traits<ElementPtr>::difference_type>>
template<typename T, dimensionality_type D, typename ElementPtr = T*, class Layout = layout_t<D, typename std::pointer_traits<ElementPtr>::difference_type>>
class subarray;

template<typename T, multi::dimensionality_type D, typename ElementPtr = T*, class Layout = layout_t<D, typename std::pointer_traits<ElementPtr>::difference_type>>
template<typename T, dimensionality_type D, typename ElementPtr = T*, class Layout = layout_t<D, typename std::pointer_traits<ElementPtr>::difference_type>>
class move_subarray;

template<typename T, dimensionality_type D, typename ElementPtr, class Layout>
Expand Down Expand Up @@ -2698,7 +2698,8 @@ struct const_subarray<T, 1, ElementPtr, Layout> // NOLINT(fuchsia-multiple-inhe

public:
constexpr auto broadcasted() const& {
multi::layout_t<2> const new_layout{this->layout(), 0, 0, (std::numeric_limits<size_type>::max)()};
multi::layout_t<1> const self_layout{this->layout()};
multi::layout_t<2> const new_layout{self_layout, 0, 0, (std::numeric_limits<size_type>::max)()};
return const_subarray<T, 2, typename const_subarray::element_const_ptr>{new_layout, types::base_};
}

Expand Down Expand Up @@ -2814,13 +2815,14 @@ struct const_subarray<T, 1, ElementPtr, Layout> // 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())};
}
Expand Down Expand Up @@ -3156,11 +3158,11 @@ struct const_subarray<T, 1, ElementPtr, Layout> // NOLINT(fuchsia-multiple-inhe

public:
template<class T2, class P2 = typename std::pointer_traits<element_ptr>::template rebind<T2>>
constexpr auto static_array_cast() const -> subarray<T2, 1, P2> { // name taken from std::static_pointer_cast
constexpr auto static_array_cast() const -> subarray<T2, 1, P2, Layout> { // name taken from std::static_pointer_cast
return {this->layout(), static_cast<P2>(this->base_)};
}
template<class T2, class P2 = typename std::pointer_traits<element_ptr>::template rebind<T2>, class... Args>
constexpr auto static_array_cast(Args&&... args) const -> subarray<T2, 1, P2> { // name taken from std::static_pointer_cast
constexpr auto static_array_cast(Args&&... args) const -> subarray<T2, 1, P2, Layout> { // name taken from std::static_pointer_cast
return {this->layout(), P2{this->base_, std::forward<Args>(args)...}};
}

Expand Down Expand Up @@ -3278,16 +3280,30 @@ constexpr auto static_array_cast(Array&& self, Args&&... args) -> decltype(auto)
}

template<typename T, dimensionality_type D, typename ElementPtr = T*>
struct array_ref // TODO(correaa) : inheredit from multi::partially_ordered2<array_ref<T, D, ElementPtr>, void>?
: subarray<T, D, ElementPtr>
class array_ref : public subarray<
T, D, ElementPtr,
std::conditional_t<(D == 1),
// continuous_layout<1, typename std::pointer_traits<ElementPtr>::difference_type>,
layout_t<D, typename std::pointer_traits<ElementPtr>::difference_type>,
layout_t<D, typename std::pointer_traits<ElementPtr>::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<ElementPtr>::difference_type>,
layout_t<D, typename std::pointer_traits<ElementPtr>::difference_type>,
layout_t<D, typename std::pointer_traits<ElementPtr>::difference_type>
>
>;

using layout_type = typename array_ref::types::layout_t;
public:
~array_ref() = default; // lints(cppcoreguidelines-special-member-functions)

using iterator = typename subarray<T, D, ElementPtr>::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

Expand Down Expand Up @@ -3315,21 +3331,21 @@ struct array_ref // TODO(correaa) : inheredit from multi::partially_ordered2<ar

template<class OtherPtr, class=std::enable_if_t<! std::is_same<OtherPtr, ElementPtr>{}>, decltype(multi::detail::explicit_cast<ElementPtr>(std::declval<OtherPtr>()))* = nullptr>
constexpr explicit array_ref(array_ref<T, D, OtherPtr>&& other)
: subarray<T, D, ElementPtr>{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<class OtherPtr, class=std::enable_if_t<! std::is_same<OtherPtr, ElementPtr>{}>, decltype(multi::detail::implicit_cast<ElementPtr>(std::declval<OtherPtr>()))* = nullptr>
// cppcheck-suppress noExplicitConstructor ; to allow terse syntax
constexpr /*implicit*/ array_ref(array_ref<T, D, OtherPtr>&& other) // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
: subarray<T, D, ElementPtr>{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<D> const& xs) /*noexcept*/ // TODO(correa) eliminate this ctor
: subarray<T, D, ElementPtr>{typename subarray<T, D, ElementPtr>::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<T, D, ElementPtr>{typename array_ref::types::layout_t{extensions}, dat} {}

constexpr array_ref(::boost::multi::extensions_t<D> exts, ElementPtr dat) noexcept
: subarray<T, D, ElementPtr>{typename array_ref::types::layout_t(exts), dat} {}
: subarray_base{typename array_ref::types::layout_t(exts), dat} {}

template<
class Array,
Expand Down Expand Up @@ -3373,7 +3389,7 @@ struct array_ref // TODO(correaa) : inheredit from multi::partially_ordered2<ar
// cppcheck-suppress noExplicitConstructor
array_ref(std::initializer_list<TT> il) : array_ref(il.begin(), typename array_ref::extensions_type{static_cast<typename array_ref::size_type>(il.size())}) {}

using subarray<T, D, ElementPtr>::operator=;
using subarray_base::operator=;

private:
template<class It> constexpr auto copy_elements_(It first) {
Expand Down Expand Up @@ -3418,10 +3434,8 @@ struct array_ref // TODO(correaa) : inheredit from multi::partially_ordered2<ar
}

template<typename TT, dimensionality_type DD = D, class... As>
// constexpr
auto operator=(array_ref<TT, DD, As...> 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;
}
Expand Down Expand Up @@ -3563,7 +3577,7 @@ struct array_ref // TODO(correaa) : inheredit from multi::partially_ordered2<ar
private:
template<class Ar>
auto serialize_structured_(Ar& arxiv, unsigned int const version) {
subarray<T, D, ElementPtr>::serialize(arxiv, version);
subarray_base::serialize(arxiv, version);
}
template<class Archive>
auto serialize_flat_(Archive& arxiv, unsigned int const /*version*/) {
Expand Down Expand Up @@ -3618,7 +3632,7 @@ struct array_ptr
using basic_ptr = subarray_ptr<T, D, Ptr, typename array_ref<T, D, Ptr>::layout_t>;

constexpr array_ptr(Ptr data, multi::extensions_t<D> extensions)
: basic_ptr{data, multi::layout_t<D>{extensions}} {}
: basic_ptr{data, typename array_ref<T, D, Ptr>::layout_t(extensions)} {}

constexpr explicit array_ptr(std::nullptr_t nil) : array_ptr{nil, multi::extensions_t<D>{}} {}

Expand Down
94 changes: 90 additions & 4 deletions include/boost/multi/detail/layout.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,78 @@ struct monostate : equality_comparable<monostate> {
friend BOOST_MULTI_HD constexpr auto operator==(monostate const& /*self*/, monostate const& /*other*/) {return true;}
};

template<multi::dimensionality_t D, typename SSsize = multi::size_t>
class continuous_layout;

template<typename SSize>
class continuous_layout<1, SSize> {
public:
continuous_layout() = default;

using dimensionality_type = multi::dimensionality_t;
using rank = std::integral_constant<dimensionality_type, 1>;
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<index>;
using index_extension = multi::extension_t<index>;
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<difference_type, 1>;
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<std::integral_constant<difference_type, 1> >;
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<typename SSize>
struct layout_t<0, SSize>
: multi::equality_comparable<layout_t<0, SSize> >
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -666,6 +738,20 @@ struct layout_t
public:
layout_t() = default;

template<
class OtherLayout,
class = decltype(sub_type {std::declval<OtherLayout const&>().sub()}),
class = decltype(stride_type{std::declval<OtherLayout const&>().stride()}),
class = decltype(offset_type{std::declval<OtherLayout const&>().offset()}),
class = decltype(nelems_type{std::declval<OtherLayout const&>().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(
Expand Down Expand Up @@ -705,9 +791,9 @@ struct layout_t
constexpr auto operator[](index idx) const {return at_aux_(idx);}

template<typename... Indices>
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_ ;}
Expand Down
7 changes: 6 additions & 1 deletion include/boost/multi/detail/tuple_zip.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
namespace boost::multi { // NOLINT(modernize-concat-nested-namespaces) keep c++14 compat
namespace detail {

template<class... Ts> class tuple;
// we need a custom tuple type, so some fundamental types (e.g. iterators) are trivially constructible
template<class... Ts> class tuple; // TODO(correaa) consider renaming it to `tpl`

template<> class tuple<> { // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
public:
Expand Down Expand Up @@ -57,6 +58,10 @@ template<class T0, class... Ts> class tuple<T0, Ts...> : tuple<Ts...> { // 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<class TT0 = T0, std::enable_if_t<sizeof(TT0*) && (sizeof...(Ts) == 0), int> =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<T0, Ts...> other) : tuple(::std::apply([](auto... es) {return tuple(es...);}, other)) {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)

Expand Down
5 changes: 3 additions & 2 deletions include/boost/multi/detail/types.hpp
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -16,7 +16,8 @@ using size_type = std::make_signed_t<std::size_t>;
using index = std::make_signed_t<size_type>;
using difference_type = std::make_signed_t<index>;

using dimensionality_type = index;
using dimensionality_t = index;
using dimensionality_type = dimensionality_t;

} // end namespace boost::multi
#endif // BOOST_MULTI_DETAIL_TYPES_HPP
18 changes: 17 additions & 1 deletion test/array_ref.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,30 @@ inline auto trace_separate_sub(multi::subarray<int, 2> 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<int, 1> const iarrr(std::data(icarr), 5);
static_assert( std::is_base_of_v<
std::random_access_iterator_tag,
std::iterator_traits<multi::array_ref<int, 1>::iterator>::iterator_category
>);

// static_assert( std::is_base_of_v<
// std::contiguous_iterator_tag,
// std::iterator_traits<multi::array_ref<int, 1>::iterator>::iterator_category
// >);
}

BOOST_AUTO_TEST_CASE(array_ref_from_carray) {
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#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},
Expand Down
2 changes: 1 addition & 1 deletion test/element_access.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
}

Expand Down

0 comments on commit d49bd5a

Please sign in to comment.