public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h>
@ 2020-09-20 23:00 Alejandro Colomar
  2020-09-21  8:38 ` Florian Weimer
  2020-09-30 15:58 ` Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h> Joseph Myers
  0 siblings, 2 replies; 30+ messages in thread
From: Alejandro Colomar @ 2020-09-20 23:00 UTC (permalink / raw)
  To: libc-alpha

Hi,

I'd like to propose exposing the macro 'array_length()' as defined in
'include/array_length.h' to the user.

Libbsd provides '__arraycount()' in <sys/cdefs.h> and some BSDs provide
'nitems()' in <sys/param.h>, so any of those 2 headers may be a good
place to do it.

That would provide a safe macro for many users that don't know how to
write it safely, or just don't feel like typing a few extra lines, and
it would also help not needing to write such a macro for all projects,
because really most of them need it.

Also, the new GCC warning -Wsizeof-pointer-div has some problems:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94746

What do you think about it?

Thanks,

Alex

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h>
  2020-09-20 23:00 Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h> Alejandro Colomar
@ 2020-09-21  8:38 ` Florian Weimer
  2020-09-21 10:11   ` Alejandro Colomar
  2020-09-30 15:58 ` Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h> Joseph Myers
  1 sibling, 1 reply; 30+ messages in thread
From: Florian Weimer @ 2020-09-21  8:38 UTC (permalink / raw)
  To: Alejandro Colomar via Libc-alpha; +Cc: Alejandro Colomar

* Alejandro Colomar via Libc-alpha:

> I'd like to propose exposing the macro 'array_length()' as defined in
> 'include/array_length.h' to the user.

It would need a good C++ port, probably one for C++98 and another one
for C++14 or later.

> Libbsd provides '__arraycount()' in <sys/cdefs.h> and some BSDs provide
> 'nitems()' in <sys/param.h>, so any of those 2 headers may be a good
> place to do it.

In this case, I would prefer nitems in <sys/param.h>, given that there
is precedent for it.  <sys/cdefs.h> seems to be a bit drastic for a new
macro with such a common name; it would create widespread build
breakage.

Maybe also ask on the libc-coord list.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h>
  2020-09-21  8:38 ` Florian Weimer
@ 2020-09-21 10:11   ` Alejandro Colomar
  2020-09-21 10:33     ` Florian Weimer
  0 siblings, 1 reply; 30+ messages in thread
From: Alejandro Colomar @ 2020-09-21 10:11 UTC (permalink / raw)
  To: Florian Weimer; +Cc: Libc-alpha, libc-coord, gcc, libstdc++

[[
CC += libc-coord@sourceware.org
CC += gcc@gcc.gnu.org
CC += libstdc++@gcc.gnu.org
]]

Hi Florian,

On 2020-09-21 10:38, Florian Weimer wrote:
 > * Alejandro Colomar via Libc-alpha:
 >
 >> I'd like to propose exposing the macro 'array_length()' as defined in
 >> 'include/array_length.h' to the user.
 >
 > It would need a good C++ port, probably one for C++98 and another one
 > for C++14 or later.

For C++, I use the following definition:


	#include <cassert>
	#include <sys/cdefs.h>
	#include <type_traits>


	#define is_array__(a)	(std::is_array <__typeof__(a)>::value)

	#define must_be_array(arr)				\
		static_assert(is_array__(arr), "Must be an array !")


	#define array_length(arr)	(			\
	{							\
		must_be_array(arr);				\
		__arraycount((arr));				\
	}							\
	)


This solves the problem about G++ not having
__builtin_types_compatible_p().

However, there are a few problems:

1) This doesn't work for VLAs (GNU extension).
    I couldn't find a way to do it.  Maybe I should file a bug in GCC.

2) Also, this requires C++11; I don't know how to do it for older C++.
    Again, support from the compiler would be great.

3) The macro can't be used in the same places as the C version,
    because of the `({})`.
    The `0 * sizeof(struct{...})` trick doesn't work in C++ due to:
	error: types may not be defined in 'sizeof' expressions

 >
 >> Libbsd provides '__arraycount()' in <sys/cdefs.h> and some BSDs provide
 >> 'nitems()' in <sys/param.h>, so any of those 2 headers may be a good
 >> place to do it.
 >
 > In this case, I would prefer nitems in <sys/param.h>, given that there
 > is precedent for it.  <sys/cdefs.h> seems to be a bit drastic for a new
 > macro with such a common name; it would create widespread build
 > breakage.

Ok.  I guess you would use 'nitems()' name, right?

 >
 > Maybe also ask on the libc-coord list.

Ok.  Added CCs.

 >
 > Thanks,
 > Florian
 >

Thanks,

Alex

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h>
  2020-09-21 10:11   ` Alejandro Colomar
@ 2020-09-21 10:33     ` Florian Weimer
  2020-09-21 12:47       ` Alejandro Colomar
  2020-09-21 14:01       ` Jonathan Wakely
  0 siblings, 2 replies; 30+ messages in thread
From: Florian Weimer @ 2020-09-21 10:33 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: Libc-alpha, gcc, libstdc++

* Alejandro Colomar:

> [[
> CC += libc-coord@sourceware.org
> CC += gcc@gcc.gnu.org
> CC += libstdc++@gcc.gnu.org
> ]]
>
> Hi Florian,
>
> On 2020-09-21 10:38, Florian Weimer wrote:
>> * Alejandro Colomar via Libc-alpha:
>>
>>> I'd like to propose exposing the macro 'array_length()' as defined in
>>> 'include/array_length.h' to the user.
>>
>> It would need a good C++ port, probably one for C++98 and another one
>> for C++14 or later.
>
> For C++, I use the following definition:
>
>
> 	#include <cassert>
> 	#include <sys/cdefs.h>
> 	#include <type_traits>
>
>
> 	#define is_array__(a)	(std::is_array <__typeof__(a)>::value)

Should be decltype.

> However, there are a few problems:
>
> 1) This doesn't work for VLAs (GNU extension).
>    I couldn't find a way to do it.  Maybe I should file a bug in GCC.

I do not think VLA support is critical.  C++ programmers will be used to
limited support in utility functions.

> 2) Also, this requires C++11; I don't know how to do it for older C++.
>    Again, support from the compiler would be great.

I think limited C++98 support is possible using a function template,
where the array length N is a template parameter.  To enable use in
constant expressions, you can return a type of char[N], and the macro
wrapper should then apply sizeof to the function result.

> 3) The macro can't be used in the same places as the C version,
>    because of the `({})`.
>    The `0 * sizeof(struct{...})` trick doesn't work in C++ due to:
> 	error: types may not be defined in 'sizeof' expressions

For C++11, you can use a constexpr function instead of a macro.

array_length should not be a macro in current C++ modes, so that we
retain compatibility if a future C++ standard adds array_length (or
nitems) on its own.  This is not a concern for legacy C++98 mode.

>> Maybe also ask on the libc-coord list.
>
> Ok.  Added CCs.

libc-coord is not hosted on sourceware:

  <https://www.openwall.com/lists/libc-coord/2020/01/30/1>

The discussion here veered off into C++ territory anyway.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h>
  2020-09-21 10:33     ` Florian Weimer
@ 2020-09-21 12:47       ` Alejandro Colomar
  2020-09-21 15:47         ` [libc-coord] " enh
  2020-09-22 16:25         ` Rich Felker
  2020-09-21 14:01       ` Jonathan Wakely
  1 sibling, 2 replies; 30+ messages in thread
From: Alejandro Colomar @ 2020-09-21 12:47 UTC (permalink / raw)
  To: Florian Weimer; +Cc: Libc-alpha, gcc, libstdc++, libc-coord

[[ CC += libc-coord at lists.openwall.com ]]

On 2020-09-21 12:33, Florian Weimer wrote:
> * Alejandro Colomar:
> 
>> [[
>> CC += libc-coord at sourceware.org
>> CC += gcc at gcc.gnu.org
>> CC += libstdc++ at gcc.gnu.org
>> ]]
>>
>> Hi Florian,
>>
>> On 2020-09-21 10:38, Florian Weimer wrote:
>>> * Alejandro Colomar via Libc-alpha:
>>>
>>>> I'd like to propose exposing the macro 'array_length()' as defined in
>>>> 'include/array_length.h' to the user.
>>>
>>> It would need a good C++ port, probably one for C++98 and another one
>>> for C++14 or later.
>>
>> For C++, I use the following definition:
>>
>>
>> 	#include <cassert>
>> 	#include <sys/cdefs.h>
>> 	#include <type_traits>
>>
>>
>> 	#define is_array__(a)	(std::is_array <__typeof__(a)>::value)
> 
> Should be decltype.

Thanks.

> 
>> However, there are a few problems:
>>
>> 1) This doesn't work for VLAs (GNU extension).
>>     I couldn't find a way to do it.  Maybe I should file a bug in GCC.
> 
> I do not think VLA support is critical.  C++ programmers will be used to
> limited support in utility functions.
> 
>> 2) Also, this requires C++11; I don't know how to do it for older C++.
>>     Again, support from the compiler would be great.
> 
> I think limited C++98 support is possible using a function template,
> where the array length N is a template parameter.  To enable use in
> constant expressions, you can return a type of char[N], and the macro
> wrapper should then apply sizeof to the function result.

Sorry, I don't know much C++, and I don't know how to do this.

> 
>> 3) The macro can't be used in the same places as the C version,
>>     because of the `({})`.
>>     The `0 * sizeof(struct{...})` trick doesn't work in C++ due to:
>> 	error: types may not be defined in 'sizeof' expressions
> 
> For C++11, you can use a constexpr function instead of a macro.
> 
> array_length should not be a macro in current C++ modes, so that we
> retain compatibility if a future C++ standard adds array_length (or
> nitems) on its own.  This is not a concern for legacy C++98 mode.

See above.

> 
>>> Maybe also ask on the libc-coord list.
>>
>> Ok.  Added CCs.
> 
> libc-coord is not hosted on sourceware:
> 
>    <https://www.openwall.com/lists/libc-coord/2020/01/30/1>
> 
> The discussion here veered off into C++ territory anyway.

I added the correct list now.

> 
> Thanks,
> Florian
> 

Thanks,

Alex.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h>
  2020-09-21 10:33     ` Florian Weimer
  2020-09-21 12:47       ` Alejandro Colomar
@ 2020-09-21 14:01       ` Jonathan Wakely
  2020-09-21 21:52         ` Expose 'array_length()' macro in <sys/param.h> Alejandro Colomar
  1 sibling, 1 reply; 30+ messages in thread
From: Jonathan Wakely @ 2020-09-21 14:01 UTC (permalink / raw)
  To: Florian Weimer; +Cc: Alejandro Colomar, gcc, libstdc++, Libc-alpha

On 21/09/20 12:33 +0200, Florian Weimer via Libstdc++ wrote:
>* Alejandro Colomar:
>
>> [[
>> CC += libc-coord@sourceware.org
>> CC += gcc@gcc.gnu.org
>> CC += libstdc++@gcc.gnu.org
>> ]]
>>
>> Hi Florian,
>>
>> On 2020-09-21 10:38, Florian Weimer wrote:
>>> * Alejandro Colomar via Libc-alpha:
>>>
>>>> I'd like to propose exposing the macro 'array_length()' as defined in
>>>> 'include/array_length.h' to the user.
>>>
>>> It would need a good C++ port, probably one for C++98 and another one
>>> for C++14 or later.
>>
>> For C++, I use the following definition:
>>
>>
>> 	#include <cassert>
>> 	#include <sys/cdefs.h>
>> 	#include <type_traits>
>>
>>
>> 	#define is_array__(a)	(std::is_array <__typeof__(a)>::value)
>
>Should be decltype.

And it's wrong for references to arrays, it should be
is_array<typename remove_reference<decltype(a)>::type>::value.

>> However, there are a few problems:
>>
>> 1) This doesn't work for VLAs (GNU extension).
>>    I couldn't find a way to do it.  Maybe I should file a bug in GCC.
>
>I do not think VLA support is critical.  C++ programmers will be used to
>limited support in utility functions.
>
>> 2) Also, this requires C++11; I don't know how to do it for older C++.
>>    Again, support from the compiler would be great.
>
>I think limited C++98 support is possible using a function template,
>where the array length N is a template parameter.  To enable use in
>constant expressions, you can return a type of char[N], and the macro
>wrapper should then apply sizeof to the function result.

Right, it's trivial to write in any version of C++:

template<typename T, std::size_t N>
#if __cplusplus >= 201103L
constexpr
#endif
inline std::size_t
array_length(const T(&)[N])
#if __cplusplus >= 201103L
noexcept
#endif
{ return N; }

>> 3) The macro can't be used in the same places as the C version,
>>    because of the `({})`.
>>    The `0 * sizeof(struct{...})` trick doesn't work in C++ due to:
>> 	error: types may not be defined in 'sizeof' expressions
>
>For C++11, you can use a constexpr function instead of a macro.
>
>array_length should not be a macro in current C++ modes, so that we
>retain compatibility if a future C++ standard adds array_length (or
>nitems) on its own.  This is not a concern for legacy C++98 mode.

A macro is 100% unacceptable for C++.

This function already exists anyway, see std::size:
https://en.cppreference.com/w/cpp/iterator/size




^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [libc-coord] Re: Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h>
  2020-09-21 12:47       ` Alejandro Colomar
