> On Jan 23, 2025, at 13:27, Martin Uecker <uec...@tugraz.at> wrote: > > Am Donnerstag, dem 23.01.2025 um 17:39 +0000 schrieb Qing Zhao: >> >>> On Jan 22, 2025, at 12:20, Martin Uecker <uec...@tugraz.at> wrote: >>> >>> Am Mittwoch, dem 22.01.2025 um 18:11 +0100 schrieb Martin Uecker: >>>> Am Mittwoch, dem 22.01.2025 um 16:37 +0000 schrieb Qing Zhao: >>>>> >>>>>> On Jan 22, 2025, at 11:22, Martin Uecker <uec...@tugraz.at> wrote: >>>>>> >>>>>> >>>>>> Hello Michael, >>>>>> >>>>>> Am Mittwoch, dem 22.01.2025 um 16:54 +0100 schrieb Michael Matz: >>>>>>> On Wed, 22 Jan 2025, Martin Uecker wrote: >>>>>>> >>>>>>>>>> So you do not need to look further. But maybe I am missing >>>>>>>>>> something >>>>>>>>>> else. >>>>>>>>> >>>>>>>>> Like ... >>>>>>>>> >>>>>>>>>>> Note further that you may have '{ .y[1][3].z }', which is still not >>>>>>>>>>> a >>>>>>>>>>> designation, but an expression under your proposal, whereas >>>>>>>>>>> '{ .y[1][3].z = 1 }' would remain a designation. This shows that >>>>>>>>>>> you >>>>>>>>>>> now need arbitrary look-ahead to disambiguate the two. A Very Bad >>>>>>>>>>> Idea. >>>>>>>>> >>>>>>>>> ... this? >>>>>>>> >>>>>>>> In .y[1][3].z after .y you can decide whether y is a member of the >>>>>>>> struct being initialized. If it is, it is a designator and if not >>>>>>>> it must be an expression. >>>>>>> >>>>>>> If y is not a member it must be an expression, true. But if it's a >>>>>>> member >>>>>>> you don't know, it may be a designation or an expression. >>>>>> >>>>>> In an initializer I know all the members. >>>>> >>>>> I am not familiar with the parser, so, I am a little confused about the >>>>> following: >>>>> >>>>> Suppose we have: >>>>> >>>>> struct foo { >>>>> int z; >>>>> float f; >>>>> } >>>>> >>>>> struct bar { >>>>> char *array __attribute__ ((counted_by (.y[1][3].z + 4))); >>>>> struct foo y[5][10]; >>>>> } >>>>> >>>>> So, in the above, when parsing the above expression inside counted_by, >>>>> can the >>>>> current parser be easily to be extended to parse it? >>>> >>>> No, I don't think this can be done easily. The issue is that you do >>>> not know the declaration for y because it hasn't been parsed yet. >>>> >>>> If you forward reference some struct member, you have several >>>> possibilities: >>>> >>>> - use it only in limited contexts where you do not need to know >>>> the type (e.g. this works for goto labels) or for a basic >>>> counted_by attribute that only takes an identifier as we have it now. >>>> >>>> - simply assume it has a certain type (size_t as is proposed in the >>>> WG14 paper Joseph mentioned) and fail later if it does not. >>>> >>>> >>>> Both options would rule the construct above (but there could be >>>> workarounds). >>> >>> One of the workarounds could be to instead call a function (which could >>> be inlined later) and that function takes a pointer to the member. >>> Then it does not need to now anything about any member, e.g.: >>> >>> >>> struct foo { >>> int z; >>> float f; >>> } >>> >>> size_t bar_count(struct bar *); >>> >>> struct bar { >>> char *array __attribute__ ((counted_by (bar_count(__self__)))); >>> struct foo y[5][10]; >>> } >>> >>> size_t bar_count(struct bar *p) >>> { >>> return p->y[1][3].z +4; >>> } >>> >>> >> In this workaround, we also need to introduce a new key word “__self__”, >> Is this the same as the “__self__” Joseph mentioned in a previous email >> (I copied Joseph’s word in below for easy reference): > > I think it could be same, although Joseph uses it with a member access. > > One could also use designator syntax (without __self__) and pass a reference > to the member only (which also seems appropriate for counted_by). > > struct foo { > int z; > float f; > } > > size_t bar_count(struct foo (*yp)[5][10]); > > struct bar { > char *array __attribute__ ((counted_by (bar_count(&.y)))); > struct foo y[5][10]; > } > > size_t bar_count(struct foo (*yp)[5][10]) > { > return (*yp)[1][3].z +4; > } >
So, in the above, “bar_count” is still a function call, right? Instead passing “__self__” to the function “bar_count”, the above passed a reference to the member (as &.y) to the function “bar_count”. > > I still prefer the version without __self__ and will respond to Michael > why I think so, but I am also fine *with* __self__. I just think the > delayed parsing version used in the Apple prototype should be avoided, > because it is very confusing (and because it needs delayed parsing). > > >> >> >> "But if you want a less-limited feature that allows for expressions, you >> need some syntax for referring to a structure member that's not ambiguous. >> For example, some new notation such as __self__.len1 to refer to a member >> of the closest enclosing structure definition when in counted_by (while >> being invalid except in counted_by inside a structure definition). >> (That's just one example of how you might define syntax that avoids >> ambiguity.)” >> >> So, for the following two approaches to represent expression as argument of >> “counted_by” attribute: >> >> A. Allowing function call as the argument, and the new key word “__self__” >> is passed to this function to >> reference members of the closest enclosing structure. >> >>> struct bar { >>> char *array __attribute__ ((counted_by (bar_count(__self__)))); >>> struct foo y[5][10]; >>> } >> >> >> B. Allowing expression with __self__ as the argument: >> >>> struct bar { >>> char *array __attribute__ ((counted_by (__self__.y[1][3].z + 4))); >>> struct foo y[5][10]; >>> } >> >> >> My question: >> >> Which is better, A or B? > > B does not work, because you can not parse the expression using the > existing parser because the type of y is unknown. Okay. I see. > > I also do not really like A because I think we should not allow function > calls, so I tend to favour the version with the cast at the moment. A little confused here-:) So, your current favor is the following: (i.e, you mentioned in the beginning of this email): > size_t bar_count(struct foo (*yp)[5][10]); > > struct bar { > char *array __attribute__ ((counted_by (bar_count(&.y)))); > struct foo y[5][10]; > } > > size_t bar_count(struct foo (*yp)[5][10]) > { > return (*yp)[1][3].z +4; > } Or something else? Qing > > Martin > >> >> >> Thanks a lot for your help. >> >> Qing >> >>> >>>> Other alternatives are: >>>> >>>> - you have same kind of forward declaration (as we have for >>>> parameters as GNU extension). In the context of C, this is the >>>> cleanest solution but either requires forward declaring the >>>> full struct (which can be done in C23) or new syntax for only >>>> forward declaring the member. >>> >>> A possible C23 workaround could be: >>> >>> struct foo { >>> int z; >>> float f; >>> } >>> >>> struct bar { >>> char *array __attribute__ ((counted_by (*))); >>> // star indicates missing size exppression >>> struct foo y[5][10]; >>> } >>> >>> struct bar { // redeclare with known size >>> char *array __attribute__ ((counted_by (.y[1][3].z + 4))); >>> struct foo y[5][10]; >>> } >>> >>> >>> Martin >>> >>> >>>> >>>> - or you use some delayed parsing where you store away the tokens >>>> and parse it later when all structure members are done. I think >>>> this is a highly problematic approach for a variety of reasons. >>>> >>>> >>>> Martin >>>> >>>> >>>>> >>>>> thanks. >>>>> >>>>> Qing >>>>>> >>>>>> Martin