On Mon, Aug 19, 2024 at 12:58:30PM GMT, Alejandro Colomar wrote: > I have a draft for an updated proposal to WG14 (more recent than n3313). > It has some changes to the documentation of prior art, some typo fixes, > etc., and renames elementsof()=>nelementsof(), but the meat of the > proposal is the same. I've also rebased it on top of the latest draft > for C2y, which is n3301. I'll send it as a reply to this subthread.
I've attached the PDF document of the draft n3313.1, the informal name for an eventual revision of the n3313 proposal. I've also attached the man(7) source. Below is a diff since n3313. Have a lovely day! Alex diff --git i/elementsof.man w/elementsof.man index 7c5a006..7002553 100644 --- i/elementsof.man +++ w/elementsof.man @@ -1,13 +1,13 @@ . . -.TH n3313\~ WG14 2024-08-15 ISO/IEC\~9899 "Proposal for C2y" +.TH n3313.1\~ WG14 202?-??-?? ISO/IEC\~9899 "Proposal for C2y" . . .SH Name . -n3313 +n3313.1 \- -New elementsof() operator (v2) +New nelementsof() operator (v2) . . .SH Category @@ -35,10 +35,6 @@ GNU Compiler Collection Martin Uecker .ME .br -.MT xry...@xry111.site -Xi Ruoyao -.ME -.br .MT xavi....@tutanota.com Xavier Del Campo Romero .ME @@ -98,6 +94,42 @@ Aaron Ballman .MT paulkon...@comcast.net Paul Koning .ME +.br +.MT daniel.lundin.m...@gmail.com +Daniel Lundin +.ME +.br +.MT strni...@protonmail.com +Nikolaos Strimpas +.ME +.br +.MT ferna...@borretti.me +Fernando Borretti +.ME +.br +.MT phdoftheho...@gmail.com +JeanHeyd Meneide +.ME +.br +.MT jonathan.protze...@ens-lyon.org +Jonathan Protzenko +.ME +.br +.MT chris.baz...@arm.com +Chris Bazley +.ME +.br +.MT ville.voutilai...@gmail.com +Ville Voutilainen +.ME +.br +.MT alexg.n...@gmail.com +Alex Celeste +.ME +.br +.MT jakublukasiew...@outlook.com +Jakub Łukasiewicz +.ME . . .SH History @@ -115,6 +147,64 @@ v2; 2024-08-15. .IP New elementsof() operator (v2) +.RS +.IP \[bu] 3 +.PD 0 +Provide an implementation for GCC. +.IP \[bu] +Rename _Lengthof => elementsof. +.IP \[bu] +Clarify when it should result in an integer constant expression. +.IP \[bu] +Require parentheses. +.IP \[bu] +Document prior art. +.IP \[bu] +Document backwards compatibility. +.IP \[bu] +Document reasons for having this operator beyond the pointer safety +(which is already solved with complex macros and/or diagnostics). +.IP \[bu] +Add specific proposed changes to the draft document (based on n3220). +.PD +.RE +.TP +n3313.1 +v3; +202?-??-??. +.IP +New nelementsof() operator (v3) +.IP +n3313.1 is an informal name for a future proposal that supersedes n3313. +.RS +.IP \[bu] 3 +.PD 0 +Rename elementsof => nelementsof. +.IP \[bu] +Rebase on n3301. +.IP \[bu] +Document performance problem of sizeof division. +.IP \[bu] +Document exponential macro growth problem of magic macros. +.IP \[bu] +Fix support for VLAs in example of NITEMS(). +This needs GNU C's +.MR __builtin_types_compatible_p "" . +.IP \[bu] +Remove concerns about double evaluation, +since it's possible to evaluate at most once, +by using +.I typeof +and GNU C's statement expression. +That improvement also adds support for type names, +so also remove that concern. +.IP \[bu] +Add support for compound literals in example of NITEMS(), +by using a variadic macro that will accept commas in the operand. +.IP \[bu] +Fix typo in proposed wording. +.PD +.RE . . .SH Synopsis @@ -126,14 +216,16 @@ This operator yields the number of elements of an array. . .SS 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 number of elements of an array, -that is, -the number of elements in the array. +It is possible to write a macro that +yields the number of elements of an array. +However, it is impossible to reject pointer arguments portably. +Here's an implementation using GNU C: .IP .EX +#define is_same_type(a, b) __builtin_types_compatible_p(a, b) +#define is_same_typeof(a, b) is_same_type(typeof(a), typeof(b)) +#define decay(a) (&*(a)) +#define is_array(a) (!is_same_typeof(a, decay(a))) #define must_be(e) \[rs] ( \[rs] 0 * (int) sizeof( \[rs] @@ -143,38 +235,30 @@ the number of elements in the array. } \[rs] ) \[rs] ) -#define is_array(a) \[rs] -( \[rs] - _Generic(&(a), \[rs] - typeof((a)[0]) **: 0, \[rs] - default: 1 \[rs] - ) \[rs] -) -#define sizeof_array(a) (sizeof(a) + must_be(is_array(a))) -#define nitems(a) (sizeof_array(a) / sizeof((a)[0])) +#define sizeof_array(a) (sizeof(a) + must_be(is_array(a))) +#define sizeof_element(a) sizeof((a)[0]) +#define NITEMS(...) \[rs] +({ \[rs] + typeof(__VA_ARGS__) *ap_; \[rs] + \[rs] + sizeof_array(*ap_) / sizeof_element(*ap_); \[rs] +}) .EE .P While diagnostics could be better, with good helper-macro names, they are decent. . -.SS Type names -. -This -.IR \%nitems () -macro is not ideal, -since it only works with expressions but not with type names. -However, for most use cases that's enough. -. .SS constexpr . -The usual -.I \%sizeof -division +A +.I \%sizeof\c +-based +implementation evaluates the operand and results in a run-time value in cases where it wouldn't be necessary. -If the top-level array number of elements +If the number of elements of the top-level array is determined by an integer constant expression, but an internal array is a VLA, .I \%sizeof @@ -185,22 +269,14 @@ int a[7][n]; int (*p)[7][n]; \& p = &a; -nitems(*p++); +NITEMS(*p++); .EE .P With a -.I \%elementsof +.I \%nelementsof operator, this would result in an integer constant expression of value 7. . -.SS Double evaluation -. -With the -.I \%sizeof\c --based implementation from above, -the example from above causes double evaluation of -.IR *p++ . -. .SS Diagnostics . Having more constant expressions would allow for increased diagnostics, @@ -209,10 +285,10 @@ For example: .IP .EX .RB $\~ "cat f.c" -#define nitems(a) (sizeof(a) / sizeof(*(a))) +#define NITEMS(a) (sizeof(a) / sizeof(*(a))) -void f(char (*a)[3][*], int (*b)[elementsof(*a)]); -void g(char (*a)[3][*], int (*b)[nitems(*a)]); +void f(char (*a)[3][*], int (*b)[nelementsof(*a)]); +void g(char (*a)[3][*], int (*b)[NITEMS(*a)]); int main(void) @@ -224,7 +300,7 @@ main(void) g(&c35, &i5); } \& -.RB $\~ "/opt/local/gnu/gcc/elementsof/bin/gcc f.c" +.RB $\~ "/opt/local/gnu/gcc/nelementsof/bin/gcc f.c" f.c: In function ‘main’: f.c:12:17: error: passing argument 2 of ‘f’ from incompatible pointer type [\-Wincompatible\-pointer\-types] 12 | f(&c35, &i5); @@ -232,15 +308,35 @@ f.c:12:17: error: passing argument 2 of ‘f’ from incompatible pointer type [ | | | int (*)[5] f.c:3:31: note: expected ‘int (*)[3]’ but argument is of type ‘int (*)[5]’ - 3 | void f(char (*a)[3][*], int (*b)[elementsof(*a)]); - | ~~~~~~^~~~~~~~~~~~~~~~ + 3 | void f(char (*a)[3][*], int (*b)[nelementsof(*a)]); + | ~~~~~~^~~~~~~~~~~~~~~~~~~ .EE . +.SS Performance +. +In cases where sizeof evaluates to a run-time value, +the division must be performed at run time. +A new operator would yield the value directly, +exposing information that the compiler already has internally, +without needing a division. +. +.SS Exponential macro expansions +. +Macros that perform type checks on the arguments +need to expand those several times. +When such macros are nested, +the number of expansions grows exponentially, +making compilation slower. +See +.UR https://lwn.net/Articles/983965/ +this <LWN.net> article +.UE . +. . .SH Proposal description . Add a new keyword named -.I \%elementsof +.I \%nelementsof which evaluates to the number of elements of an array operand, that is, the number of elements in the array. @@ -289,12 +385,12 @@ Common names include: .PD 0 \%ARRAY_SIZE() .IP \[bu] +\%NITEMS() +.IP \[bu] \%NELEM() .IP \[bu] \%NELEMS() .IP \[bu] -\%NITEMS() -.IP \[bu] \%NELTS() .IP \[bu] \%elementsof() @@ -335,18 +431,18 @@ even if it is technically possible: .RS .IP .EX -#define DEREFERENCE(a, n) DEREFERENCE_ ## n (a, c) -#define DEREFERENCE_9(a) (*********(a)) -#define DEREFERENCE_8(a) (********(a)) -#define DEREFERENCE_7(a) (*******(a)) -#define DEREFERENCE_6(a) (******(a)) -#define DEREFERENCE_5(a) (*****(a)) -#define DEREFERENCE_4(a) (****(a)) -#define DEREFERENCE_3(a) (***(a)) -#define DEREFERENCE_2(a) (**(a)) -#define DEREFERENCE_1(a) (*(a)) -#define DEREFERENCE_0(a) ((a)) -#define extent(a, n) nitems(DEREFERENCE(a, n)) +#define DEREFERENCE_n(a, n) DEREFERENCE_ ## n (a, c) +#define DEREFERENCE_9(a) (*********(a)) +#define DEREFERENCE_8(a) (********(a)) +#define DEREFERENCE_7(a) (*******(a)) +#define DEREFERENCE_6(a) (******(a)) +#define DEREFERENCE_5(a) (*****(a)) +#define DEREFERENCE_4(a) (****(a)) +#define DEREFERENCE_3(a) (***(a)) +#define DEREFERENCE_2(a) (**(a)) +#define DEREFERENCE_1(a) (*(a)) +#define DEREFERENCE_0(a) ((a)) +#define extent(a, n) NITEMS(DEREFERENCE(a, n)) .EE .RE .IP @@ -403,15 +499,23 @@ elements is a term that programmers are already familiar with. . A code search on large online platforms revealed that while -.I \%elementsof -is already in use by existing projects, -all of them seem to be compatible with our proposal, -by expanding to the usual -.I \%sizeof -division. +.I \%nelementsof +is in use in a single project (that we could find), +and it is semantically compatible with our proposal, +by yielding the number of elements of an array. .P .I \%lengthof is in use with incompatible semantics. +.P +Also, while projects already use names like +.I nelts +for variable names, +they don't use name ending in +.I of +for variable names. +That's more reason to use a name ending in +.I of +which is commonly used only for operator-like macros and functions. . .SS Parentheses . @@ -460,7 +564,7 @@ since it's semantically compatible with existing code. . .SH Future directions . -.I \%elementsof +.I \%nelementsof could be extended to support function parameters declared with array notation. Here's an example @@ -471,9 +575,9 @@ n3188 .IP .EX wchar_t * -wmemset(wchar_t wcs[.n], wchar_t wc, size_t n) +wmemset(wchar_t wcs[.n], wchar_t wc, const size_t n) { - for (size_t i = 0; i < elementsof(wcs); i++) + for (size_t i = 0; i < nelementsof(wcs); i++) wcs[i] = wc; \& return wcs; @@ -498,15 +602,15 @@ Or just the nice name directly? . .SH Proposed wording . -.SS 6.3.2.1 Lvalues, arrays, and function designators +.SS 6.3.3.1 Lvalues, arrays, and function designators . .TP p3 .IP .EX Except when it is the operand of the sizeof operator, -+or the elementsof operator, - or the typeof operators, ++or the nelementsof operator, + or typeof operators, or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" @@ -518,21 +622,21 @@ p3 Forward references .IP .EX - prefix increment and decrement operators (6.5.4.1), -\-the sizeof and alignof operators (6.5.4.4), -+the sizeof, elementsof, and alignof operators (6.5.4.4), + prefix increment and decrement operators (6.5.4.2), +\-the sizeof and alignof operators (6.5.4.5), ++the sizeof, nelementsof, and alignof operators (6.5.4.5), structure and union members (6.5.3.4). .EE . -.SS 6.4.1 Keywords +.SS 6.4.2 Keywords . .TP Syntax (p1) .IP .EX - double -+elementsof - else + long ++nelementsof + nullptr .EE . .SS 6.5.4 Unary operators @@ -548,25 +652,25 @@ Syntax (p1) unary-operator cast-expression sizeof unary-expression sizeof ( type-name ) -+ elementsof ( type-name ) ++ nelementsof ( type-name ) alignof ( type-name ) .EE . -.SS 6.5.4.4 The sizeof and alignof operators +.SS 6.5.4.5 The sizeof and alignof operators . .TP Title .IP .EX \-The sizeof and alignof operators -+The sizeof, elementsof, and alignof operators ++The sizeof, nelementsof, and alignof operators .EE .TP Constraints (p1) .IP .EX or to an expression that designates a bit-field member. -+The elementsof operator shall not be applied to an expression that ++The nelementsof operator shall not be applied to an expression that +has an incomplete type or +does not have array type, +or to the parenthesized name of such a type. @@ -577,22 +681,26 @@ Constraints (p1) Semantics (pX; insert as p2) .IP .EX -+The elementsof operator yields the number of elements ++The nelementsof 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, -+the operand is not evaluated and the result is an integer constant. ++the operand is not evaluated ++and the result is an integer constant expression. .EE .TP EXAMPLE 2 (p7) +.IP +.EX \-Another use of the sizeof operator is -+A use of the elementsof operator is ++A use of the nelementsof operator is to compute the number of elements in an array \- sizeof array / sizeof array[0] -+ elementsof array ++ nelementsof(array) +.EE . .SS 6.6 Constant expressions . @@ -600,13 +708,14 @@ EXAMPLE 2 (p7) Semantics (p8) .IP .EX - An integer constant expression117) shall have integer type + An integer constant expression115) shall have integer type and shall only have operands that are - integer constants, + integer literals, named and compound literal constants of integer type, character constants, -\-sizeof expressions whose results are integer constants, -+sizeof or elementsof expressions whose results are integer constants, +\-sizeof expressions ++sizeof or nelementsof expressions + whose results are integer constant expressions, alignof expressions, and floating, named, or compound literal constants of arithmetic type that are the immediate operands of casts. @@ -614,17 +723,17 @@ Semantics (p8) shall only convert arithmetic types to integer types, except as part of an operand to the typeof operators, sizeof operator, -+elementsof operator, ++nelementsof operator, or alignof operator. .EE .TP -Footnote 115) +Footnote 113) .IP .EX The operand of a typeof (6.7.3.6), sizeof, -+elementsof, ++nelementsof, or alignof operator is usually not evaluated (6.5.4.4). .EE @@ -635,25 +744,26 @@ Semantics (p10) An arithmetic constant expression shall have arithmetic type and shall only have operands that are - integer constants, - floating constants, + integer literals, + floating literals, named or compound literal constants of arithmetic type, - character constants, -\-sizeof expressions whose results are integer constants, -+sizeof or elementsof expressions whose results are integer constants, + character literals, +\-sizeof expressions ++sizeof or nelementsof expressions + whose results are integer constant expressions, and alignof expressions. Cast operators in an arithmetic constant expression shall only convert arithmetic types to arithmetic types, except as part of an operand to the typeof operators, sizeof operator, -+elementsof operator, ++nelementsof operator, or alignof operator. .EE . .SS 6.7.2 Storage-class specifiers . .TP -Footnote 128) +Footnote 127) .IP .EX The implementation can treat any register declaration simply @@ -674,7 +784,7 @@ Footnote 128) an array declared with storage-class specifier register \-is sizeof +are sizeof, -+elementsof, ++nelementsof, and the typeof operators. .EE . @@ -690,7 +800,7 @@ Semantics (p5) would not affect the result of the operator, it is unspecified whether or not the size expression is evaluated. +Where a size expression is part of -+the operand of a elementsof operator ++the operand of a nelementsof operator +and changing the value of the size expression +would not affect the result of the operator, +the size expression is not evaluated. @@ -706,19 +816,20 @@ Constraints (p3) .IP .EX \[bu] part of the operand of a sizeof operator - whose result is an integer constant; -+\[bu] part of the operand of a elementsof operator - whose result is an integer constant; + whose result is an integer constant expression; ++\[bu] part of the operand of a nelementsof operator ++ whose result is an integer constant expression; \[bu] part of the operand of an alignof operator - whose result is an integer constant; + whose result is an integer constant expression; .EE .TP Semantics (p5) .IP .EX An external definition is - an external declaration that is also a definition of - a function (other than an inline definition) + an external declaration + that is also a definition of a function + (other than an inline definition) or an object. If an identifier declared with external linkage is used in an expression @@ -729,11 +840,11 @@ Semantics (p5) part of the expression in a generic association that is not the result expression of its generic selection, \-or part of a sizeof or alignof operator -+or part of a sizeof, elementsof, or alignof operator ++or part of a sizeof, nelementsof, or alignof operator whose result is an integer constant expression), somewhere in the entire program there shall be exactly one external definition for the identifier; - otherwise, there shall be no more than one.191) + otherwise, there shall be no more than one.190) .EE . .SS 6.10.2 Conditional inclusion @@ -743,7 +854,7 @@ EXAMPLE 5 (p22) .IP .EX \- return (int)(meow[0] + meow[(sizeof(meow) / sizeof(*meow)) \- 1]); -+ return (int)(meow[0] + meow[elementsof(meow) \- 1]); ++ return (int)(meow[0] + meow[nelementsof(meow) \- 1]); .EE . .SS 6.10.4.1 #embed preprocessing directive @@ -753,14 +864,14 @@ EXAMPLE 1 (p16) .IP .EX \- have_you_any_wool(baa_baa, sizeof(baa_baa)); -+ have_you_any_wool(baa_baa, elementsof(baa_baa)); ++ have_you_any_wool(baa_baa, nelementsof(baa_baa)); .EE .TP EXAMPLE 4 (p19) .IP .EX \- const size_t f_size = sizeof(embed_data); -+ const size_t f_n = elementsof(embed_data); ++ const size_t f_n = nelementsof(embed_data); \- unsigned char f_data[f_size]; + unsigned char f_data[f_n]; FILE* f_source = fopen("data.dat", "rb"); @@ -786,7 +897,7 @@ EXAMPLE 1 (p5) .EX \- static_assert((sizeof(sound_signature) / sizeof(*sound_signature)) == 4, \- "There should only be 4 elements in this array."); -+ static_assert(elementsof(sound_signature) == 4); ++ static_assert(nelementsof(sound_signature) == 4); .EE .TP EXAMPLE 2 (p6) @@ -794,7 +905,7 @@ EXAMPLE 2 (p6) .EX \- static_assert((sizeof(sound_signature) / sizeof(*sound_signature)) == 4, \- "There should only be 4 elements in this array."); -+ static_assert(elementsof(sound_signature) == 4); ++ static_assert(nelementsof(sound_signature) == 4); .EE . .SS 6.10.4.4 prefix parameter @@ -804,10 +915,10 @@ EXAMPLE (p4) .IP .EX \- int is_good = (sizeof(whl) == 1 && whl[0] == \[aq]\0\[aq]) -+ int is_good = (elementsof(whl) == 1 && whl[0] == \[aq]\0\[aq]) ++ int is_good = (nelementsof(whl) == 1 && whl[0] == \[aq]\0\[aq]) || (whl[0] == \[aq]\[rs]xEF\[aq] && whl[1] == \[aq]\[rs]xBB\[aq] \- && whl[2] == \[aq]\[rs]xBF\[aq] && whl[sizeof(whl) \- 1] == \[aq]\0\[aq]); -+ && whl[2] == \[aq]\[rs]xBF\[aq] && whl[elementsof(whl) \- 1] == \[aq]\0\[aq]); ++ && whl[2] == \[aq]\[rs]xBF\[aq] && whl[nelementsof(whl) \- 1] == \[aq]\0\[aq]); .EE . .SS A.2.2 Keywords @@ -816,15 +927,15 @@ EXAMPLE (p4) (6.4.1) .IP .EX - double -+elementsof - else + long ++nelementsof + nullptr .EE . .SS A.3.1 Expressions . .TP -(6.5.4) +(6.5.4.1) .IP .EX unary-expression: @@ -834,51 +945,51 @@ EXAMPLE (p4) unary-operator cast-expression sizeof unary-expression sizeof ( type-name ) -+ elementsof ( type-name ) ++ nelementsof ( type-name ) alignof ( type-name ) .EE . .SS J.2 Undefined behavior . .TP -(52) +(50) .IP .EX An expression that is required to be an integer constant expression does not have an integer type; - has operands that are not integer constants, + has operands that are not integer literals, named constants, compound literal constants, enumeration constants, - character constants, + character literals, predefined constants, \-sizeof expressions -+sizeof or elementsof expressions - whose results are integer constants, ++sizeof or nelementsof expressions + whose results are integer constant expression, alignof expressions, - or immediately-cast floating constants; + or immediately-cast floating literals; or contains casts \-(outside operands to sizeof and alignof operators) -+(outside operands to sizeof, elementsof, and alignof operators) ++(outside operands to sizeof, nelementsof, and alignof operators) other than conversions of arithmetic types to integer types (6.6). .EE .TP -(54) +(52) .IP .EX An arithmetic constant expression does not have arithmetic type; - has operands that are not integer constants, - floating constants, + has operands that are not integer literals, + floating literals, named and compound literal constants of arithmetic type, - character constants, + character literals, predefined constants, \-sizeof expressions -+sizeof or elementsof expressions - whose results are integer constants, ++sizeof or nelementsof expressions + whose results are integer constant expressions, or alignof expressions; or contains casts \-(outside operands to sizeof or alignof operators) -+(outside operands to sizeof, elementsof, or alignof operators) ++(outside operands to sizeof, nelementsof, or alignof operators) other than conversions of arithmetic types to arithmetic types (6.6). .EE . @@ -888,19 +999,19 @@ EXAMPLE (p4) p2 .IP .EX - dsubl -+elementsof - elif + negative_sign ++nelementsof + nextafterd128 .EE . -.SS K.3.5.3.3 The fscanf_s function +.SS K.3.5.4.3 The fscanf_s function . .TP EXAMPLE 2 (p8) .IP .EX \- n = fscanf_s(stdin, "%s", s, sizeof s); -+ n = fscanf_s(stdin, "%s", s, elementsof(s)); ++ n = fscanf_s(stdin, "%s", s, nelementsof(s)); .EE . .SS K.3.7.4.1 The strtok_s function @@ -911,8 +1022,8 @@ EXAMPLE (p10) .EX \- rsize_t max1 = sizeof(str1); \- rsize_t max2 = sizeof(str2); -+ rsize_t max1 = elementsof(str1); -+ rsize_t max2 = elementsof(str2); ++ rsize_t max1 = nelementsof(str1); ++ rsize_t max2 = nelementsof(str2); .EE . .SS K.3.9.4.1.2 The wcrtomb_s function @@ -922,7 +1033,7 @@ Description (p4) .IP .EX \- wcrtomb_s(&retval, buf, sizeof buf, L’\0’, ps) -+ wcrtomb_s(&retval, buf, elementsof(buf), L’\0’, ps) ++ wcrtomb_s(&retval, buf, nelementsof(buf), L’\0’, ps) .EE . . @@ -933,6 +1044,6 @@ The discussion .UE of a patch set implementing an -.I __elementsof__ +.I __nelementsof__ operator in GCC. It also discusses drafts of this paper. -- <https://www.alejandro-colomar.es/>
elementsof.pdf
Description: Adobe PDF document
elementsof.man
Description: Unix manual page
signature.asc
Description: PGP signature