On Mon, Mar 30, 2026 at 3:34 PM Jakub Jelinek <[email protected]> wrote:

> Hi!
>
> The following patch attempts to implement
> https://cplusplus.github.io/LWG/lwg-active.html#4537
> Not sure if the concept has the best name or there could be some better
> implementation, nor if having auto return type on define_static_array
> is acceptable.
>
> I've added tests for the returned type (though am not sure if that
> is correct in all cases or if we don't need further tests).
>
> In any case, this patch breaks
>
> FAIL: g++.dg/reflect/range_args.C  -std=c++26 (test for excess errors)
> Excess errors:
> /usr/src/gcc/obj/x86_64-pc-linux-gnu/libstdc++-v3/include/meta:652:25:
> error: no match for call to '(const std::ranges::__access::_Size)
> (std::ranges::filter_view<std::span<const char>, bool (*)(char)>&)'
> /usr/src/gcc/gcc/testsuite/g++.dg/reflect/range_args.C:75:33: error:
> non-constant condition for static assertion
> /usr/src/gcc/obj/x86_64-pc-linux-gnu/libstdc++-v3/include/meta:652:25:
> error: no match for call to '(const std::ranges::__access::_Size)
> (std::ranges::take_while_view<std::span<const char>, bool (*)(char)>&)'
> /usr/src/gcc/gcc/testsuite/g++.dg/reflect/range_args.C:76:33: error:
> non-constant condition for static assertion
> This is on
> constexpr std::span<const char> vv = "abcd01234";
> constexpr bool not_digit(char c) {
>   return c < '0' || c > '9';
> }
>
> static_assert (test_value_range (vv | std::views::filter (not_digit))); //
> bidirectional
> static_assert (test_value_range (vv | std::views::take_while
> (not_digit))); // non-common
>
> Dunno if this is a filter_view/take_while_view bug, something not thought
> out during LWG4537 resolution, bug in the concept or its use, something
> else.
>
The ternary expression does not have not-compiled branches, so
ranges::size(__r) needs to always
be constant expression.
constexpr size_t __extent   = __detail::__statically_sized<_Rg>   ?
ranges::size(__r) : dynamic_extent;

You can implement it using lambda:
constexpr size_t __extent   = [&__r] {
  if constexpr (__detail::__statically_sized<_Rg> )
     return ranges::size(__r);
  else
   return dynamic_extent;
};

But my preference would be to split the whole body:
if constexpr (__detail::__statically_sized<_Rg>)
{
   constexpr size_t __size =   ranges::size(__r) ;
   if constexpr (__size > 0)
       return  span<const _Tp, __size>(meta::extract<const
_Tp(&)[__size]>(__array)); // use span from c-array cosntructor
   // you can also use ptr, size
  else
     return span<const _Tp, 0>(); // default constructor
}
else // The old code that we already have, without any changes

       if (meta::is_array_type(__type))
-       return span<const _Tp>(meta::extract<const _Tp*>(__array),
-                              meta::extent(__type, 0U));


