On Wed, May 7, 2025 at 11:37 AM Luc Grosheintz <luc.groshei...@gmail.com>
wrote:

>
> On 5/6/25 2:47 PM, Tomasz Kaminski wrote:
> > On Tue, May 6, 2025 at 1:39 PM Luc Grosheintz <luc.groshei...@gmail.com>
> > wrote:
> >
> >>
> >> On 5/6/25 11:28 AM, Tomasz Kaminski wrote:
> >>> For better reference, here is illustration of the design I was thinking
> >>> about:
> >>> https://godbolt.org/z/7aTcM8fz4
> >>> I would also consider having left_mapping_base to accept padding, where
> >>> layout_left uses left_mapping_base<Extents::static_extent(1) !=
> >>> dynamic_extent, Extents::static_extent(1), 1>.
> >>>
> >>
> >> Thank you for all the help! I think I'm doing what you're proposing.
> >> However,
> >> now I'm seeing an issue related to `explicit(cond)`.
> >>
> >> Essentially, it seems like with GCC the explicit for inherited ctors is
> >> ignored
> >> while with Clang it isn't.
> >>
> >> There's three variation of the issue: my working copy of layouts, a
> >> simplified
> >> version I extracted from the working copy:
> >> https://godbolt.org/z/8zfoeoc7j
> >> Here extents are convertible if the IndexType is convertible.
> >>
> >> and a modification of your reproducer:
> >> https://godbolt.org/z/hG6YKosrf
> >> Here extents are convertible if:
> >>    (Extent == dynamic_extent || Extents == OExtent) && ...
> >>
> > As a temporary workaround we could use a separate overloads:
> > template<typename OtherExtents>
> > explicit right_mapping_base(right_mapping_base<OtherExtents> const&) {}
> >
> > template<typename OtherExtents>
> > requires std::is_convertible_v<OtherExtents, Extents>
> > right_mapping_base(right_mapping_base<OtherExtents> const&) {}
> > The second overload is more constrained than the first.
>
> I have a version that works for rank 0, 1, and N for layout_left and
> layout_right. It would be nice to see how much we saved, and confirm
> that we didn't miss anything.
>
> To count number of symbol I thought one could use `nm`. So I create
> a dummy file:
>
> ```
> std::layout_left::mapping<std::extents<int>> m0;
>
> bool all_unique()
> {
>    return m0.is_unique();
> }
> ```
>
The test I would perform would be :
std::layout_left::mapping<std::extents<int>> l0;
std::layout_right:mapping<std::extents<int>> r0;
// stride
bool all_unique()
{
   return l0.is_unique();
   return r0.is_unique();
}
And we should have only one is_unique symbol.