@ 2020-09-21 15:47         ` enh
  2020-09-22 16:25         ` Rich Felker
  1 sibling, 0 replies; 30+ messages in thread
From: enh @ 2020-09-21 15:47 UTC (permalink / raw)
  To: libc-coord; +Cc: Florian Weimer, Libc-alpha, gcc, libstdc++

Why would C++ programmers need this given
https://en.cppreference.com/w/cpp/iterator/size ?

On Mon, Sep 21, 2020, 05:54 Alejandro Colomar <colomar.6.4.3@gmail.com>
wrote:

> [[ CC += libc-coord at lists.openwall.com ]]
>
> On 2020-09-21 12:33, Florian Weimer wrote:
> > * Alejandro Colomar:
> >
> >> [[
> >> CC += libc-coord at sourceware.org
> >> CC += gcc at gcc.gnu.org
> >> CC += libstdc++ at gcc.gnu.org
> >> ]]
> >>
> >> Hi Florian,
> >>
> >> On 2020-09-21 10:38, Florian Weimer wrote:
> >>> * Alejandro Colomar via Libc-alpha:
> >>>
> >>>> I'd like to propose exposing the macro 'array_length()' as defined in
> >>>> 'include/array_length.h' to the user.
> >>>
> >>> It would need a good C++ port, probably one for C++98 and another one
> >>> for C++14 or later.
> >>
> >> For C++, I use the following definition:
> >>
> >>
> >>      #include <cassert>
> >>      #include <sys/cdefs.h>
> >>      #include <type_traits>
> >>
> >>
> >>      #define is_array__(a)   (std::is_array <__typeof__(a)>::value)
> >
> > Should be decltype.
>
> Thanks.
>
> >
> >> However, there are a few problems:
> >>
> >> 1) This doesn't work for VLAs (GNU extension).
> >>     I couldn't find a way to do it.  Maybe I should file a bug in GCC.
> >
> > I do not think VLA support is critical.  C++ programmers will be used to
> > limited support in utility functions.
> >
> >> 2) Also, this requires C++11; I don't know how to do it for older C++.
> >>     Again, support from the compiler would be great.
> >
> > I think limited C++98 support is possible using a function template,
> > where the array length N is a template parameter.  To enable use in
> > constant expressions, you can return a type of char[N], and the macro
> > wrapper should then apply sizeof to the function result.
>
> Sorry, I don't know much C++, and I don't know how to do this.
>
> >
> >> 3) The macro can't be used in the same places as the C version,
> >>     because of the `({})`.
> >>     The `0 * sizeof(struct{...})` trick doesn't work in C++ due to:
> >>      error: types may not be defined in 'sizeof' expressions
> >
> > For C++11, you can use a constexpr function instead of a macro.
> >
> > array_length should not be a macro in current C++ modes, so that we
> > retain compatibility if a future C++ standard adds array_length (or
> > nitems) on its own.  This is not a concern for legacy C++98 mode.
>
> See above.
>
> >
> >>> Maybe also ask on the libc-coord list.
> >>
> >> Ok.  Added CCs.
> >
> > libc-coord is not hosted on sourceware:
> >
> >    <https://www.openwall.com/lists/libc-coord/2020/01/30/1>
> >
> > The discussion here veered off into C++ territory anyway.
>
> I added the correct list now.
>
> >
> > Thanks,
> > Florian
> >
>
> Thanks,
>
> Alex.
>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/param.h>
  2020-09-21 14:01       ` Jonathan Wakely
@ 2020-09-21 21:52         ` Alejandro Colomar
  2020-09-21 22:04           ` Jonathan Wakely
  0 siblings, 1 reply; 30+ messages in thread
From: Alejandro Colomar @ 2020-09-21 21:52 UTC (permalink / raw)
  To: Jonathan Wakely, Florian Weimer; +Cc: gcc, libstdc++, Libc-alpha, libc-coord

I have developed this draft code, the C++ part being based on what you 
wrote.

I am a C programmer, and my C++ is very basic, and I tend to write 
C-compatible code when I need C++, so I can't really write the C++ part.

I tested the code with all C versions (--std= {c89, c99, c11, c18, 
c2x}), and it worked for all of them (correctly returning 18 in all of 
them), and if I uncomment the part of the pointer, it has a nice error 
message.  I used `-Wall -Wextra -Werror -pedantic -Wno-vla 
-Wno-sizeof-pointer-div`.

However, the C++ part needs some work to be able to compile.

Would you mind finishing it?


Thanks,

Alex
------------------------------------------------------------------

#if defined(__cplusplus)
# include <cstddef>

# if __cplusplus >= 201703L
#  include <array>
#  define array_length(arr)	(std:size(arr))
# else

#  if __cplusplus >= 201103L
constexpr
#  endif
inline std::size_t
array_length(const T(&array)[N])
#  if __cplusplus >= 201103L
noexcept
#  endif
{
	return	N;
}
# endif

# if __cplusplus >= 202002L
#  define array_slength(arr)	(std:ssize(arr))
# else

#  if __cplusplus >= 201103L
constexpr
#  endif
inline std::ptrdiff_t
array_slength(const T(&array)[N])
#  if __cplusplus >= 201103L
noexcept
#  endif
{
	return	N;
}
# endif


#else /* !defined(__cplusplus) */
#include <stddef.h>

# define __is_same_type(a, b)						\
	__builtin_types_compatible_p(__typeof__(a), __typeof__(b))
# define __is_array(arr)	(!__is_same_type((arr), &(arr)[0]))

# if __STDC_VERSION__ >= 201112L
#  define __must_be(e, msg)	(					\
	0 * (int)sizeof(						\
		struct {						\
			_Static_assert((e), msg);			\
			char ISO_C_forbids_a_struct_with_no_members__;	\
		}							\
	)								\
)
# else
#  define __must_be(e, msg)	(					\
	0 * (int)sizeof(						\
		struct {						\
			int	: (-!(e));				\
			char ISO_C_forbids_a_struct_with_no_members__;	\
		}							\
	)								\
)
# endif

# define __must_be_array(arr)	__must_be(__is_array(arr), "Must be an 
array!")

# define __array_length(arr)	(sizeof(arr) / sizeof((arr)[0]))
# define array_length(arr)	(__array_length(arr) + __must_be_array(arr))
# define array_slength(arr)	((ptrdiff_t)array_length(arr))
#endif


int main(void)
{
	int a[5];
	const int x = 6;
	int b[x];
#if __cplusplus >= 201103L
	constexpr
#endif
int y = 7;
	int c[y];
	int *p;
	(void)p;

	return	array_slength(a) + array_slength(b) +
		array_length(c) /*+
		array_length(p)*/;
}




^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/param.h>
  2020-09-21 21:52         ` Expose 'array_length()' macro in <sys/param.h> Alejandro Colomar