> 2026-03-30  Jakub Jelinek  <[email protected]>
>
>         * include/std/meta (std::meta::__detail::__statically_sized): New
>         concept.
>         (std::meta::define_static_array): Change return type to auto.  Use
>         __detail::__statically_sized<_Rg> ? ranges::size(__r) :
> dynamic_extent
>         as second template argument to span, for empty span construct
>         span from static_cast<const _Tp*>(nullptr), 0 instead of default
>         constructing it.
>
>         * g++.dg/reflect/define_static_array1.C (l): Another variable
>         with define_static_array test from array<int, 0>.
>         Add static assertions for types of the define_static_array results.
>
> --- libstdc++-v3/include/std/meta.jj    2026-03-30 12:57:50.289580921 +0200
> +++ libstdc++-v3/include/std/meta       2026-03-30 15:02:54.818234758 +0200
> @@ -632,18 +632,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        return meta::extract<const ranges::range_value_t<_Rg>*>(__str);
>      }
>
> +  namespace __detail
> +  {
> +    template<typename _Rg>
> +    concept __statically_sized
> +      = requires(_Rg&& __r) {
>
+       static_cast<char(*)[static_cast<size_t>(ranges::size(__r))
> +                           >= 0]>(nullptr);
>
That nice trick, could you rename it to __static_sized_range, and put it
in ranges_base.h?
I got problem with my version when imported from module std, do you see the
same issue?

> +      };
> +  } // namespace __detail
> +
>    template<ranges::input_range _Rg>
> -    consteval span<const ranges::range_value_t<_Rg>>
> +    consteval auto
>      define_static_array(_Rg&& __r)
>      {
>        using _Tp = ranges::range_value_t<_Rg>;
> +      constexpr size_t __extent
> +       = __detail::__statically_sized<_Rg>
> +         ? ranges::size(__r) : dynamic_extent;
>        auto __array = meta::reflect_constant_array(__r);
>        auto __type = meta::type_of(__array);
>        if (meta::is_array_type(__type))
> -       return span<const _Tp>(meta::extract<const _Tp*>(__array),
> -                              meta::extent(__type, 0U));
> +       return span<const _Tp, __extent>(meta::extract<const
> _Tp*>(__array),
> +                                        meta::extent(__type, 0U));
>        else
> -       return span<const _Tp>();
> +       return span<const _Tp, __extent>(static_cast<const _Tp*>(nullptr),
> +                                        0);
>      }
>
>    template<class _Tp>
> --- gcc/testsuite/g++.dg/reflect/define_static_array1.C.jj      2026-03-27
> 10:17:16.120298331 +0100
> +++ gcc/testsuite/g++.dg/reflect/define_static_array1.C 2026-03-30
> 15:19:15.286549988 +0200
> @@ -22,6 +22,7 @@ constexpr auto h = std::define_static_ar
>  constexpr auto i = std::define_static_array (std::vector <V> { V { 1, 2,
> 3 }, V { 2, 3, 4 }, V { 3, 4, 5 } });
>  constexpr auto j = std::define_static_array (std::vector <long long> {});
>  constexpr auto k = std::define_static_array
> (std::meta::nonstatic_data_members_of (^^V,
> std::meta::access_context::current ()));
> +constexpr auto l = std::define_static_array (std::array <int, 0> {});
>  static_assert (a.data () == std::define_static_string ("abcd") && a.size
> () == 5);
>  static_assert (b.data () == std::define_static_string (U"abcd\0ef\N{LATIN
> CAPITAL LETTER AE}")
>                && b.size () == sizeof (U"abcd\0ef\N{LATIN CAPITAL LETTER
> AE}") / sizeof (char32_t));
> @@ -40,6 +41,19 @@ static_assert (h.data () == std::define_
>  static_assert (i.size () == 3);
>  static_assert (j.data () == nullptr && j.size () == 0);
>  static_assert (k.size () == 3 && k[0] == ^^V::a && k[1] == ^^V::b && k[2]
> == ^^V::c);
> +static_assert (l.data () == nullptr && l.size () == 0);
> +static_assert (type_of (^^a) == ^^const std::span <const char, 5>);
> +static_assert (type_of (^^b) == ^^const std::span <const char32_t, sizeof
> (U"abcd\0ef\N{LATIN CAPITAL LETTER AE}") / sizeof (char32_t)>);
> +static_assert (type_of (^^c) == ^^const std::span <const char,
> std::dynamic_extent>);
> +static_assert (type_of (^^d) == ^^const std::span <const int, 4>);
> +static_assert (type_of (^^e) == ^^const std::span <const float, 6>);
> +static_assert (type_of (^^f) == ^^const std::span <const int,
> std::dynamic_extent>);
> +static_assert (type_of (^^g) == ^^const std::span <const int,
> std::dynamic_extent>);
> +static_assert (type_of (^^h) == ^^const std::span <const int,
> std::dynamic_extent>);
> +static_assert (type_of (^^i) == ^^const std::span <const V,
> std::dynamic_extent>);
> +static_assert (type_of (^^j) == ^^const std::span <const long long,
> std::dynamic_extent>);
> +static_assert (type_of (^^k) == ^^const std::span <const std::meta::info,
> std::dynamic_extent>);
> +static_assert (type_of (^^l) == ^^const std::span <const int, 0>);
>
>  int
>  main ()
>
>         Jakub
>
>

Reply via email to