From: Markus Armbruster <armbru@redhat.com>
To: Eric Blake <eblake@redhat.com>
Cc: Markus Armbruster <armbru@redhat.com>,
qemu-devel@nongnu.org, kwolf@redhat.com, hreitz@redhat.com,
vsementsov@yandex-team.ru, jsnow@redhat.com,
idryomov@gmail.com, sw@weilnetz.de, sstabellini@kernel.org,
anthony.perard@citrix.com, paul@xen.org, pbonzini@redhat.com,
marcandre.lureau@redhat.com, berrange@redhat.com,
thuth@redhat.com, philmd@linaro.org, stefanha@redhat.com,
fam@euphon.net, quintela@redhat.com, peterx@redhat.com,
leobras@redhat.com, kraxel@redhat.com, qemu-block@nongnu.org,
xen-devel@lists.xenproject.org, alex.bennee@linaro.org,
peter.maydell@linaro.org
Subject: Re: [PATCH 7/7] qobject atomics osdep: Make a few macros more hygienic
Date: Tue, 19 Sep 2023 08:29:58 +0200 [thread overview]
Message-ID: <87v8c63cbd.fsf@pond.sub.org> (raw)
In-Reply-To: <viam47z5ascty5zluzvj3byrrrp2fe6jh6vevcaggpozxwzabj@avo7fb3gs7bt> (Eric Blake's message of "Fri, 1 Sep 2023 08:18:38 -0500")
Eric Blake <eblake@redhat.com> writes:
> On Fri, Sep 01, 2023 at 10:48:26AM +0200, Markus Armbruster wrote:
>> > Indeed, not fully understanding the preprocessor makes for some
>> > interesting death traps.
>>
>> We use ALL_CAPS for macros to signal "watch out for traps".
>>
>
>> >> -#define QOBJECT(obj) ({ \
>> >> - typeof(obj) _obj = (obj); \
>> >> - _obj ? container_of(&(_obj)->base, QObject, base) : NULL; \
>> >> +#define QOBJECT_INTERNAL(obj, l) ({ \
>> >> + typeof(obj) PASTE(_obj, l) = (obj); \
>> >> + PASTE(_obj, l) \
>> >> + ? container_of(&(PASTE(_obj, l))->base, QObject, base) : NULL; \
>> >> })
>> >> +#define QOBJECT(obj) QOBJECT_INTERNAL((obj), __COUNTER__)
>> >
>> > Slick! Every call to QOBJECT() defines a unique temporary variable
>> > name. But as written, QOBJECT(o) expands to this (when __COUNTER__ is
>> > 1):
>> >
>> > ({
>> > typeof((o)) _obj1 = ((o));
>> > _obj1 ? container_of(&(_obj1)->base, QObject, base) : NULL;
>> > })
>> >
>> > which has three sets of redundant parens that could be elided. Why do
>> > you need to add () around obj when forwarding to QOBJECT_INTERNAL()?
>>
>> Habit: enclose macro arguments in parenthesis unless there's a specific
>> need not to.
>>
>> > The only way the expansion of the text passed through the 'obj'
>> > parameter can contain a comma is if the user has already parenthesized
>> > it on their end (not that I expect a comma expression to be a frequent
>> > argument to QOBJECT(), but who knows). Arguing that it is to silence
>> > a syntax checker is weak; since we must NOT add parens around the
>> > parameters to QOBJECT_INTERNAL (calling PASTE((_obj), (l)) is
>> > obviously wrong).
>> >
>> > Meanwhile, the repetition of three calls to PASTE() is annoying. How
>> > about:
>> >
>> > #define QOBJECT_INTERNAL_2(_obj, _tmp) ({ \
>> > typeof _obj _tmp = _obj; \
>> > _tmp ? container_of(&_tmp->base, QObject, base) : NULL; \
>> > )}
>> > #define QOBJECT_INTERNAL_1(_arg, _tmp) QOBJECT_INTERNAL_2(_arg, _tmp)
>> > #define QOBJECT_INTERNAL(_arg, _ctr) \
>> > QOBJECT_INTERNAL_1(_arg, PASTE(_obj, _ctr))
>> > #define QOBJECT(obj) QOBJECT_INTERNAL((obj), __COUNTER__)
>> >
>> > or:
>> >
>> > #define QOBJECT_INTERNAL_2(_obj, _tmp) ({ \
>> > typeof(_obj) _tmp = (_obj); \
>> > _tmp ? container_of(&_tmp->base, QObject, base) : NULL; \
>> > )}
>> > #define QOBJECT_INTERNAL_1(_arg, _tmp) QOBJECT_INTERNAL_2(_arg, _tmp)
>> > #define QOBJECT_INTERNAL(_arg, _ctr) \
>> > QOBJECT_INTERNAL_1(_arg, PASTE(_obj, _ctr))
>> > #define QOBJECT(obj) QOBJECT_INTERNAL(obj, __COUNTER__)
>> >
>> > where, in either case, QOBJECT(o) should then expand to
>> >
>> > ({
>> > typeof (o) _obj1 = (o);
>> > _obj1 ? container_of(&_obj1->base, QObject, base) : NULL;
>> > })
>>
>> The idea is to have the innermost macro take the variable name instead
>> of the counter. "PASTE(_obj, l)" then becomes "_tmp" there, which is
>> more legible. However, we pay for it by going through two more macros.
>>
>> Can you explain why you need two more?
>
> Likewise habit, on my part (and laziness - I wrote the previous email
> without testing anything). I've learned over the years that pasting is
> hard (you can't mix ## with a macro that you want expanded without 2
> layers of indirection), so I started out by adding two layers of
> QOBJECT_INTERNAL, then wrote QOBJECT to forward to QOBJECT_INTERNAL.
> But now that you asked, I actually spent the time to test with the
> preprocessor, and your hunch is indeed correct: I over-compensated
> becaues of my habit.
>
> $cat foo.c
> #define PASTE(_a, _b) _a##_b
>
> #define QOBJECT_INTERNAL_2(_obj, _tmp) ({ \
> typeof _obj _tmp = _obj; \
> _tmp ? container_of(&_tmp->base, QObject, base) : NULL; \
> )}
> #define QOBJECT_INTERNAL_1(_arg, _tmp) QOBJECT_INTERNAL_2(_arg, _tmp)
> #define QOBJECT_INTERNAL(_arg, _ctr) \
> QOBJECT_INTERNAL_1(_arg, PASTE(_obj, _ctr))
> #define QOBJECT(obj) QOBJECT_INTERNAL((obj), __COUNTER__)
>
> QOBJECT(o)
>
> #define Q_INTERNAL_1(_obj, _tmp) ({ \
> typeof _obj _tmp = _obj; \
> _tmp ? container_of(&_tmp->base, QObject, base) : NULL; \
> )}
> #define Q_INTERNAL(_arg, _ctr) \
> Q_INTERNAL_1(_arg, PASTE(_obj, _ctr))
> #define Q(obj) Q_INTERNAL((obj), __COUNTER__)
>
> Q(o)
> $ gcc -E foo.c
> # 0 "foo.c"
> # 0 "<built-in>"
> # 0 "<command-line>"
> # 1 "/usr/include/stdc-predef.h" 1 3 4
> # 0 "<command-line>" 2
> # 1 "foo.c"
> # 12 "foo.c"
> ({ typeof (o) _obj0 = (o); _obj0 ? container_of(&_obj0->base, QObject, base) : NULL; )}
> # 22 "foo.c"
> ({ typeof (o) _obj1 = (o); _obj1 ? container_of(&_obj1->base, QObject, base) : NULL; )}
>
> So the important part was merely ensuring that __COUNTER__ has a
> chance to be expanded PRIOR to the point where ## gets its argument,
> and one less layer of indirection was still sufficient for that.
The version with less indirection is easier to understand for me.
>>
>> Complication: the innermost macro needs to take all its variable names
>> as arguments. The macro above has just one. Macros below have two.
>>
>> Not quite sure which version is easier to understand.
>
> Proper comments may help; once you realize that you are passing in the
> unique variable name(s) to be used as the temporary identifier(s),
> rather than an integer that still needs to be glued, then separating
> the task of generating name(s) (which is done once per name, instead
> of repeated 3 times) makes sense to me. I also like minimizing the
> number of times I have to spell out __COUNTER__; wrapping unique name
> generation behind a well-named helper macro gives a better view of why
> it is needed in the first place.
I can add this comment to every instance of the __COUNTER__ trick:
/*
* Preprocessor wizardry ahead: glue(_val, l) expands to a new
* identifier in each macro expansion. Helps avoid shadowing
* variables and the unwanted name captures that come with it.
*/
> At any rate, this comment still applies:
>
>> >
>> > I think you are definitely on the right track to have all internal
>> > variable declarations within a macro definition use multi-layered
>> > expansion with the help of __COUNTER__ to ensure that the macro's
>> > temporary variable is globally unique; so if you leave it as written,
>> > I could live with:
>> >
>> > Reviewed-by: Eric Blake <eblake@redhat.com>
>> >
>> > But if you respin it to pick up any of my suggestions, I'll definitely
>> > spend another review cycle on the result.
>> >
>> > If it helps, here's what I ended up using in nbdkit:
>> >
>> > https://gitlab.com/nbdkit/nbdkit/-/blame/master/common/include/unique-name.h#L36
>> > /* https://stackoverflow.com/a/1597129
>> > * https://stackoverflow.com/a/12711226
>> > */
>> > #define XXUNIQUE_NAME(name, counter) name ## counter
>> > #define XUNIQUE_NAME(name, counter) XXUNIQUE_NAME (name, counter)
>> > #define NBDKIT_UNIQUE_NAME(name) XUNIQUE_NAME (name, __COUNTER__)
>> >
>> > https://gitlab.com/nbdkit/nbdkit/-/blame/master/common/include/minmax.h#L47
>> > #include "unique-name.h"
>> >
>> > #undef MIN
>> > #define MIN(x, y) \
>> > MIN_1 ((x), (y), NBDKIT_UNIQUE_NAME (_x), NBDKIT_UNIQUE_NAME (_y))
>> >
>> > ...
>> > #define MIN_1(x, y, _x, _y) ({ \
>> > __auto_type _x = (x); \
>> > __auto_type _y = (y); \
>> > _x < _y ? _x : _y; \
>> > })
>>
>> Thanks!
>
> and so I'm fine leaving it up to you if you want to copy the paradigm
> I've seen elsewhere, or if you want to leave it as you first proposed.
> The end result is the same, and it is more of a judgment call on which
> form is easier for you to reason about.
I need to review the two competing versions once more to decide which I
find easier to read. Or shall I say less hard; preprocessor wizardry is
never really easy.
> But as other commenters mentioned, we already have a glue() macro, so
> use that instead of PASTE(), no matter what else you choose.
>
> Looking forward to v2!
Thanks again!
next prev parent reply other threads:[~2023-09-19 6:31 UTC|newest]
Thread overview: 39+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-08-31 13:25 [PATCH 0/7] Steps towards enabling -Wshadow=local Markus Armbruster
2023-08-31 13:25 ` [PATCH 1/7] migration/rdma: Fix save_page method to fail on polling error Markus Armbruster
2023-08-31 13:38 ` Eric Blake
2023-09-19 5:40 ` Markus Armbruster
2023-08-31 20:17 ` Peter Xu
2023-09-06 3:48 ` Zhijian Li (Fujitsu)
2023-08-31 13:25 ` [PATCH 2/7] migration: Clean up local variable shadowing Markus Armbruster
2023-08-31 20:19 ` Peter Xu
2023-08-31 13:25 ` [PATCH 3/7] ui: " Markus Armbruster
2023-08-31 14:53 ` Peter Maydell
2023-09-01 8:11 ` Markus Armbruster
2023-08-31 13:25 ` [PATCH 4/7] block/dirty-bitmap: " Markus Armbruster
2023-08-31 19:29 ` Stefan Hajnoczi
2023-09-15 7:52 ` Kevin Wolf
2023-09-19 5:48 ` Markus Armbruster
2023-09-19 9:45 ` Kevin Wolf
2023-09-20 13:38 ` Markus Armbruster
2023-08-31 13:25 ` [PATCH 5/7] block/vdi: " Markus Armbruster
2023-08-31 19:26 ` Stefan Hajnoczi
2023-09-15 7:41 ` Kevin Wolf
2023-09-18 14:47 ` Markus Armbruster
2023-08-31 13:25 ` [PATCH 6/7] block: " Markus Armbruster
2023-08-31 19:24 ` Stefan Hajnoczi
2023-09-11 10:44 ` Anthony PERARD
2023-09-11 11:17 ` Ilya Dryomov
2023-09-15 8:10 ` Kevin Wolf
2023-09-18 14:49 ` Markus Armbruster
2023-08-31 13:25 ` [PATCH 7/7] qobject atomics osdep: Make a few macros more hygienic Markus Armbruster
2023-08-31 14:30 ` Eric Blake
2023-09-01 8:48 ` Markus Armbruster
2023-09-01 13:18 ` Eric Blake
2023-09-19 6:29 ` Markus Armbruster [this message]
2023-09-01 12:59 ` Cédric Le Goater
2023-09-01 14:31 ` Philippe Mathieu-Daudé
2023-09-01 14:50 ` Markus Armbruster
2023-09-01 14:54 ` Cédric Le Goater
2023-08-31 15:52 ` Richard Henderson
2023-09-01 8:12 ` Markus Armbruster
2023-09-01 8:05 ` [PATCH 0/7] Steps towards enabling -Wshadow=local Markus Armbruster
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87v8c63cbd.fsf@pond.sub.org \
--to=armbru@redhat.com \
--cc=alex.bennee@linaro.org \
--cc=anthony.perard@citrix.com \
--cc=berrange@redhat.com \
--cc=eblake@redhat.com \
--cc=fam@euphon.net \
--cc=hreitz@redhat.com \
--cc=idryomov@gmail.com \
--cc=jsnow@redhat.com \
--cc=kraxel@redhat.com \
--cc=kwolf@redhat.com \
--cc=leobras@redhat.com \
--cc=marcandre.lureau@redhat.com \
--cc=paul@xen.org \
--cc=pbonzini@redhat.com \
--cc=peter.maydell@linaro.org \
--cc=peterx@redhat.com \
--cc=philmd@linaro.org \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=quintela@redhat.com \
--cc=sstabellini@kernel.org \
--cc=stefanha@redhat.com \
--cc=sw@weilnetz.de \
--cc=thuth@redhat.com \
--cc=vsementsov@yandex-team.ru \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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 NNTP newsgroup(s).