@ 2020-09-21 22:04           ` Jonathan Wakely
  2020-09-21 22:13             ` Ville Voutilainen
  2020-09-22  9:16             ` [libc-coord] Re: Expose 'array_length()' macro in <sys/param.h> Florian Weimer
  0 siblings, 2 replies; 30+ messages in thread
From: Jonathan Wakely @ 2020-09-21 22:04 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: Florian Weimer, gcc, libstdc++, Libc-alpha, libc-coord

On 21/09/20 23:52 +0200, Alejandro Colomar via Libstdc++ wrote:
>I have developed this draft code, the C++ part being based on what you 
>wrote.
>
>I am a C programmer, and my C++ is very basic, and I tend to write 
>C-compatible code when I need C++, so I can't really write the C++ 
>part.
>
>I tested the code with all C versions (--std= {c89, c99, c11, c18, 
>c2x}), and it worked for all of them (correctly returning 18 in all of 
>them), and if I uncomment the part of the pointer, it has a nice error 
>message.  I used `-Wall -Wextra -Werror -pedantic -Wno-vla 
>-Wno-sizeof-pointer-div`.
>
>However, the C++ part needs some work to be able to compile.
>
>Would you mind finishing it?
>
>
>Thanks,
>
>Alex
>------------------------------------------------------------------
>
>#if defined(__cplusplus)
># include <cstddef>
>
># if __cplusplus >= 201703L
>#  include <array>

That should be <iterator> not <array>.

>#  define array_length(arr)	(std:size(arr))

C++ programmers will not accept a macro for this.

># else


You're missing "template<typename T, std::size_t N>" here.

>#  if __cplusplus >= 201103L
>constexpr
>#  endif
>inline std::size_t
>array_length(const T(&array)[N])

Remove the name of the unused parameter.

>#  if __cplusplus >= 201103L
>noexcept
>#  endif
>{
>	return	N;
>}
># endif
>
># if __cplusplus >= 202002L
>#  define array_slength(arr)	(std:ssize(arr))
># else
>
>#  if __cplusplus >= 201103L
>constexpr
>#  endif
>inline std::ptrdiff_t
>array_slength(const T(&array)[N])
>#  if __cplusplus >= 201103L
>noexcept
>#  endif
>{
>	return	N;
>}
># endif
>
>
>#else /* !defined(__cplusplus) */
>#include <stddef.h>
>
># define __is_same_type(a, b)						\
>	__builtin_types_compatible_p(__typeof__(a), __typeof__(b))
># define __is_array(arr)	(!__is_same_type((arr), &(arr)[0]))
>
># if __STDC_VERSION__ >= 201112L
>#  define __must_be(e, msg)	(					\
>	0 * (int)sizeof(						\
>		struct {						\
>			_Static_assert((e), msg);			\
>			char ISO_C_forbids_a_struct_with_no_members__;	\
>		}							\
>	)								\
>)
># else
>#  define __must_be(e, msg)	(					\
>	0 * (int)sizeof(						\
>		struct {						\
>			int	: (-!(e));				\
>			char ISO_C_forbids_a_struct_with_no_members__;	\
>		}							\
>	)								\
>)
># endif
>
># define __must_be_array(arr)	__must_be(__is_array(arr), "Must be an 
>array!")
>
># define __array_length(arr)	(sizeof(arr) / sizeof((arr)[0]))
># define array_length(arr)	(__array_length(arr) + __must_be_array(arr))
># define array_slength(arr)	((ptrdiff_t)array_length(arr))
>#endif
>
>
>int main(void)
>{
>	int a[5];
>	const int x = 6;
>	int b[x];
>#if __cplusplus >= 201103L
>	constexpr
>#endif
>int y = 7;
>	int c[y];
>	int *p;
>	(void)p;
>
>	return	array_slength(a) + array_slength(b) +
>		array_length(c) /*+
>		array_length(p)*/;
>}
>
>
>


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/param.h>
  2020-09-21 22:04           ` Jonathan Wakely
@ 2020-09-21 22:13             ` Ville Voutilainen
  2020-09-22  9:10               ` Alejandro Colomar
  2020-09-22 11:40               ` Florian Weimer
  2020-09-22  9:16             ` [libc-coord] Re: Expose 'array_length()' macro in <sys/param.h> Florian Weimer
  1 sibling, 2 replies; 30+ messages in thread
From: Ville Voutilainen @ 2020-09-21 22:13 UTC (permalink / raw)
  To: Jonathan Wakely
  Cc: Alejandro Colomar, Florian Weimer, gcc, libstdc++,
	Libc-alpha, libc-coord

On Tue, 22 Sep 2020 at 01:07, Jonathan Wakely via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
> >#  define array_length(arr)    (std:size(arr))
>
> C++ programmers will not accept a macro for this.

..in other words, the C++17 version of it needs to be an inline
function that returns std::size of an array,
not a macro. All C++ versions need to be functions, and there should
not be any #defines in any of the
C++ code.

Why should this be array_length and not __array_length? This is a
vendor extension, so it should use
a name that is reserved for such?

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/param.h>
  2020-09-21 22:13             ` Ville Voutilainen
@ 2020-09-22  9:10               ` Alejandro Colomar
  2020-09-22  9:40                 ` Jonathan Wakely
  2020-09-22 11:40               ` Florian Weimer
  1 sibling, 1 reply; 30+ messages in thread
From: Alejandro Colomar @ 2020-09-22  9:10 UTC (permalink / raw)
  To: Ville Voutilainen, Jonathan Wakely, Florian Weimer
  Cc: gcc, libstdc++, Libc-alpha, libc-coord, LKML

[[ CC += LKML ]]

Thanks for all your input.  I learned some C++ :)

The following code works for all C and C++ standards:
g++ --std={c++98, c++03, c++11, c++14, c++17, c++20}
gcc --std={c89, c99, c11, c18, c2x}
With `-Wall -Wextra -Werror -pedantic -Wno-vla -Wno-sizeof-pointer-div`.
It doesn't compile when '+ __array_length(p)' is uncommented.
It compiles, and returns the correct value (18).
  With some exceptions:
c++ doesn't accept the VLA (w[]):

array_length.c: In function 'int main()':
array_length.c:101:22: error: no matching function for call to 
'__array_slength(int [y])'
   101 |   + __array_slength(w)
       |                      ^
array_length.c:38:1: note: candidate: 'template<class T, long int N> 
std::ptrdiff_t __array_slength(const T (&)[N])'
    38 | __array_slength(const T(&)[N])
       | ^~~~~~~~~~~~~~~
array_length.c:38:1: note:   template argument deduction/substitution 
failed:
array_length.c:101:22: note:   variable-sized array type 'long int' is 
not a valid template argument
   101 |   + __array_slength(w)
       |                      ^

But we can live with limited support for VLAs in C++.
So I needed to comment '+ __array_slength(w)',
and then the program correctly returns 11.

As Ville suggested, I renamed the function/macro to __array_[s]length().
However, (some) BSDs already provide nitems() in <sys/param.h>,
so it probably wouldn't be very drastic to
provide this function/macro with the name '[s]nitems()' there.

Would you like to add anything else before I write the patch?


BTW, I should note one more thing:

Linux has a macro named '__must_be_array()' with the same API,
but slightly different internal implementation,
so they should be aware of this change.
However, I don't think they include <sys/param.h> a lot,
so maybe it doesn't break anything; but they should be aware anyway.
I CC'd the LKML so they are aware and can give any suggestions.

Thanks,

Alex


------------------------------------------------------------------------

#if defined(__cplusplus)

# include <cstddef>
# if __cplusplus >= 201703L
#  include <iterator>
# endif

template<typename T, std::size_t N>
# if __cplusplus >= 201103L
constexpr
# endif
inline std::size_t
# if __cplusplus >= 201703L
__array_length(const T(&arr)[N])
# else
__array_length(const T(&)[N])
# endif
# if __cplusplus >= 201103L
noexcept
# endif
{
# if __cplusplus >= 201703L
	return	std::size(arr);
# else
	return	N;
# endif
}

template<typename T, std::ptrdiff_t N>
# if __cplusplus >= 201103L
constexpr
# endif
inline std::ptrdiff_t
# if __cplusplus >= 202002L
__array_slength(const T(&arr)[N])
# else
__array_slength(const T(&)[N])
# endif
# if __cplusplus >= 201103L
noexcept
# endif
{
# if __cplusplus >= 202002L
	return	std::ssize(arr);
# else
	return	N;
# endif
}


#else /* !defined(__cplusplus) */
#include <stddef.h>

# define __is_same_type(a, b)						\
	__builtin_types_compatible_p(__typeof__(a), __typeof__(b))
# define __is_array(arr)	(!__is_same_type((arr), &(arr)[0]))

# if __STDC_VERSION__ >= 201112L
#  define __must_be(e, msg)	(					\
	0 * (int)sizeof(						\
		struct {						\
			_Static_assert((e), msg);			\
			char ISO_C_forbids_a_struct_with_no_members__;	\
		}							\
	)								\
)
# else
#  define __must_be(e, msg)	(					\
	0 * (int)sizeof(						\
		struct {						\
			int	: (-!(e));				\
			char ISO_C_forbids_a_struct_with_no_members__;	\
		}							\
	)								\
)
# endif

# define __must_be_array(arr)	__must_be(__is_array(arr), "Must be an 
array!")

# define __array_len(arr)	(sizeof(arr) / sizeof((arr)[0]))
# define __array_length(arr)	(__array_len(arr) + __must_be_array(arr))
# define __array_slength(arr)	((ptrdiff_t)__array_length(arr))
#endif


int main(void)
{
	int a[5];
	const int x = 6;
	int v[x];
	int y = 7;
	int w[y];
	int *p;
	(void)p;
	(void)v;
	(void)w;

	return	__array_slength(a)
		+ __array_slength(v)
		+ __array_slength(w)
//		+ __array_length(p)
		;
}

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [libc-coord] Re: Expose 'array_length()' macro in <sys/param.h>
  2020-09-21 22:04           ` Jonathan Wakely
  2020-09-21 22:13             ` Ville Voutilainen
@ 2020-09-22  9:16             ` Florian Weimer
  1 sibling, 0 replies; 30+ messages in thread
From: Florian Weimer @ 2020-09-22  9:16 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Alejandro Colomar, libc-coord, gcc, libstdc++, Libc-alpha

* Jonathan Wakely:

>>#  define array_length(arr)	(std:size(arr))
>
> C++ programmers will not accept a macro for this.

I think we need a macro for C++98 support because there is no other way
to produce constant expression.

Note that this intended for C programs compiled in C++ mode.  As pointed
out, new C++ code should rather use std::size.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/param.h>
  2020-09-22  9:10               ` Alejandro Colomar