but with a lot more duplication. Then compile it with:
>      gcc -O2 -c many_symbols.cc
> look at `nm many_symbols.o`; and I see nothing. Just one for `m0`
> and another for `all_unique`. With `gcc -O0` I see all the symbols,
> but I'm not sure how relevant -O0 is.
>
> Does this way of checking make sense, or do you have some strategy
> to ensure that we're effectively reducing the number of symbols?
>
> >
> >>
> >>> On Tue, May 6, 2025 at 10:48 AM Tomasz Kaminski <tkami...@redhat.com>
> >> wrote:
> >>>
> >>>> The constructors that are inside mapping_left, that I think represents
> >>>> constructors with other extends:
> >>>> template<class U>
> >>>> mapping_left(const mapping_left_base<N, U>& other)
> >>>> : mapping_left_base<N, double>(other) {}
> >>>> Can be placed in mapping_left_base, and they will be inherited, as
> only
> >>>> copy/move constructors are shadowed.
> >>>>
> >>>>
> >>>> On Tue, May 6, 2025 at 9:11 AM Tomasz Kaminski <tkami...@redhat.com>
> >>>> wrote:
> >>>>
> >>>>>
> >>>>>
> >>>>> On Mon, May 5, 2025 at 9:20 PM Luc Grosheintz <
> >> luc.groshei...@gmail.com>
> >>>>> wrote:
> >>>>>
> >>>>>>
> >>>>>>
> >>>>>> On 5/5/25 9:44 AM, Tomasz Kaminski wrote:
> >>>>>>> On Sat, May 3, 2025 at 2:39 PM Luc Grosheintz <
> >>>>>> luc.groshei...@gmail.com>
> >>>>>>> wrote:
> >>>>>>>
> >>>>>>>>
> >>>>>>>>
> >>>>>>>> On 4/30/25 7:13 AM, Tomasz Kaminski wrote:
> >>>>>>>>> Hi,
> >>>>>>>>>
> >>>>>>>>> As we will be landing patches for extends, this will become a
> >>>>>> separate
> >>>>>>>>> patch series.
> >>>>>>>>> I would prefer, if you could commit per layout, and start with
> >>>>>>>> layout_right
> >>>>>>>>> (default)
> >>>>>>>>> I try to provide prompt responses, so if that works better for
> you,
> >>>>>> you
> >>>>>>>> can
> >>>>>>>>> post a patch
> >>>>>>>>> only with this layout first, as most of the comments will apply
> to
> >>>>>> all of
> >>>>>>>>> them.
> >>>>>>>>>
> >>>>>>>>> For the general design we have constructors that allow conversion
> >>>>>> between
> >>>>>>>>> rank-0
> >>>>>>>>> and rank-1 layouts left and right. This is done because they
> >>>>>> essentially
> >>>>>>>>> represents
> >>>>>>>>> the same layout. I think we could benefit from that in code by
> >>>>>> having a
> >>>>>>>>> base classes
> >>>>>>>>> for rank0 and rank1 mapping:
> >>>>>>>>> template<typename _Extents>
> >>>>>>>>> _Rank0_mapping_base
> >>>>>>>>> {
> >>>>>>>>>        static_assert(_Extents::rank() == 0);
> >>>>>>>>>
> >>>>>>>>>        template<OtherExtents>
> >>>>>>>>>        // explicit, requires goes here
> >>>>>>>>>        _Rank0_mapping_base(_Rank0_mapping_base<OtherExtents>);
> >>>>>>>>>
> >>>>>>>>>         // All members layout_type goes her
> >>>>>>>>> };
> >>>>>>>>>
> >>>>>>>>> template<typename _Extents>
> >>>>>>>>> _Rank1_mapping_base
> >>>>>>>>> {
> >>>>>>>>>        static_assert(_Extents::rank() == 1);
> >>>>>>>>>       // Static assert for product is much simpler here, as we
> need
> >> to
> >>>>>>>> check one
> >>>>>>>>>
> >>>>>>>>>        template<OtherExtents>
> >>>>>>>>>        // explicit, requires goes here
> >>>>>>>>>        _Rank1_mapping_base(_Rank1_mapping_base<OtherExtents>);
> >>>>>>>>>
> >>>>>>>>>       // Call operator can also be simplified
> >>>>>>>>>       index_type operator()(index_type i) const // conversion
> >> happens
> >>>>>> at
> >>>>>>>> user
> >>>>>>>>> side
> >>>>>>>>>
> >>>>>>>>>       // cosntructor from strided_layout of Rank1 goes here.
> >>>>>>>>>
> >>>>>>>>>         // All members layout_type goes her
> >>>>>>>>> };
> >>>>>>>>> Then we will specialize layout_left/right/stride to use
> >>>>>>>> _Rank0_mapping_base
> >>>>>>>>> as a base for rank() == 0
> >>>>>>>>> and layout_left/right to use _Rank1_mapping as base for rank()1;
> >>>>>>>>> template<typename T, unsigned... Ids>
> >>>>>>>>> struct extents {};
> >>>>>>>>>
> >>>>>>>>> struct layout
> >>>>>>>>> {
> >>>>>>>>> template<typename Extends>
> >>>>>>>>> struct mapping
> >>>>>>>>> {
> >>>>>>>>> // static assert that Extents mmyst be specialization of _Extents
> >>>>>> goes
> >>>>>>>> here.
> >>>>>>>>> }
> >>>>>>>>> };
> >>>>>>>>>
> >>>>>>>>> template<typename _IndexType>
> >>>>>>>>> struct layout::mapping<extents<_IndexType>>
> >>>>>>>>> : _Rank0_mapping_base<extents<_IndexType>>
> >>>>>>>>> {
> >>>>>>>>> using layout_type = layout_left;
> >>>>>>>>> // Provides converting constructor.
> >>>>>>>>> using
> >> _Rank0_mapping_base<extents<_IndexType>>::_Rank0_mapping_base;
> >>>>>>>>> // This one is implicit;
> >>>>>>>>> mapping(_Rank0_mapping_base<extents<_IndexType>> const&);
> >>>>>>>>> };
> >>>>>>>>>
> >>>>>>>>> template<typename _IndexType, unsigned _Ext>
> >>>>>>>>> struct layout::mapping<extents<_IndexType, _Ext>>
> >>>>>>>>> : _Rank1_mapping_base<extents<_IndexType>>
> >>>>>>>>>
> >>>>>>>>> {
> >>>>>>>>> using layout_type = layout_left;
> >>>>>>>>> // Provides converting constructor.
> >>>>>>>>> using
> >> _Rank0_mapping_base<extents<_IndexType>>::_Rank0_mapping_base;
> >>>>>>>>> // This one is implicit, allows construction from layout_right
> >>>>>>>>> mapping(_Rank1_mapping_base<extents<_IndexType>> const&);
> >>>>>>>>> };
> >>>>>>>>> };
> >>>>>>>>>
> >>>>>>>>> template<typename _IndexType, unsigned... _Ext>
> >>>>>>>>> requires sizeof..(_Ext) > = 2
> >>>>>>>>> struct layout::mapping<extents<_IndexType, _Ext>>
> >>>>>>>>>
> >>>>>>>>> The last one is a generic implementation that you can use in
> yours.
> >>>>>>>>> Please also include a comment explaining that we are deviating
> from
> >>>>>>>>> standard text here.
> >>>>>>>>>
> >>>>>>>>
> >>>>>>>> Thank for reviewing and offering fast review cycles, I can't say
> >> I've
> >>>>>>>> ever felt that they were anything but wonderfully fast and I
> >>>>>> appologise
> >>>>>>>> for the delay (I've been away hiking for two days).
> >>>>>>>>
> >>>>>>>> The reason I implement all three is that I needed to see them all.
> >>>>>>>> Otherwise, I can see and "feel" the impact of the duplication (or
> >>>>>>>> efforts to reduce duplication). It's also to make sure I
> understand
> >>>>>>>> precisely how the layouts are similar and different. The idea is
> >> that
> >>>>>>>> you'd review one at a time; and by adding the others you can pick
> >>>>>> which
> >>>>>>>> one and have a glance at the other if it's helpful during review.
> >>>>>>>>
> >>>>>>>> The review contains three topics. This email responds to the idea
> of
> >>>>>>>> introducing a common base class. I believe I superficially
> >> understand
> >>>>>>>> the request. However, it's not clear to me what we gain.
> >>>>>>>>
> >>>>>>>> The reorganization seems to stress the how rank 0 and rank 1
> layouts
> >>>>>> are
> >>>>>>>> similar; at the cost of making the uniformity of layout_left
> >>>>>> (regardless
> >>>>>>>> of rank) and layout_right (regardless of rank) less obvious.
> >>>>>> Personally,
> >>>>>>>> I quite like that we can express all layouts of one kind
> regardless
> >> of
> >>>>>>>> their rank, without resorting to special cases via specialization.
> >>>>>>>>
> >>>>>>>> To me the standard reads like the layouts are three separate,
> >>>>>>>> independent entities. However, because it would be too tedious to
> >> not
> >>>>>>>> allow conversion between layouts of rank 0 and 1, a couple of
> ctors
> >>>>>> were
> >>>>>>>> added. An example, of how the layouts are not consider related is
> >> that
> >>>>>>>> we can't compare layout_left and layout_right mappings for
> equality.
> >>>>>>>>
> >>>>>>>> If I count, the current implementation has 3 copies: layout_left,
> >>>>>>>> layout_right, layout_stride. The proposed changes add two (likely
> >>>>>> three)
> >>>>>>>> base classes which, require three specializations of layout_right
> >> and
> >>>>>>>> layout_left each. Therefore I end up with 6 copies of layout_left
> >> and
> >>>>>>>> layout_right; and 2 (or 3) base classes. Or if we manage it use
> the
> >>>>>> base
> >>>>>>>> classes for all layouts: 9 specialization, 2 (or 3) base classes.
> >>>>>>>> Therefore, in terms of numbers the restructuring doesn't seem
> >>>>>> favourable
> >>>>>>>> and I feel would require noticeably more repetition in the tests.
> >>>>>>>>
> >>>>>>> I think we can use a conditional base, to eliminate
> specializations,
> >>>>>>> and have layout_left_base.
> >>>>>>>
> >>>>>>>>
> >>>>>>>> While the rank zero and rank one versions are a lot simpler than
> the
> >>>>>>>> generic copy, they aren't entirely empty either (we'll likely need
> >> to
> >>>>>>>> use `using super::*` several times). Furthermore, I don't see any
> >> real
> >>>>>>>> simplifications in the generic copy. Meaning we still have a copy
> >> that
> >>>>>>>> has all the complexities and could (almost) handle the other two
> >>>>>> cases.
> >>>>>>>>
> >>>>>>> I think using a common bases, and allowing layout_left/layout_right
> >> to
> >>>>>> be
> >>>>>>> constructed from the base classes, will automatically enable the
> >>>>>> conversions
> >>>>>>> that are currently expressed via constructor:
> >>>>>>> layout_left <-> layout_right for ranks 0 and 1,
> >>>>>>> layout_stride <-> layout_left/right for rank 0 (because they are
> >> rank0)
> >>>>>>>
> >>>>>>>>
> >>>>>>>> Since layout_stride is a little different, I'm not sure we can
> reuse
> >>>>>> the
> >>>>>>>> base classes for it. For example it allows conversion more freely,
> >> but
> >>>>>>>> is only implicit for rank 0; whereas the other two allow implicit
> >>>>>>>> conversion between each other for rank 0 and 1.
> >>>>>>>>
> >>>>>>> This is why I said rank0_mapping_base should be used for
> layout_left,
> >>>>>>> layout_rigth and layout_stride
> >>>>>>> (and later layout_left_padeed, and layout_right_padded).
> >>>>>>> For  rank1 this would be shared between layout_left, laout_right,
> >>>>>>> layout_left_paded and layout_right_paded.
> >>>>>>>
> >>>>>>>>
> >>>>>>>> Most importantly though, the reorganization makes it very hard to
> >>>>>>>> compare the implementation with the text of the standard.
> Therefore,
> >>>>>>>> making it more likely that a discrepancy goes unnoticed.
> >>>>>>>>
> >>>>>>>> One example of the subtleties involved in restructuring the code
> is
> >>>>>> the
> >>>>>>>> following: layout_right, layout_left support CTAD via the ctor
> >>>>>>>> `mapping(extents_type)`. However, when using specialization, that
> >>>>>> stops
> >>>>>>>> working and we'd need to add deduction guides to make the
> >>>>>> implementation
> >>>>>>>> conforming. I suspect a Godbolt can explain the situation better:
> >>>>>>>> https://godbolt.org/z/EdbE7ME6P
> >>>>>>>
> >>>>>>> We could use simple specializations, and derive from different base
> >>>>>> classes
> >>>>>>> using conditional_t. This would require having
> >>>>>>>
> >>>>>>
> >>>>> Thank you for looking deep into that idea, before we would go with
> >>>>> totally separate classes,
> >>>>> I would like to understands your points.
> >>>>>
> >>>>>>
> >>>>>> I gave this another try; and now I'm stumbling again as show here:
> >>>>>> https://godbolt.org/z/hfh9zE8ME
> >>>>>
> >>>>> This could be done as:
> >>>>> template<size_t N, typename T>
> >>>>> using mapping_middle_base = std::conditional_t<N == 0, rank0_base<T>,
> >>>>> rankN_middle_base<T>>;
> >>>>>
> >>>>> template<size_t N>
> >>>>> class mapping_middle : public mapping_middle_base<N, double>
> >>>>> {
> >>>>> using _Base = mapping_middle_base<N, double>;
> >>>>> public:
> >>>>> using _Base::_Base;
> >>>>>
> >>>>> mapping_middle(const _Base& other)
> >>>>> {}
> >>>>> };
> >>>>> https://godbolt.org/z/M7jxq6qTK
> >>>>>
> >>>>>>
> >>>>>>
> >>>>>> As shown, it could be solved using three ctors
> `mapping(_Rank0_base)`,
> >>>>>> `mapping(_Rank1_base)` and `mapping(_RankN_base)` with the
> respective
> >>>>>> `requires` clauses. However that feels like it defeats the purpose.
> >>>>>>
> >>>>> I was thinking about having a simple constructor that constructs from
> >>>>> _Base.
> >>>>>
> >>>>>>
> >>>>>> Frankly, the savings in terms of lines of code are not great.
> Partly,
> >>>>>> because we often need `using extents_type = __super::extents`; or
> >>>>>> the ctor `mapping(mapping_base<OExtents>&)`.
> >>>>>>
> >>>>> I see that I was unclear there, my point was not about the code size
> in
> >>>>> terms of source file,
> >>>>> but the emitted code into binary. What I mean with separate  layouts,
> >> for
> >>>>> E being every
> >>>>> static extent and and dynamic_extent, and every member function `fun`
> >> we
> >>>>> will have:
> >>>>> layout_left<extends<E>>::fun();
> >>>>> layout_right<extends<E>>::fun();
> >>>>> layout_left_padded<extends<E>>::fun();
> >>>>> layout_right_padded<extends<E>>::fun();
> >>>>> emitted in binary, and I would like to see if we can reduce it to
> >>>>> rank1_mapping_base<extends<E>>::fun();
> >>>>> That's why I am looking into base class direction.
> >>>>>
> >>>>>
> >>>>>> The generic cases of `operator()`, `stride`, `required_span_size`
> tend
> >>>>>> to supported the rank 0 and rank 1 without special cases. (Making it
> >>>>>> harder to save lines of code by having special cases for rank 0, 1
> and
> >>>>>> N.)
> >>>>>>
> >>>>> Again, I was not clear, when referring to code size, and more
> thinking
> >> of
> >>>>> binary size
> >>>>> and number of symbols.
> >>>>>
> >>>>>>
> >>>>>> The `stride` is a nice example of how it's all almost uniform, but
> not
> >>>>>> quite. It's present for layout_stride and the padded ones, but not
> >>>>>> layout_left and layout_right. For rank 1 it works for anything, just
> >> not
> >>>>>> layout_stride. (Further reducing the savings in terms of lines of
> >> code.)
> >>>>>>
> >>>>>>>>
> >>>>>>>>
> >>>>>>>> In summary: personally I feel using base classes adds repetition,
> >>>>>>>> complexity and makes it hard (for me impossible) to check that the
> >>>>>>>> implementation is conforming.
> >>>>>>>>
> >>>>>>>> Naturally, I can be convinced to change the implementation. Maybe
> >> you
> >>>>>>>> could explain a little what and why you don't like the admittedly
> >> very
> >>>>>>>> brute-force implementation.
> >>>>>>>>
> >>>>>>> There are a few things I had in mind. For rank 1 checking the
> >>>>>> precondition
> >>>>>>> for the size of multidimensional index space is trivial,
> >>>>>>> and we certainly could provide a __glibcxx_assert in such a case.
> >> This
> >>>>>> can
> >>>>>>> be done for the separate implementation, but I think
> >>>>>>> we will end up with repeated if constexpr checks in all mappings.
> >> This
> >>>>>> is
> >>>>>>> why in my mind, the reduced rank0 and rank1 cases
> >>>>>>> felt a bit special.
> >>>>>>> For example, mdspan<T, extents<N>> can be seen as replacement for
> >>>>>> span<N>,
> >>>>>>> which provides support for adjusting accessor
> >>>>>>> and pointer type.
> >>>>>>>
> >>>>>>> My original request, however, was however driven by code size. The
> >>>>>>> layout_left, layout_right, layout_left_padded, layout_right_padded
> >>>>>>> with rank 0, have members with same semantics (operator(), stride,
> >>>>>> ...),
> >>>>>>> and we could have only one definition for them, instead of four.
> >>>>>>>
> >>>>>>>>
> >>>>>>>>>
> >>>>>>>>> On Tue, Apr 29, 2025 at 2:56 PM Luc Grosheintz <
> >>>>>> luc.groshei...@gmail.com
> >>>>>>>>>
> >>>>>>>>> wrote:
> >>>>>>>>>
> >>>>>>>>>> Implements the parts of layout_left that don't depend on any of
> >> the
> >>>>>>>>>> other layouts.
> >>>>>>>>>>
> >>>>>>>>>> libstdc++/ChangeLog:
> >>>>>>>>>>
> >>>>>>>>>>             * include/std/mdspan (layout_left): New class.
> >>>>>>>>>>
> >>>>>>>>>> Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
> >>>>>>>>>> ---
> >>>>>>>>>>      libstdc++-v3/include/std/mdspan | 179
> >>>>>> ++++++++++++++++++++++++++++++++
> >>>>>>>>>>      1 file changed, 179 insertions(+)
> >>>>>>>>>>
> >>>>>>>>>> diff --git a/libstdc++-v3/include/std/mdspan
> >>>>>>>>>> b/libstdc++-v3/include/std/mdspan
> >>>>>>>>>> index 39ced1d6301..e05048a5b93 100644
> >>>>>>>>>> --- a/libstdc++-v3/include/std/mdspan
> >>>>>>>>>> +++ b/libstdc++-v3/include/std/mdspan
> >>>>>>>>>> @@ -286,6 +286,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>>>>>>>
> >>>>>>>>>>        namespace __mdspan
> >>>>>>>>>>        {
> >>>>>>>>>> +    template<typename _Extents>
> >>>>>>>>>> +      constexpr typename _Extents::index_type
> >>>>>>>>>> +      __fwd_prod(const _Extents& __exts, size_t __r) noexcept
> >>>>>>>>>> +      {
> >>>>>>>>>> +       typename _Extents::index_type __fwd = 1;
> >>>>>>>>>> +       for(size_t __i = 0; __i < __r; ++__i)
> >>>>>>>>>> +         __fwd *= __exts.extent(__i);
> >>>>>>>>>> +       return __fwd;
> >>>>>>>>>> +      }
> >>>>>>>>>>
> >>>>>>>>> As we are inside the standard library implementation, we can do
> >> some
> >>>>>>>> tricks
> >>>>>>>>> here,
> >>>>>>>>> and provide two functions:
> >>>>>>>>> // Returns the std::span(_ExtentsStorage::_Ext).substr(f, l);
> >>>>>>>>> // For extents forward to __static_exts
> >>>>>>>>> span<typename Extends::index_type> __static_exts(size_t f, size_t
> >> l);
> >>>>>>>>> // Returns the
> >>>>>>>>>
> >>>>>>>>
> >>>>>>
> >>
> std::span(_ExtentsStorage::_M_dynamic_extents).substr(_S_dynamic_index[f],
> >>>>>>>>> _S_dynamic_index[l);
> >>>>>>>>> span<typename Extends::index_type> __dynamic_exts(Extents const&
> >> c);
> >>>>>>>>> Then you can befriend this function both to extents and
> >>>>>> _ExtentsStorage.
> >>>>>>>>> Also add index_type members to _ExtentsStorage.
> >>>>>>>>>
> >>>>>>>>> Then instead of having fwd-prod and rev-prod I would have:
> >>>>>>>>> template<typename _Extents>
> >>>>>>>>> consteval size_t __static_ext_prod(size_t f, size_t l)
> >>>>>>>>> {
> >>>>>>>>>       // multiply E != dynamic_ext from __static_exts
> >>>>>>>>> }
> >>>>>>>>> constexpr size __ext_prod(const _Extents& __exts, size_t f,
> size_t
> >> l)
> >>>>>>>>> {
> >>>>>>>>>        // multiply __static_ext_prod<_Extents>(f, l) and each
> >> elements
> >>>>>> of
> >>>>>>>>> __dynamic_exts(__exts, f, l);
> >>>>>>>>> }
> >>>>>>>>>
> >>>>>>>>> Then fwd-prod(e, n) would be __ext_prod(e, 0, n), and rev_prod(e,
> >> n)
> >>>>>>>> would
> >>>>>>>>> be __ext_prod(e, __ext.rank() -n, n, __ext.rank())
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>>> +
> >>>>>>>>>> +    template<typename _Extents>
> >>>>>>>>>> +      constexpr typename _Extents::index_type
> >>>>>>>>>> +      __rev_prod(const _Extents& __exts, size_t __r) noexcept
> >>>>>>>>>> +      {
> >>>>>>>>>> +       typename _Extents::index_type __rev = 1;
> >>>>>>>>>> +       for(size_t __i = __r + 1; __i < __exts.rank(); ++__i)
> >>>>>>>>>> +         __rev *= __exts.extent(__i);
> >>>>>>>>>> +       return __rev;
> >>>>>>>>>> +      }
> >>>>>>>>>> +
> >>>>>>>>>>          template<typename _IndexType, size_t... _Counts>
> >>>>>>>>>>            auto __build_dextents_type(integer_sequence<size_t,
> >>>>>> _Counts...>)
> >>>>>>>>>>             -> extents<_IndexType, ((void) _Counts,
> >>>>>> dynamic_extent)...>;
> >>>>>>>>>> @@ -304,6 +324,165 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>>>>>>>>>          explicit extents(_Integrals...) ->
> >>>>>>>>>>            extents<size_t,
> >>>>>> __mdspan::__dynamic_extent<_Integrals>()...>;
> >>>>>>>>>>
> >>>>>>>>>> +  struct layout_left
> >>>>>>>>>> +  {
> >>>>>>>>>> +    template<typename _Extents>
> >>>>>>>>>> +      class mapping;
> >>>>>>>>>> +  };
> >>>>>>>>>> +
> >>>>>>>>>> +  namespace __mdspan
> >>>>>>>>>> +  {
> >>>>>>>>>> +    template<typename _Tp>
> >>>>>>>>>> +      constexpr bool __is_extents = false;
> >>>>>>>>>> +
> >>>>>>>>>> +    template<typename _IndexType, size_t... _Extents>
> >>>>>>>>>> +      constexpr bool __is_extents<extents<_IndexType,
> >>>>>> _Extents...>> =
> >>>>>>>>>> true;
> >>>>>>>>>> +
> >>>>>>>>>> +    template<size_t _Count>
> >>>>>>>>>> +    struct _LinearIndexLeft
> >>>>>>>>>> +    {
> >>>>>>>>>> +      template<typename _Extents, typename... _Indices>
> >>>>>>>>>> +       static constexpr typename _Extents::index_type
> >>>>>>>>>> +       _S_value(const _Extents& __exts, typename
> >>>>>> _Extents::index_type
> >>>>>>>>>> __idx,
> >>>>>>>>>> +                _Indices... __indices) noexcept
> >>>>>>>>>> +       {
> >>>>>>>>>> +         return __idx + __exts.extent(_Count)
> >>>>>>>>>> +           * _LinearIndexLeft<_Count + 1>::_S_value(__exts,
> >>>>>>>> __indices...);
> >>>>>>>>>> +       }
> >>>>>>>>>> +
> >>>>>>>>>> +      template<typename _Extents>
> >>>>>>>>>> +       static constexpr typename _Extents::index_type
> >>>>>>>>>> +       _S_value(const _Extents&) noexcept
> >>>>>>>>>> +       { return 0; }
> >>>>>>>>>> +    };
> >>>>>>>>>> +
> >>>>>>>>>> +    template<typename _Extents, typename... _Indices>
> >>>>>>>>>> +      constexpr typename _Extents::index_type
> >>>>>>>>>> +      __linear_index_left(const _Extents& __exts, _Indices...
> >>>>>>>> __indices)
> >>>>>>>>>> +      {
> >>>>>>>>>> +       return _LinearIndexLeft<0>::_S_value(__exts,
> >> __indices...);
> >>>>>>>>>> +      }
> >>>>>>>>>>
> >>>>>>>>> This can be eliminated by fold expressions, see below.
> >>>>>>>>>
> >>>>>>>>>> +
> >>>>>>>>>> +    template<typename _IndexType, typename _Tp, size_t _Nm>
> >>>>>>>>>> +      consteval bool
> >>>>>>>>>> +      __is_representable_product(array<_Tp, _Nm> __factors)
> >>>>>>>>>> +      {
> >>>>>>>>>> +       size_t __rest = numeric_limits<_IndexType>::max();
> >>>>>>>>>> +       for(size_t __i = 0; __i < _Nm; ++__i)
> >>>>>>>>>> +       {
> >>>>>>>>>> +         if (__factors[__i] == 0)
> >>>>>>>>>> +           return true;
> >>>>>>>>>> +         __rest /= _IndexType(__factors[__i]);
> >>>>>>>>>> +       }
> >>>>>>>>>> +       return __rest > 0;
> >>>>>>>>>> +      }
> >>>>>>>>>>
> >>>>>>>>> I would replace that with
> >>>>>>>>> template<IndexType>
> >>>>>>>>> consteval size_t __div_reminder(span<const size_t, _Nm>
> __factors,
> >>>>>> size_t
> >>>>>>>>> __val)
> >>>>>>>>> {
> >>>>>>>>>          size_t __rest = val;
> >>>>>>>>>          for(size_t __i = 0; __i < _Nm; ++__i)
> >>>>>>>>>          {
> >>>>>>>>>            if (__factors[__i] == dynamic_extent)
> >>>>>>>>>              continue;
> >>>>>>>>>            if (__factors[__i] != 0)
> >>>>>>>>>               return val;
> >>>>>>>>>             __rest /= _IndexType(__factors[__i]);
> >>>>>>>>>            if (__res == 0)
> >>>>>>>>>              return 0;
> >>>>>>>>>          }
> >>>>>>>>>         return __rest;
> >>>>>>>>> }
> >>>>>>>>>
> >>>>>>>>> We can express the is presentable check as
> >>>>>>>>> static constexpr __dyn_reminder =
> >>>>>>>> __div_reminder(__static_exts<Extents>(0,
> >>>>>>>>> rank()), std::numeric_limits<Index>::max());
> >>>>>>>>> static_assert(__dyn_reminder > 0);
> >>>>>>>>> However, with __dyn_reminder value, the precondition
> >>>>>>>>> https://eel.is/c++draft/mdspan.layout#left.cons-1,
> >>>>>>>>> can be checked by doing equivalent of __div_remainder for
> >>>>>> __dyn_extents
> >>>>>>>>> with __val being __dyn_reminder.
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>>> +
> >>>>>>>>>> +    template<typename _Extents>
> >>>>>>>>>> +      consteval array<typename _Extents::index_type,
> >>>>>> _Extents::rank()>
> >>>>>>>>>> +      __static_extents_array()
> >>>>>>>>>> +      {
> >>>>>>>>>> +       array<typename _Extents::index_type, _Extents::rank()>
> >>>>>> __exts;
> >>>>>>>>>> +       for(size_t __i = 0; __i < _Extents::rank(); ++__i)
> >>>>>>>>>> +         __exts[__i] = _Extents::static_extent(__i);
> >>>>>>>>>> +       return __exts;
> >>>>>>>>>> +      }
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> Replaced by __static_exts accessor, as described above.
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>>> +
> >>>>>>>>>> +    template<typename _Extents, typename _IndexType>
> >>>>>>>>>> +      concept __representable_size = _Extents::rank_dynamic()
> !=
> >> 0
> >>>>>>>>>> +      || __is_representable_product<_IndexType>(
> >>>>>>>>>> +         __static_extents_array<_Extents>());
> >>>>>>>>>> +
> >>>>>>>>>> +    template<typename _Extents>
> >>>>>>>>>> +      concept __layout_extent = __representable_size<
> >>>>>>>>>> +       _Extents, typename _Extents::index_type>;
> >>>>>>>>>> +  }
> >>>>>>>>>>
> >>>>>>>>> +
> >>>>>>>>>> +  template<typename _Extents>
> >>>>>>>>>> +    class layout_left::mapping
> >>>>>>>>>> +    {
> >>>>>>>>>> +      static_assert(__mdspan::__layout_extent<_Extents>,
> >>>>>>>>>> +       "The size of extents_type is not representable as
> >>>>>> index_type.");
> >>>>>>>>>> +    public:
> >>>>>>>>>> +      using extents_type = _Extents;
> >>>>>>>>>> +      using index_type = typename extents_type::index_type;
> >>>>>>>>>> +      using size_type = typename extents_type::size_type;
> >>>>>>>>>> +      using rank_type = typename extents_type::rank_type;
> >>>>>>>>>> +      using layout_type = layout_left;
> >>>>>>>>>> +
> >>>>>>>>>> +      constexpr
> >>>>>>>>>> +      mapping() noexcept = default;
> >>>>>>>>>> +
> >>>>>>>>>> +      constexpr
> >>>>>>>>>> +      mapping(const mapping&) noexcept = default;
> >>>>>>>>>> +
> >>>>>>>>>> +      constexpr
> >>>>>>>>>> +      mapping(const extents_type& __extents) noexcept
> >>>>>>>>>> +      : _M_extents(__extents)
> >>>>>>>>>> +      {
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>>> }
> >>>>>>>>>> +
> >>>>>>>>>> +      template<typename _OExtents>
> >>>>>>>>>> +       requires (is_constructible_v<extents_type, _OExtents>)
> >>>>>>>>>> +       constexpr explicit(!is_convertible_v<_OExtents,
> >>>>>> extents_type>)
> >>>>>>>>>> +       mapping(const mapping<_OExtents>& __other) noexcept
> >>>>>>>>>> +       : _M_extents(__other.extents())
> >>>>>>>>>> +       {
> >>>>>>>>>
> >>>>>>>>> Here we could do checks at compile time:
> >>>>>>>>> if constexpr(_OExtents::rank_dynamic() == 0)
> >>>>>>>>>      static_assert( __div_remainder(...) > 0);
> >>>>>>>>> }
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>>> }
> >>>>>>>>>> +
> >>>>>>>>>> +      constexpr mapping&
> >>>>>>>>>> +      operator=(const mapping&) noexcept = default;
> >>>>>>>>>> +
> >>>>>>>>>> +      constexpr const extents_type&
> >>>>>>>>>> +      extents() const noexcept { return _M_extents; }
> >>>>>>>>>> +
> >>>>>>>>>> +      constexpr index_type
> >>>>>>>>>> +      required_span_size() const noexcept
> >>>>>>>>>> +      { return __mdspan::__fwd_prod(_M_extents,
> >>>>>> _M_extents.rank()); }
> >>>>>>>>>> +
> >>>>>>>>>> +      template<__mdspan::__valid_index_type<index_type>...
> >>>>>> _Indices>
> >>>>>>>>>>
> >>>>>>>>> // Because we extracted rank0 and rank1 specializations
> >>>>>>>>>
> >>>>>>>>>> +       requires (sizeof...(_Indices) + 1 ==
> extents_type::rank())
> >>>>>>>>>> +       constexpr index_type
> >>>>>>>>>> +       operator()(index_type __idx, _Indices... __indices)
> const
> >>>>>>>> noexcept
> >>>>>>>>>> +       {
> >>>>>>>>>>
> >>>>>>>>> This could be implemented as, please synchronize the names.
> >>>>>>>>> if constexpr (!is_same_v<_Indices, index_type> || ...)
> >>>>>>>>>       // Reduce the number of instantations.
> >>>>>>>>>       return operator()(index_type _idx0,
> >>>>>>>>> static_cast<index_type>(std::move(__indices))....);
> >>>>>>>>> else
> >>>>>>>>>       {
> >>>>>>>>>           // This can be used for layout stride, if you start
> with
> >>>>>> __res =
> >>>>>>>> 0;
> >>>>>>>>>           index_type __res = _idx0;
> >>>>>>>>>           index_type __mult = _M_extents.extent(0);
> >>>>>>>>>           auto __update = [&__res, &__mult, __pos =
> 1u](index_type
> >>>>>> __idx)
> >>>>>>>>> mutable
> >>>>>>>>>              {
> >>>>>>>>>                  __res += __idx * __mult;
> >>>>>>>>>                  __mult *= _M_extents.extent(__pos);
> >>>>>>>>>                  ++__pos;
> >>>>>>>>>              };
> >>>>>>>>>          // Fold over invocation of lambda
> >>>>>>>>>           (__update(_Indices), ....);
> >>>>>>>>>           return __res;
> >>>>>>>>>       }
> >>>>>>>>>
> >>>>>>>>> This could be even simpler and written as (use for layout
> stride):
> >>>>>>>>> size_t __pos = 0;
> >>>>>>>>> return (index_type(0) + ... + __indices * stride(__pos++));
> >>>>>>>>> Here, I prefer to avoid multiplying multiple times.
> >>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> +         return __mdspan::__linear_index_left(
> >>>>>>>>>> +           _M_extents, static_cast<index_type>(__indices)...);
> >>>>>>>>>> +       }
> >>>>>>>>>> +
> >>>>>>>>>> +      static constexpr bool
> >>>>>>>>>> +      is_always_unique() noexcept { return true; }
> >>>>>>>>>> +
> >>>>>>>>>> +      static constexpr bool
> >>>>>>>>>> +      is_always_exhaustive() noexcept { return true; }
> >>>>>>>>>> +
> >>>>>>>>>> +      static constexpr bool
> >>>>>>>>>> +      is_always_strided() noexcept { return true; }
> >>>>>>>>>> +
> >>>>>>>>>> +      static constexpr bool
> >>>>>>>>>> +      is_unique() noexcept { return true; }
> >>>>>>>>>> +
> >>>>>>>>>> +      static constexpr bool
> >>>>>>>>>> +      is_exhaustive() noexcept { return true; }
> >>>>>>>>>> +
> >>>>>>>>>> +      static constexpr bool
> >>>>>>>>>> +      is_strided() noexcept { return true; }
> >>>>>>>>>> +
> >>>>>>>>>> +      constexpr index_type
> >>>>>>>>>> +      stride(rank_type __i) const noexcept
> >>>>>>>>>> +      requires (extents_type::rank() > 0)
> >>>>>>>>>> +      {
> >>>>>>>>>> +       __glibcxx_assert(__i < extents_type::rank());
> >>>>>>>>>> +       return __mdspan::__fwd_prod(_M_extents, __i);
> >>>>>>>>>> +      }
> >>>>>>>>>> +
> >>>>>>>>>> +      template<typename _OExtents>
> >>>>>>>>>> +       requires (extents_type::rank() == _OExtents::rank())
> >>>>>>>>>> +       friend constexpr bool
> >>>>>>>>>> +       operator==(const mapping& __self, const
> >> mapping<_OExtents>&
> >>>>>>>>>> __other)
> >>>>>>>>>> +       noexcept
> >>>>>>>>>> +       { return __self.extents() == __other.extents(); }
> >>>>>>>>>> +
> >>>>>>>>>> +    private:
> >>>>>>>>>> +      [[no_unique_address]] extents_type _M_extents;
> >>>>>>>>>> +    };
> >>>>>>>>>> +
> >>>>>>>>>>      _GLIBCXX_END_NAMESPACE_VERSION
> >>>>>>>>>>      }
> >>>>>>>>>>      #endif
> >>>>>>>>>> --
> >>>>>>>>>> 2.49.0
> >>>>>>>>>>
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>
> >>>>>>>>
> >>>>>>>
> >>>>>>
> >>>>>>
> >>>
> >>
> >>
> >
>
>

Reply via email to