Hi Aaron, On Wed, Aug 14, 2024 at 12:40:41PM GMT, Ballman, Aaron wrote: > > We should not impose an implementation in the language where doing > > it in a header can be completely sufficient. > > But can doing this in a header be completely sufficient in practice? > e.g., the user who passes a pointer rather than an array is in for > quite a surprise, or passing a struct, or passing a FAM, etc. If we > want to put constraints on the interface, that may be more challenging > to do from a header file than from the compiler.
I've provided a C23-portable and safe implementation of lengthof() as a macro: Portability Prior to C23 it was impossible to do this portably, but since C23 it is possible to portably write a macro that determines the num‐ ber of elements of an array, that is, the number of elements in the array. #define must_be(e) \ ( \ 0 * (int) sizeof( \ struct { \ static_assert(e); \ int ISO_C_forbids_a_struct_with_no_members; \ } \ ) \ ) #define is_array(a) \ ( \ _Generic(&(a), \ typeof((a)[0]) **: 0, \ default: 1 \ ) \ ) #define sizeof_array(a) (sizeof(a) + must_be(is_array(a))) #define nitems(a) (sizeof_array(a) / sizeof((a)[0])) While diagnostics could be better, with good helper‐macro names, they are decent. The issues with this implementation are also listed in the paper. Here's a TL;DR: - It doesn't accept type names. - In results unnecessarily in run-time values where a keyword could result in an integer constant expression: int a[7][n]; int (*p)[7][n]; p = &a; nitems(*p++); - Double evaluation: not only the macro evaluates in more cases than a keyword, it evaluates twice (due to the two sizeof calls). - Less diagnostics. Since there are less constant expressions, there are less opportunities to catch UB. So far, we've lived with all of those issues (plus the lack of portability, since this could only be implemented via compiler extensions until C23). But ideally, I'd like to avoid the wording juggling that would be required to allow such an implementation. Here's an example of the difference in wording that would be required: The elementsof operator yields the number of elements of its operand. The number of elements is determined from the type of the operand. The result is an integer. If the number of elements of the array type is variable, the operand is evaluated; +otherwise, +if the operand is a variable-length array, +it is unspecified whether the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant. +If the operand is evaluated, +it is unspecified the number of times it is evaluated. Which sounds very suspicious. > I'm still thinking on how important rank + extent is vs overall array > length. If C had constexpr functions, then I'd almost certainly want > array rank and extent to be the building blocks and then lengthof can > be a constexpr function looping over rank and summing extents. But we > don't have that yet, and "bird hand" vs "bird in bush"... :-D Or you can build it the other way around: define extent() as a macro that wraps lengthof(). About rank, I suspect you could also develop something with _Generic(3), but I didn't try. Cheers, Alex -- <https://www.alejandro-colomar.es/>
signature.asc
Description: PGP signature