@ 2020-09-22  9:40                 ` Jonathan Wakely
  2020-09-22 10:01                   ` Florian Weimer
  0 siblings, 1 reply; 30+ messages in thread
From: Jonathan Wakely @ 2020-09-22  9:40 UTC (permalink / raw)
  To: Alejandro Colomar
  Cc: Ville Voutilainen, Florian Weimer, gcc, libstdc++,
	Libc-alpha, LKML, libc-coord

On 22/09/20 11:10 +0200, Alejandro Colomar via Libstdc++ wrote:
>[[ CC += LKML ]]
>
>Thanks for all your input.  I learned some C++ :)
>
>The following code works for all C and C++ standards:
>g++ --std={c++98, c++03, c++11, c++14, c++17, c++20}
>gcc --std={c89, c99, c11, c18, c2x}
>With `-Wall -Wextra -Werror -pedantic -Wno-vla -Wno-sizeof-pointer-div`.
>It doesn't compile when '+ __array_length(p)' is uncommented.
>It compiles, and returns the correct value (18).
> With some exceptions:
>c++ doesn't accept the VLA (w[]):
>
>array_length.c: In function 'int main()':
>array_length.c:101:22: error: no matching function for call to 
>'__array_slength(int [y])'
>  101 |   + __array_slength(w)
>      |                      ^
>array_length.c:38:1: note: candidate: 'template<class T, long int N> 
>std::ptrdiff_t __array_slength(const T (&)[N])'
>   38 | __array_slength(const T(&)[N])
>      | ^~~~~~~~~~~~~~~
>array_length.c:38:1: note:   template argument deduction/substitution 
>failed:
>array_length.c:101:22: note:   variable-sized array type 'long int' is 
>not a valid template argument
>  101 |   + __array_slength(w)
>      |                      ^
>
>But we can live with limited support for VLAs in C++.
>So I needed to comment '+ __array_slength(w)',
>and then the program correctly returns 11.
>
>As Ville suggested, I renamed the function/macro to __array_[s]length().
>However, (some) BSDs already provide nitems() in <sys/param.h>,
>so it probably wouldn't be very drastic to
>provide this function/macro with the name '[s]nitems()' there.
>
>Would you like to add anything else before I write the patch?
>
>
>BTW, I should note one more thing:
>
>Linux has a macro named '__must_be_array()' with the same API,
>but slightly different internal implementation,
>so they should be aware of this change.
>However, I don't think they include <sys/param.h> a lot,
>so maybe it doesn't break anything; but they should be aware anyway.
>I CC'd the LKML so they are aware and can give any suggestions.
>
>Thanks,
>
>Alex
>
>
>------------------------------------------------------------------------
>
>#if defined(__cplusplus)
>
># include <cstddef>
># if __cplusplus >= 201703L
>#  include <iterator>
># endif
>
>template<typename T, std::size_t N>

You need to use reserved names here.

># if __cplusplus >= 201103L
>constexpr
># endif
>inline std::size_t
># if __cplusplus >= 201703L
>__array_length(const T(&arr)[N])
># else
>__array_length(const T(&)[N])
># endif
># if __cplusplus >= 201103L
>noexcept

If this is going to be part of Glibc then you can use its __THROW
macro instead.


># endif
>{
># if __cplusplus >= 201703L
>	return	std::size(arr);
># else
>	return	N;
># endif
>}

I don't see much point in using std::size here. If you're going to
provide the alternative implementation for when std::size isn't
defined, why not just use it always?

template<typename _Tp, std::size_t _Num>
#if __cplusplus >= 201103L
constexpr
#endif
inline std::size_t
__array_length(const _Tp(&)[_Num]) __THROW
{
   return _Num;
}

This only requires <cstddef>, not <iterator>.

>
>template<typename T, std::ptrdiff_t N>

This declaration is wrong, the array extent has type std::size_t. The
type you return from the function doesn't change that.

The __array_slength definition should be identical to __array_length
except for its name and return type.

template<typename _Tp, std::size_t _Num>
#if __cplusplus >= 201103L
constexpr
#endif
inline std::ptrdiff_t
__array_slength(const _Tp(&)[_Num]) __THROW
{
   return _Num;
}


>
>
>#else /* !defined(__cplusplus) */
>#include <stddef.h>
>
># define __is_same_type(a, b)						\
>	__builtin_types_compatible_p(__typeof__(a), __typeof__(b))
># define __is_array(arr)	(!__is_same_type((arr), &(arr)[0]))
>
># if __STDC_VERSION__ >= 201112L
>#  define __must_be(e, msg)	(					\
>	0 * (int)sizeof(						\
>		struct {						\
>			_Static_assert((e), msg);			\
>			char ISO_C_forbids_a_struct_with_no_members__;	\
>		}							\
>	)								\
>)
># else
>#  define __must_be(e, msg)	(					\
>	0 * (int)sizeof(						\
>		struct {						\
>			int	: (-!(e));				\
>			char ISO_C_forbids_a_struct_with_no_members__;	\
>		}							\
>	)								\
>)
># endif
>
># define __must_be_array(arr)	__must_be(__is_array(arr), "Must be an 
>array!")
>
># define __array_len(arr)	(sizeof(arr) / sizeof((arr)[0]))
># define __array_length(arr)	(__array_len(arr) + __must_be_array(arr))
># define __array_slength(arr)	((ptrdiff_t)__array_length(arr))
>#endif
>
>
>int main(void)
>{
>	int a[5];
>	const int x = 6;
>	int v[x];
>	int y = 7;
>	int w[y];
>	int *p;
>	(void)p;
>	(void)v;
>	(void)w;
>
>	return	__array_slength(a)
>		+ __array_slength(v)
>		+ __array_slength(w)
>//		+ __array_length(p)
>		;
>}
>


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/param.h>
  2020-09-22  9:40                 ` Jonathan Wakely
@ 2020-09-22 10:01                   ` Florian Weimer
  2020-09-22 10:35                     ` Alejandro Colomar
  0 siblings, 1 reply; 30+ messages in thread
From: Florian Weimer @ 2020-09-22 10:01 UTC (permalink / raw)
  To: Jonathan Wakely
  Cc: Alejandro Colomar, Ville Voutilainen, gcc, libstdc++,
	Libc-alpha, LKML, libc-coord

* Jonathan Wakely:

> I don't see much point in using std::size here. If you're going to
> provide the alternative implementation for when std::size isn't
> defined, why not just use it always?
>
> template<typename _Tp, std::size_t _Num>
> #if __cplusplus >= 201103L
> constexpr
> #endif
> inline std::size_t
> __array_length(const _Tp(&)[_Num]) __THROW
> {
>   return _Num;
> }
>
> This only requires <cstddef>, not <iterator>.

I agree that this is an advantage.  But the version without constexpr is
not sufficient because __array_length does not produce a constant
expression.

I've seen something like this used instead:

  template<typename _Tp, std::size_t _Num>
  char (&___array_length(const _Tp(&)[_Num]))[_Num];
  #define __array_length(v) (sizeof(___array_length(v)))

If the function type is too cute, a helper struct could be used instead.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/param.h>
  2020-09-22 10:01                   ` Florian Weimer
@ 2020-09-22 10:35                     ` Alejandro Colomar
  0 siblings, 0 replies; 30+ messages in thread
From: Alejandro Colomar @ 2020-09-22 10:35 UTC (permalink / raw)
  To: Florian Weimer, Jonathan Wakely
  Cc: Ville Voutilainen, gcc, libstdc++, Libc-alpha, LKML, libc-coord

Thanks again for your improvements.

I think this might be ready for a patch already.
Any more thoughts?

Thanks,

Alex

------------------------


#if defined(__cplusplus)
# include <cstddef>

# if __cplusplus >= 201103L
template<typename _Tp, std::size_t _Len>
constexpr inline std::size_t
__array_length(const _Tp(&)[_Len]) __THROW
{
	return	_Len;
}

template<typename _Tp, std::size_t _Len>
constexpr inline std::ptrdiff_t
__array_slength(const _Tp(&)[_Len]) __THROW
{
	return	_Len;
}
# else /* __cplusplus < 201103L */
template<typename _Tp, std::size_t _Len>
char (&__array_length(const _Tp(&)[_Len]))[_Len];
#  define __array_length(_Arr)	(sizeof(__array_length(_Arr)))
#  define __array_slength(_Arr)						\
	(static_cast<std::ptrdiff_t>(__array_length(_Arr)))
# endif /* __cplusplus >= 201103L */


#else /* !defined(__cplusplus) */
#include <stddef.h>

# define __is_same_type(_A, _B)						\
	__builtin_types_compatible_p(__typeof__(_A), __typeof__(_B))
# define __is_array(_Arr)	(!__is_same_type((_Arr), &(_Arr)[0]))

# if __STDC_VERSION__ >= 201112L
#  define __must_be(_Expr, _Msg)	(				\
	0 * (int)sizeof(						\
		struct {						\
			_Static_assert((_Expr), _Msg);			\
			char _ISO_C_forbids_a_struct_with_no_members;	\
		}							\
	)								\
)
# else
#  define __must_be(_Expr, _Msg)	(				\
	0 * (int)sizeof(						\
		struct {						\
			int	: (-!(_Expr));				\
			char _ISO_C_forbids_a_struct_with_no_members;	\
		}							\
	)								\
)
# endif

# define __must_be_array(_Arr)	__must_be(__is_array(_Arr), "Must be an 
array!")

# define __array_len(_Arr)	(sizeof(_Arr) / sizeof((_Arr)[0]))
# define __array_length(_Arr)	(__array_len(_Arr) + __must_be_array(_Arr))
# define __array_slength(_Arr)	((ptrdiff_t)__array_length(_Arr))
#endif /* defined(__cplusplus) */


static int a[5];
static int v[__array_slength(a)];
static int w[__array_length(v)];
static int *p;

int main(void)
{
	int aa[5];
	const int xx = 6;
	int vv[xx];
	int yy = 7;
	int ww[yy];
	int *pp;

	(void)p;
	(void)pp;
	(void)ww;

	return	__array_slength(a)
		+ __array_length(v)
		+ __array_slength(w)
/*		+ __array_length(p) */ /* Always breaks :) */
		+ __array_length(aa)
		+ __array_slength(vv)
		+ __array_length(ww) /* Not in C++ */
/*		+ __array_length(pp) */ /* Always breaks :) */
		;
}

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/param.h>
  2020-09-21 22:13             ` Ville Voutilainen
  2020-09-22  9:10               ` Alejandro Colomar
@ 2020-09-22 11:40               ` Florian Weimer
  2020-09-22 14:58                 ` [RFC] <sys/param.h>: Add nitems() and snitems() macros Alejandro Colomar
  1 sibling, 1 reply; 30+ messages in thread
From: Florian Weimer @ 2020-09-22 11:40 UTC (permalink / raw)
  To: Ville Voutilainen
  Cc: Jonathan Wakely, Alejandro Colomar, gcc, libstdc++,
	Libc-alpha, libc-coord

* Ville Voutilainen:

> Why should this be array_length and not __array_length? This is a
> vendor extension, so it should use
> a name that is reserved for such?

In general, we use __ for glibc internals that are not intended for
direct access by users.  In some cases, we have non-__ identifiers that
are redirected to these __ identifiers.

In some cases it may be beneficial to expose vendor extensions under __
names, so that they can be used in strict standard modes, but we are
quite far away from that for C++ at least.

Thanks,
Florian
-- 
Red Hat GmbH, https://de.redhat.com/ , Registered seat: Grasbrunn,
Commercial register: Amtsgericht Muenchen, HRB 153243,
Managing Directors: Charles Cachera, Brian Klemm, Laurie Krebs, Michael O'Neill


^ permalink raw reply	[flat|nested] 30+ messages in thread

* [RFC] <sys/param.h>: Add nitems() and snitems() macros
  2020-09-22 11:40               ` Florian Weimer
@ 2020-09-22 14:58                 ` Alejandro Colomar
  2020-09-25 13:20                   ` [PATCH v2] " Alejandro Colomar
  0 siblings, 1 reply; 30+ messages in thread
From: Alejandro Colomar @ 2020-09-22 14:58 UTC (permalink / raw)
  To: libc-alpha
  Cc: libc-coord, libstdc++,
	linux-kernel, linux-man, gcc, fweimer, jwakely,
	ville.voutilainen, enh, Alejandro Colomar

'nitems()' calculates the length of an array in number of items.
It is safe: if a pointer is passed to the macro (or function, in C++),
the compilation is broken due to:
 - In >= C11: _Static_assert()
 - In C89, C99: Negative anonymous bitfield
 - In C++: The template requires an array

'snitems()' is equivalent to nitems(),
but it returns a 'ptrdiff_t' instead of a 'size_t'.
It is useful for comparison with signed integer values.

Some BSDs already provide a macro nitems() in <sys/param.h>,
although it usually doesn't provide safety against pointers.

This patch uses the same name for compatibility reasons,
and to be the least disruptive with existing code.

This patch also adds some other macros, which are required by 'nitems()':

__is_same_type(_A, _B):
Returns non-zero if the two input arguments are of the same type.

__is_array(_Arr):
Returns non-zero if the input argument is of an array type.

__must_be(_Expr, _Msg):
Allows using _Static_assert() everywhere an expression can be used.
It evaluates '(int)0' or breaks the compilation.

__must_be_array(_Arr):
It evaluates to '(int)0' if the argument is of an array type.
Else, it breaks compilation.

__array_len(_Arr):
It implements the basic sizeof division needed to calculate the array length.


P.S.: I'd like to put this patch in the public domain.


Signed-off-by: Alejandro Colomar <colomar.6.4.3@gmail.com>
---

[[ CC += linux-man ]]

A few more things:

I copied the contents of this patch into my system <sys/param.h>
and recompiled my projects to use these definitions, and they worked correctly.

A few more notes:

For linux-man (which is CC'd):

When/if this patch is accepted, I'll write nitems.3 (and snitems.3).

For LKML (which is CC'd):

Please comment if there are any conflicts with your macro '__must_be_array()'
(or any other conflicts, BTW).

Cheers,

Alex



 misc/sys/param.h | 57 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/misc/sys/param.h b/misc/sys/param.h
index d7c319b157..f11f5dd4fd 100644
--- a/misc/sys/param.h
+++ b/misc/sys/param.h
@@ -102,5 +102,62 @@
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #define MAX(a,b) (((a)>(b))?(a):(b))
 
+/* Macros related to the types of variables */
+# define __is_same_type(_A, _B)  __builtin_types_compatible_p(__typeof__(_A), \
+                                                              __typeof__(_B))
+# define __is_array(_Arr)	(!__is_same_type((_Arr), &(_Arr)[0]))
+
+/* Macros for embedding _Static_assert() in expressions */
+# if __STDC_VERSION__ >= 201112L
+#  define __must_be(_Expr, _Msg)  (                                           \
+        0 * (int)sizeof(                                                      \
+          struct {                                                            \
+            _Static_assert((_Expr), _Msg);                                    \
+            char _ISO_C_forbids_a_struct_with_no_members;                     \
+          }                                                                   \
+        )                                                                     \
+)
+# else
+#  define __must_be(_Expr, _Msg)  (                                           \
+        0 * (int)sizeof(                                                      \
+          struct {                                                            \
+            int  : (-!(_Expr));                                               \
+            char _ISO_C_forbids_a_struct_with_no_members;                     \
+          }                                                                   \
+        )                                                                     \
+)
+# endif
+
+# define __must_be_array(_Arr)	__must_be(__is_array(_Arr), "Must be an array!")
+
+/* Macros for array sizes */
+#if defined(__cplusplus)
+# if __cplusplus >= 201103L
+template<typename _Tp, std::size_t _Len>
+  constexpr inline std::size_t
+  nitems(const _Tp(&)[_Len]) __THROW
+  {
+    return _Len;
+  }
+
+template<typename _Tp, std::size_t _Len>
+  constexpr inline std::ptrdiff_t
+  snitems(const _Tp(&)[_Len]) __THROW
+  {
+    return _Len;
+  }
+# else /* __cplusplus < 201103L */
+template<typename _Tp, std::size_t _Len>
+  char (&__nitems_chararr(const _Tp(&)[_Len]))[_Len];
+
+#  define nitems(_Arr)          (sizeof(__nitems_chararr(_Arr)))
+#  define snitems(_Arr)         (static_cast<std::ptrdiff_t>(nitems(_Arr)))
+# endif /* __cplusplus < 201103L */
+#else /* !defined(__cplusplus) */
+# define __array_len(_Arr)      (sizeof(_Arr) / sizeof((_Arr)[0]))
+# define nitems(_Arr)           (__array_len(_Arr) + __must_be_array(_Arr))
+# define snitems(_Arr)          ((ptrdiff_t)nitems(_Arr))
+#endif /* !defined(__cplusplus) */
+
 
 #endif  /* sys/param.h */
-- 
2.28.0


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [libc-coord] Re: Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h>
  2020-09-21 12:47       ` Alejandro Colomar
  2020-09-21 15:47         ` [libc-coord] " enh
@ 2020-09-22 16:25         ` Rich Felker
  2020-09-22 16:44           ` Jonathan Wakely
  1 sibling, 1 reply; 30+ messages in thread
From: Rich Felker @ 2020-09-22 16:25 UTC (permalink / raw)
  To: libc-coord; +Cc: Florian Weimer, Libc-alpha, gcc, libstdc++

On Mon, Sep 21, 2020 at 02:47:51PM +0200, Alejandro Colomar wrote:
> [[ CC += libc-coord at lists.openwall.com ]]

I missed the beginning of this so perhaps it's already been discussed,
but I don't see how cdefs.h is a remotely reasonable place for this.
cdefs.h is included by all glibc headers and would expose anything it
defines into the namespace, so it can't define anything not in the
reserved namespace. Moreover, the whole purpose of cdefs.h is for
internal use by glibc's headers; the things it defines are not public
interfaces. So even fixing the name to be __array_length or something
would not solve the problem; rather it would exacerbate the problem of
applications treating internal glibc glue as stuff for their
consumption.

The alternative of sys/param.h is at least non-offensive, but it's
full of messy legacy macros and I don't think it's something that new
applications should be using. It also might conflict with equivalent
macros in existing software using this header.

Is there really a reason to want a nonstandard macro like this to do
something that's already trivial to do in the base language and has a
standard idiom (sizeof x / sizeof *x)?

Rich

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [libc-coord] Re: Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h>
  2020-09-22 16:25         ` Rich Felker
@ 2020-09-22 16:44           ` Jonathan Wakely
  2020-09-22 16:53             ` Ville Voutilainen
  0 siblings, 1 reply; 30+ messages in thread
From: Jonathan Wakely @ 2020-09-22 16:44 UTC (permalink / raw)
  To: Rich Felker; +Cc: libc-coord, Florian Weimer, gcc, libstdc++, Libc-alpha

On 22/09/20 12:25 -0400, Rich Felker wrote:
>Is there really a reason to want a nonstandard macro like this to do
>something that's already trivial to do in the base language and has a
>standard idiom (sizeof x / sizeof *x)?

IMHO no.


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [libc-coord] Re: Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h>
  2020-09-22 16:44           ` Jonathan Wakely
@ 2020-09-22 16:53             ` Ville Voutilainen
  0 siblings, 0 replies; 30+ messages in thread
From: Ville Voutilainen @ 2020-09-22 16:53 UTC (permalink / raw)
  To: Jonathan Wakely
  Cc: Rich Felker, Florian Weimer, gcc, libstdc++, Libc-alpha, libc-coord

On Tue, 22 Sep 2020 at 19:46, Jonathan Wakely via Libstdc++
<libstdc++@gcc.gnu.org> wrote:
>
> On 22/09/20 12:25 -0400, Rich Felker wrote:
> >Is there really a reason to want a nonstandard macro like this to do
> >something that's already trivial to do in the base language and has a
> >standard idiom (sizeof x / sizeof *x)?
>
> IMHO no.

While it's anciently stupid that that trick needs to be learned by
every programmer instead of having
something straightforward readily available, I can't become a fan of
this direction when it introduces
lowercase macros that might clash with something existing or something
that other people will want to write.
And yes, I know, we can't do any better in C, due to array decay.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v2] <sys/param.h>: Add nitems() and snitems() macros
  2020-09-22 14:58                 ` [RFC] <sys/param.h>: Add nitems() and snitems() macros Alejandro Colomar
@ 2020-09-25 13:20                   ` Alejandro Colomar
  2020-09-25 14:10                     ` Alejandro Colomar
  0 siblings, 1 reply; 30+ messages in thread
From: Alejandro Colomar @ 2020-09-25 13:20 UTC (permalink / raw)
  To: libc-alpha
  Cc: libc-coord, libstdc++,
	gcc, linux-kernel, linux-man, fweimer, jwakely,
	ville.voutilainen, enh, colomar.6.4.3, rusty

'nitems()' calculates the length of an array in number of items.
It is safe: if a pointer is passed to the macro (or function, in C++),
the compilation is broken due to:
 - In >= C11: _Static_assert()
 - In C89, C99: Negative anonymous bitfield
 - In C++: The template requires an array

'snitems()' is equivalent to nitems(),
but it returns a 'ptrdiff_t' instead of a 'size_t'.
It is useful for comparison with signed integer values.

Some BSDs already provide a macro nitems() in <sys/param.h>,
although it usually doesn't provide safety against pointers.

This patch uses the same name for compatibility reasons,
and to be the least disruptive with existing code.

This patch also adds some other macros, which are required by 'nitems()':

__is_same_type(_A, _B):
Returns non-zero if the two input arguments are of the same type.

__is_array(_Arr):
Returns non-zero if the input argument is of an array type.

__must_be(_Expr, _Msg):
Allows using _Static_assert() everywhere an expression can be used.
It evaluates '(int)0' or breaks the compilation.

__must_be_array(_Arr):
It evaluates to '(int)0' if the argument is of an array type.
Else, it breaks compilation.

__array_len(_Arr):
It implements the basic sizeof division needed to calculate the array length.


P.S.: I'd like to put this patch in the public domain.


Signed-off-by: Alejandro Colomar <colomar.6.4.3@gmail.com>
---
 misc/sys/param.h | 60 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/misc/sys/param.h b/misc/sys/param.h
index d7c319b157..88e95c2dba 100644
--- a/misc/sys/param.h
+++ b/misc/sys/param.h
@@ -102,5 +102,65 @@
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #define MAX(a,b) (((a)>(b))?(a):(b))
 
+/* Macros related to the types of variables */
+# define __is_same_type(_A, _B)  __builtin_types_compatible_p(__typeof__(_A), \
+                                                              __typeof__(_B))
+# define __is_array(_Arr)	(!__is_same_type((_Arr), &(_Arr)[0]))
+
+/* Macros for embedding _Static_assert() in expressions */
+# if __STDC_VERSION__ >= 201112L
+#  define __must_be(_Expr, _Msg)  (                                           \
+        0 * (int)sizeof(                                                      \
+          struct {                                                            \
+            _Static_assert((_Expr), _Msg);                                    \
+            char _ISO_C_forbids_a_struct_with_no_members;                     \
+          }                                                                   \
+        )                                                                     \
+)
+# else
+#  define __must_be(_Expr, _Msg)  (                                           \
+        0 * (int)sizeof(                                                      \
+          struct {                                                            \
+            int  : (-!(_Expr));                                               \
+            char _ISO_C_forbids_a_struct_with_no_members;                     \
+          }                                                                   \
+        )                                                                     \
+)
+# endif
+
+# define __must_be_array(_Arr)	__must_be(__is_array(_Arr), "Must be an array!")
+
+/* Macros for array sizes */
+#if defined(__cplusplus)
+# if __cplusplus >= 201103L
+template<typename _Tp, std::size_t _Len>
+  constexpr inline std::size_t
+  nitems(const _Tp(&)[_Len]) __THROW
+  {
+    return _Len;
+  }
+
+template<typename _Tp, std::size_t _Len>
+  constexpr inline std::ptrdiff_t
+  snitems(const _Tp(&)[_Len]) __THROW
+  {
+    return _Len;
+  }
+
+# else /* __cplusplus < 201103L */
+template<typename _Tp, std::size_t _Len>
+  char
+  (&__nitems_chararr(const _Tp(&)[_Len]))[_Len];
+
+#  define nitems(_Arr)          (sizeof(__nitems_chararr(_Arr)))
+#  define snitems(_Arr)         (static_cast<std::ptrdiff_t>(nitems(_Arr)))
+# endif /* __cplusplus < 201103L */
+
+#else /* !defined(__cplusplus) */
+# define __array_len(_Arr)      (sizeof(_Arr) / sizeof((_Arr)[0]))
+# define nitems(_Arr)           (__array_len(_Arr) + __must_be_array(_Arr))
+# define snitems(_Arr)          ((ptrdiff_t)nitems(_Arr))
+#endif /* !defined(__cplusplus) */
+
 
 #endif  /* sys/param.h */
-- 
2.28.0


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] <sys/param.h>: Add nitems() and snitems() macros
  2020-09-25 13:20                   ` [PATCH v2] " Alejandro Colomar
@ 2020-09-25 14:10                     ` Alejandro Colomar
  2020-09-25 14:48                       ` Jonathan Wakely
  0 siblings, 1 reply; 30+ messages in thread
From: Alejandro Colomar @ 2020-09-25 14:10 UTC (permalink / raw)
  To: libc-alpha
  Cc: libc-coord, libstdc++,
	gcc, linux-kernel, linux-man, fweimer, jwakely,
	ville.voutilainen, enh, rusty



On 2020-09-25 15:20, Alejandro Colomar wrote:
 > 'nitems()' calculates the length of an array in number of items.
 > It is safe: if a pointer is passed to the macro (or function, in C++),
 > the compilation is broken due to:
 >   - In >= C11: _Static_assert()
 >   - In C89, C99: Negative anonymous bitfield
 >   - In C++: The template requires an array
 >
 > 'snitems()' is equivalent to nitems(),
 > but it returns a 'ptrdiff_t' instead of a 'size_t'.
 > It is useful for comparison with signed integer values.
 >
 > Some BSDs already provide a macro nitems() in <sys/param.h>,
 > although it usually doesn't provide safety against pointers.
 >
 > This patch uses the same name for compatibility reasons,
 > and to be the least disruptive with existing code.
 >
 > This patch also adds some other macros, which are required by 'nitems()':
 >
 > __is_same_type(_A, _B):
 > Returns non-zero if the two input arguments are of the same type.
 >
 > __is_array(_Arr):
 > Returns non-zero if the input argument is of an array type.
 >
 > __must_be(_Expr, _Msg):
 > Allows using _Static_assert() everywhere an expression can be used.
 > It evaluates '(int)0' or breaks the compilation.
 >
 > __must_be_array(_Arr):
 > It evaluates to '(int)0' if the argument is of an array type.
 > Else, it breaks compilation.
 >
 > __array_len(_Arr):
 > It implements the basic sizeof division needed to calculate the array 
length.
 >
 >
 > P.S.: I'd like to put this patch in the public domain.
 >
 >
 > Signed-off-by: Alejandro Colomar <colomar.6.4.3@gmail.com>
 > ---

I patched my own system's <sys/param.h> with this,
and while 'nitems()' works fine,
I had to include <stddef.h> in my main.c to be able to use 'snitems()',
because I didn't have 'ptrdiff_t',
eventhough <sys/param.h> already includes <stddef.h>.

I completely ignore the mechanisms behind system headers including
other system headers.

Moreover, I didn't find 'ptrdiff_t' defined in any of my systems headers
I used 'user@debian:/usr/include$ grep -rn ptrdiff_t'.  Does GCC do magic?

What's the problem with that?  How should I fix the patch?

My system:  Debian bullseye/sid; x86-64; gcc 10; libc 2.31-3

Thanks,

Alex


 >   misc/sys/param.h | 60 ++++++++++++++++++++++++++++++++++++++++++++++++
 >   1 file changed, 60 insertions(+)
 >
 > diff --git a/misc/sys/param.h b/misc/sys/param.h
 > index d7c319b157..88e95c2dba 100644
 > --- a/misc/sys/param.h
 > +++ b/misc/sys/param.h
 > @@ -102,5 +102,65 @@
 >   #define MIN(a,b) (((a)<(b))?(a):(b))
 >   #define MAX(a,b) (((a)>(b))?(a):(b))
 >
 > +/* Macros related to the types of variables */
 > +# define __is_same_type(_A, _B) 
__builtin_types_compatible_p(__typeof__(_A), \
 > + 
__typeof__(_B))
 > +# define __is_array(_Arr)	(!__is_same_type((_Arr), &(_Arr)[0]))
 > +
 > +/* Macros for embedding _Static_assert() in expressions */
 > +# if __STDC_VERSION__ >= 201112L
 > +#  define __must_be(_Expr, _Msg)  ( 
          \
 > +        0 * (int)sizeof( 
          \
 > +          struct { 
          \
 > +            _Static_assert((_Expr), _Msg); 
          \
 > +            char _ISO_C_forbids_a_struct_with_no_members; 
          \
 > +          } 
          \
 > +        ) 
          \
 > +)
 > +# else
 > +#  define __must_be(_Expr, _Msg)  ( 
          \
 > +        0 * (int)sizeof( 
          \
 > +          struct { 
          \
 > +            int  : (-!(_Expr)); 
          \
 > +            char _ISO_C_forbids_a_struct_with_no_members; 
          \
 > +          } 
          \
 > +        ) 
          \
 > +)
 > +# endif
 > +
 > +# define __must_be_array(_Arr)	__must_be(__is_array(_Arr), "Must be 
an array!")
 > +
 > +/* Macros for array sizes */
 > +#if defined(__cplusplus)
 > +# if __cplusplus >= 201103L
 > +template<typename _Tp, std::size_t _Len>
 > +  constexpr inline std::size_t
 > +  nitems(const _Tp(&)[_Len]) __THROW
 > +  {
 > +    return _Len;
 > +  }
 > +
 > +template<typename _Tp, std::size_t _Len>
 > +  constexpr inline std::ptrdiff_t
 > +  snitems(const _Tp(&)[_Len]) __THROW
 > +  {
 > +    return _Len;
 > +  }
 > +
 > +# else /* __cplusplus < 201103L */
 > +template<typename _Tp, std::size_t _Len>
 > +  char
 > +  (&__nitems_chararr(const _Tp(&)[_Len]))[_Len];
 > +
 > +#  define nitems(_Arr)          (sizeof(__nitems_chararr(_Arr)))
 > +#  define snitems(_Arr) 
(static_cast<std::ptrdiff_t>(nitems(_Arr)))
 > +# endif /* __cplusplus < 201103L */
 > +
 > +#else /* !defined(__cplusplus) */
 > +# define __array_len(_Arr)      (sizeof(_Arr) / sizeof((_Arr)[0]))
 > +# define nitems(_Arr)           (__array_len(_Arr) + 
__must_be_array(_Arr))
 > +# define snitems(_Arr)          ((ptrdiff_t)nitems(_Arr))
 > +#endif /* !defined(__cplusplus) */
 > +
 >
 >   #endif  /* sys/param.h */
 >

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] <sys/param.h>: Add nitems() and snitems() macros
  2020-09-25 14:10                     ` Alejandro Colomar
@ 2020-09-25 14:48                       ` Jonathan Wakely
  2020-09-25 16:30                         ` Alejandro Colomar
  0 siblings, 1 reply; 30+ messages in thread
From: Jonathan Wakely @ 2020-09-25 14:48 UTC (permalink / raw)
  To: Alejandro Colomar
  Cc: libc-alpha, libc-coord, libstdc++,
	gcc, linux-kernel, linux-man, fweimer, ville.voutilainen, enh,
	rusty

On 25/09/20 16:10 +0200, Alejandro Colomar wrote:
>
>
>On 2020-09-25 15:20, Alejandro Colomar wrote:
>> 'nitems()' calculates the length of an array in number of items.
>> It is safe: if a pointer is passed to the macro (or function, in C++),
>> the compilation is broken due to:
>>   - In >= C11: _Static_assert()
>>   - In C89, C99: Negative anonymous bitfield
>>   - In C++: The template requires an array
>>
>> 'snitems()' is equivalent to nitems(),
>> but it returns a 'ptrdiff_t' instead of a 'size_t'.
>> It is useful for comparison with signed integer values.
>>
>> Some BSDs already provide a macro nitems() in <sys/param.h>,
>> although it usually doesn't provide safety against pointers.
>>
>> This patch uses the same name for compatibility reasons,
>> and to be the least disruptive with existing code.
>>
>> This patch also adds some other macros, which are required by 'nitems()':
>>
>> __is_same_type(_A, _B):
>> Returns non-zero if the two input arguments are of the same type.
>>
>> __is_array(_Arr):
>> Returns non-zero if the input argument is of an array type.
>>
>> __must_be(_Expr, _Msg):
>> Allows using _Static_assert() everywhere an expression can be used.
>> It evaluates '(int)0' or breaks the compilation.
>>
>> __must_be_array(_Arr):
>> It evaluates to '(int)0' if the argument is of an array type.
>> Else, it breaks compilation.
>>
>> __array_len(_Arr):
>> It implements the basic sizeof division needed to calculate the 
>array length.
>>
>>
>> P.S.: I'd like to put this patch in the public domain.
>>
>>
>> Signed-off-by: Alejandro Colomar <colomar.6.4.3@gmail.com>
>> ---
>
>I patched my own system's <sys/param.h> with this,
>and while 'nitems()' works fine,
>I had to include <stddef.h> in my main.c to be able to use 'snitems()',
>because I didn't have 'ptrdiff_t',
>eventhough <sys/param.h> already includes <stddef.h>.
>
>I completely ignore the mechanisms behind system headers including
>other system headers.
>
>Moreover, I didn't find 'ptrdiff_t' defined in any of my systems headers
>I used 'user@debian:/usr/include$ grep -rn ptrdiff_t'.  Does GCC do magic?
>
>What's the problem with that?  How should I fix the patch?

Do you really need to provide snitems?

Users can use (ptrdiff_t)nitems if needed, can't they?

C++ provides std::ssize because there are reasons to want it in
generic contexts when using the function on arbitrary container-like
objects. But for an array size you know that ptrdiff_t is wide enough
to represent the size of any array.

Do you have a use case that requries snitems, or can you assume YAGNI?


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] <sys/param.h>: Add nitems() and snitems() macros
  2020-09-25 14:48                       ` Jonathan Wakely
@ 2020-09-25 16:30                         ` Alejandro Colomar
  2020-09-25 17:39                           ` Jonathan Wakely
  2020-09-25 17:42                           ` Jonathan Wakely
  0 siblings, 2 replies; 30+ messages in thread
From: Alejandro Colomar @ 2020-09-25 16:30 UTC (permalink / raw)
  To: Jonathan Wakely
  Cc: libc-alpha, libc-coord, libstdc++,
	gcc, linux-kernel, linux-man, fweimer, ville.voutilainen, enh,
	rusty

Hello Jonathan,

On 2020-09-25 16:48, Jonathan Wakely wrote:
 > Do you really need to provide snitems?
 >
 > Users can use (ptrdiff_t)nitems if needed, can't they?

They can, but that adds casts in the code,
which makes longer lines that are somewhat harder to read.
To avoid that, users may sometimes omit the cast with possible UB.
BTW, I use

IMO, array indices should be declared as 'ptrdiff_t' always,
and not 'size_t'.  More generically, I use unsigned integer types for two
reasons:  bitwise operations, and library functions that require me to 
do so.

I don't intend to force anyone with my opinion, of course,
but if I were to choose a type for 'nitems()', it would be 'ptrdiff_t'.

However, for legacy reasons people will expect that macro to be unsigned,
so I'd have 'nitems()' unsigned, and then a signed version prefixed with 
an 's'.

Some very interesting links about this topic:

Bjarne Stroustrup (and others) about signed and unsigned integers:
https://www.youtube.com/watch?v=Puio5dly9N8&t=12m56s
https://www.youtube.com/watch?v=Puio5dly9N8&t=42m41s

The two links above are two interesting moments of the same video.

I guess that might be the reason they added std::ssize, BTW.

Google's C++ Style Guide about unsigned integers:
https://google.github.io/styleguide/cppguide.html#Integer_Types

And the most voted StackOverflow answer to the question
'What is the correct type for array indexes in C?':
https://stackoverflow.com/a/3174900/6872717

 >
 > C++ provides std::ssize because there are reasons to want it in
 > generic contexts when using the function on arbitrary container-like
 > objects. But for an array size you know that ptrdiff_t is wide enough
 > to represent the size of any array.>
 > Do you have a use case that requries snitems, or can you assume YAGNI?
 >

I have a few use cases:

1)

int	alx_gnuplot_set_style		(struct Alx_Gnuplot *restrict gnuplot,
					 int style, const char *restrict opt)
{

	if (style < 0  ||  style >= ARRAY_SSIZE(styles))
		return	-1;

	if (alx_strlcpys(gnuplot->style, styles[style],
					ARRAY_SIZE(gnuplot->style), NULL))
		return	-1;
	if (opt)
		return	alx_strbcatf(gnuplot->style, NULL, " %s", opt);
	return	0;

}

[https://github.com/alejandro-colomar/libalx/blob/master/src/extra/plot/setup.c]

2) I have many loops that access arrays; I'll just make up an example of
how I normally access arrays:

void foo(ptrdiff_t nmemb)
{
         int arr[nmemb];

         for (ptrdiff_t i = 0; i < ARRAY_SSIZE(arr); i++)
                 arr[i] = i;
}

Grepping through my code,
I have a similar number of ARRAY_SIZE() and ARRAY_SSIZE().
I could have '#define snitems(arr) ((ptrdiff_t)nitems(arr))' in my projects,
but is it really necessary?


Did I convince you? :-)

Thanks,

Alex


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] <sys/param.h>: Add nitems() and snitems() macros
  2020-09-25 16:30                         ` Alejandro Colomar
@ 2020-09-25 17:39                           ` Jonathan Wakely
  2020-09-25 17:42                           ` Jonathan Wakely
  1 sibling, 0 replies; 30+ messages in thread
From: Jonathan Wakely @ 2020-09-25 17:39 UTC (permalink / raw)
  To: Alejandro Colomar
  Cc: fweimer, linux-man, libc-alpha, gcc, rusty, linux-kernel,
	libstdc++,
	libc-coord, enh

On 25/09/20 18:30 +0200, Alejandro Colomar via Libstdc++ wrote:
>Hello Jonathan,
>
>On 2020-09-25 16:48, Jonathan Wakely wrote:
>> Do you really need to provide snitems?
>>
>> Users can use (ptrdiff_t)nitems if needed, can't they?
>
>They can, but that adds casts in the code,
>which makes longer lines that are somewhat harder to read.
>To avoid that, users may sometimes omit the cast with possible UB.
>BTW, I use
>
>IMO, array indices should be declared as 'ptrdiff_t' always,
>and not 'size_t'.  More generically, I use unsigned integer types for two
>reasons:  bitwise operations, and library functions that require me to 
>do so.
>
>I don't intend to force anyone with my opinion, of course,
>but if I were to choose a type for 'nitems()', it would be 'ptrdiff_t'.
>
>However, for legacy reasons people will expect that macro to be unsigned,
>so I'd have 'nitems()' unsigned, and then a signed version prefixed 
>with an 's'.
>
>Some very interesting links about this topic:
>
>Bjarne Stroustrup (and others) about signed and unsigned integers:
>https://www.youtube.com/watch?v=Puio5dly9N8&t=12m56s
>https://www.youtube.com/watch?v=Puio5dly9N8&t=42m41s
>
>The two links above are two interesting moments of the same video.
>
>I guess that might be the reason they added std::ssize, BTW.

Yes, I'm aware of all the rationale. I already said that it makes
sense in C++ where you have generic code. I am not convinced that it's
necessary to add to <sys/param.h> when all it does is a cast from
size_t to ptrdiff_t.


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] <sys/param.h>: Add nitems() and snitems() macros
  2020-09-25 16:30                         ` Alejandro Colomar
  2020-09-25 17:39                           ` Jonathan Wakely
@ 2020-09-25 17:42                           ` Jonathan Wakely
  2020-09-25 17:46                             ` Alejandro Colomar
  1 sibling, 1 reply; 30+ messages in thread
From: Jonathan Wakely @ 2020-09-25 17:42 UTC (permalink / raw)
  To: Alejandro Colomar
  Cc: fweimer, linux-man, libc-alpha, gcc, rusty, linux-kernel,
	libstdc++,
	libc-coord, enh

On 25/09/20 18:30 +0200, Alejandro Colomar via Libstdc++ wrote:
>I have a similar number of ARRAY_SIZE() and ARRAY_SSIZE().
>I could have '#define snitems(arr) ((ptrdiff_t)nitems(arr))' in my projects,
>but is it really necessary?

The barrier for adding something to glibc headers should be a LOT
higher than "I could [do it in my own code], but is it really
necessary?"

>Did I convince you? :-)

No.



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v2] <sys/param.h>: Add nitems() and snitems() macros
  2020-09-25 17:42                           ` Jonathan Wakely
@ 2020-09-25 17:46                             ` Alejandro Colomar
  2020-09-25 19:37                               ` [PATCH v3] <sys/param.h>: Add nitems() Alejandro Colomar
  0 siblings, 1 reply; 30+ messages in thread
From: Alejandro Colomar @ 2020-09-25 17:46 UTC (permalink / raw)
  To: Jonathan Wakely
  Cc: fweimer, linux-man, libc-alpha, gcc, rusty, linux-kernel,
	libstdc++,
	libc-coord, enh



On 2020-09-25 19:39, Jonathan Wakely wrote:
 > Yes, I'm aware of all the rationale. I already said that it makes
 > sense in C++ where you have generic code. I am not convinced that it's
 > necessary to add to <sys/param.h> when all it does is a cast from
 > size_t to ptrdiff_t.
 >

While I would prefer a signed version, I could live with only 
'nitems()'.  Having all the __must_be_array thing is the most important 
part.

On 2020-09-25 19:42, Jonathan Wakely wrote:
> On 25/09/20 18:30 +0200, Alejandro Colomar via Libstdc++ wrote:
>> I have a similar number of ARRAY_SIZE() and ARRAY_SSIZE().
>> I could have '#define snitems(arr) ((ptrdiff_t)nitems(arr))' in my 
>> projects,
>> but is it really necessary?
> 
> The barrier for adding something to glibc headers should be a LOT
> higher than "I could [do it in my own code], but is it really
> necessary?"
> 
>> Did I convince you? :-)
> 
> No.
> 
> 

Well, you convinced me :)

I'll rewrite the patch, and the problem about <stddef.h> will vanish.

Cheers,

Alex

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v3] <sys/param.h>: Add nitems()
  2020-09-25 17:46                             ` Alejandro Colomar
@ 2020-09-25 19:37                               ` Alejandro Colomar
  0 siblings, 0 replies; 30+ messages in thread
From: Alejandro Colomar @ 2020-09-25 19:37 UTC (permalink / raw)
  To: libc-alpha
  Cc: libc-coord, libstdc++,
	gcc, linux-kernel, linux-man, jwakely, fweimer,
	ville.voutilainen, enh, rusty, colomar.6.4.3

'nitems()' calculates the length of an array in number of items.
It is safe: if a pointer is passed to the macro (or function, in C++),
the compilation is broken due to:
 - In >= C11: _Static_assert()
 - In C89, C99: Negative anonymous bitfield
 - In C++: The template requires an array

Some BSDs already provide a macro nitems() in <sys/param.h>,
although it usually doesn't provide safety against pointers.

This patch uses the same name for compatibility reasons,
and to be the least disruptive with existing code.

This patch also adds some other macros, which are required by 'nitems()':

__is_same_type(_A, _B):
Returns non-zero if the two input arguments are of the same type.

__is_array(_Arr):
Returns non-zero if the input argument is of an array type.

__must_be(_Expr, _Msg):
Allows using _Static_assert() everywhere an expression can be used.
It evaluates '(int)0' or breaks the compilation.

__must_be_array(_Arr):
It evaluates to '(int)0' if the argument is of an array type.
Else, it breaks compilation.

__array_len(_Arr):
It implements the basic sizeof division needed to calculate the array length.


P.S.: I'd like to put this patch in the public domain.


Signed-off-by: Alejandro Colomar <colomar.6.4.3@gmail.com>
---
 misc/sys/param.h | 51 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/misc/sys/param.h b/misc/sys/param.h
index d7c319b157..d88cbacc9c 100644
--- a/misc/sys/param.h
+++ b/misc/sys/param.h
@@ -102,5 +102,56 @@
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #define MAX(a,b) (((a)>(b))?(a):(b))
 
+/* Macros related to the types of variables */
+# define __is_same_type(_A, _B) __builtin_types_compatible_p(__typeof__(_A),  \
+                                                             __typeof__(_B))
+# define __is_array(_Arr)	(!__is_same_type((_Arr), &(_Arr)[0]))
+
+/* Macros for embedding _Static_assert() in expressions */
+# if __STDC_VERSION__ >= 201112L
+#  define __must_be(_Expr, _Msg)  (                                           \
+        0 * (int)sizeof(                                                      \
+          struct {                                                            \
+            _Static_assert((_Expr), _Msg);                                    \
+            char _ISO_C_forbids_a_struct_with_no_members;                     \
+          }                                                                   \
+        )                                                                     \
+)
+# else
+#  define __must_be(_Expr, _Msg)  (                                           \
+        0 * (int)sizeof(                                                      \
+          struct {                                                            \
+            int  : (-!(_Expr));                                               \
+            char _ISO_C_forbids_a_struct_with_no_members;                     \
+          }                                                                   \
+        )                                                                     \
+)
+# endif
+
+# define __must_be_array(_Arr)	__must_be(__is_array(_Arr), "Must be an array!")
+
+/* Macros for array sizes */
+#if defined(__cplusplus)
+# if __cplusplus >= 201103L
+template<typename _Tp, std::size_t _Len>
+  constexpr inline std::size_t
+  nitems(const _Tp(&)[_Len]) __THROW
+  {
+    return _Len;
+  }
+
+# else /* __cplusplus < 201103L */
+template<typename _Tp, std::size_t _Len>
+  char
+  (&__nitems_chararr(const _Tp(&)[_Len]))[_Len];
+
+#  define nitems(_Arr)          (sizeof(__nitems_chararr(_Arr)))
+# endif /* __cplusplus < 201103L */
+
+#else /* !defined(__cplusplus) */
+# define __array_len(_Arr)      (sizeof(_Arr) / sizeof((_Arr)[0]))
+# define nitems(_Arr)           (__array_len(_Arr) + __must_be_array(_Arr))
+#endif /* !defined(__cplusplus) */
+
 
 #endif  /* sys/param.h */
-- 
2.28.0


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h>
  2020-09-20 23:00 Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h> Alejandro Colomar
  2020-09-21  8:38 ` Florian Weimer
@ 2020-09-30 15:58 ` Joseph Myers
  2020-09-30 20:39   ` Alejandro Colomar
  1 sibling, 1 reply; 30+ messages in thread
From: Joseph Myers @ 2020-09-30 15:58 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: libc-alpha

For some reason http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2529.pdf 
doesn't seem to have reached the agenda of a WG14 meeting yet, but adding 
a language feature like that to the standard would be another approach.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h>
  2020-09-30 15:58 ` Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h> Joseph Myers
@ 2020-09-30 20:39   ` Alejandro Colomar
  0 siblings, 0 replies; 30+ messages in thread
From: Alejandro Colomar @ 2020-09-30 20:39 UTC (permalink / raw)
  To: Joseph Myers
  Cc: libc-alpha, libc-coord, libstdc++,
	gcc, linux-kernel, linux-man, Jonathan Wakely, Florian Weimer,
	Ville Voutilainen, enh, Rusty Russell, Alejandro Colomar



On 2020-09-30 17:58, Joseph Myers wrote:
> For some reason http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2529.pdf
> doesn't seem to have reached the agenda of a WG14 meeting yet, but adding
> a language feature like that to the standard would be another approach.
> 

Hi Joseph,

Yes, that would be great!

I hope they add that to the language. When/if that happens, nitems() 
could be `#define nitems(arr) _Lengthof(arr)` for std >= c2x.

In the meantime, I would add this macro to libc.

Maybe gcc could add such a great feature as an extension even before the 
standard does...

Too many wishes :)

BTW, I sent a PATCH v4 that I should've sent --in-reply-to PATCH v3 in 
this thread (but I forgot to do so); I'll link to it here:

https://sourceware.org/pipermail/libc-alpha/2020-September/117986.html

Thanks,

Alex

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2020-09-30 20:39 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-20 23:00 Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h> Alejandro Colomar
2020-09-21  8:38 ` Florian Weimer
2020-09-21 10:11   ` Alejandro Colomar
2020-09-21 10:33     ` Florian Weimer
2020-09-21 12:47       ` Alejandro Colomar
2020-09-21 15:47         ` [libc-coord] " enh
2020-09-22 16:25         ` Rich Felker
2020-09-22 16:44           ` Jonathan Wakely
2020-09-22 16:53             ` Ville Voutilainen
2020-09-21 14:01       ` Jonathan Wakely
2020-09-21 21:52         ` Expose 'array_length()' macro in <sys/param.h> Alejandro Colomar
2020-09-21 22:04           ` Jonathan Wakely
2020-09-21 22:13             ` Ville Voutilainen
2020-09-22  9:10               ` Alejandro Colomar
2020-09-22  9:40                 ` Jonathan Wakely
2020-09-22 10:01                   ` Florian Weimer
2020-09-22 10:35                     ` Alejandro Colomar
2020-09-22 11:40               ` Florian Weimer
2020-09-22 14:58                 ` [RFC] <sys/param.h>: Add nitems() and snitems() macros Alejandro Colomar
2020-09-25 13:20                   ` [PATCH v2] " Alejandro Colomar
2020-09-25 14:10                     ` Alejandro Colomar
2020-09-25 14:48                       ` Jonathan Wakely
2020-09-25 16:30                         ` Alejandro Colomar
2020-09-25 17:39                           ` Jonathan Wakely
2020-09-25 17:42                           ` Jonathan Wakely
2020-09-25 17:46                             ` Alejandro Colomar
2020-09-25 19:37                               ` [PATCH v3] <sys/param.h>: Add nitems() Alejandro Colomar
2020-09-22  9:16             ` [libc-coord] Re: Expose 'array_length()' macro in <sys/param.h> Florian Weimer
2020-09-30 15:58 ` Expose 'array_length()' macro in <sys/cdefs.h> or <sys/param.h> Joseph Myers
2020-09-30 20:39   ` Alejandro Colomar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).