On Thu, Sep 29, 2022 at 02:42:05PM +0100, Richard W.M. Jones wrote:
> > + (* Output __attribute__((nonnull)) for the function
parameters:
> > + * eg. struct nbd_handle *, int, char *
> > + * => [ true, false, true ]
> > + * => LIBNBD_ATTRIBUTE_NONNULL((1,3))
> > + * => __attribute__((nonnull,(1,3)))
>
> Style question. Do we want to REQUIRE the clients of this macro to
> pass in (), or would it be better to have a varargs format?
>
> > + *)
> > + let nns : bool list =
> > + [ true ] (* struct nbd_handle * *)
> > + @ List.flatten (List.map arg_attr_nonnull args)
> > + @ List.flatten (List.map optarg_attr_nonnull optargs) in
> > + let nns = List.mapi (fun i b -> (i+1, b)) nns in
> > + let nns = filter_map (fun (i, b) -> if b then Some i else None) nns in
> > + let nns : string list = List.map string_of_int nns in
> > + pr "\n LIBNBD_ATTRIBUTE_NONNULL((%s));\n" (String.concat
"," nns)
>
> For generated code, it is just as easy to cope with either style (we
> can strip a layer of () if we want a varargs format).
>
...
> > + pr "#ifndef LIBNBD_ATTRIBUTE_NONNULL\n";
> > + pr "#if defined(__GNUC__) && LIBNBD_GCC_VERSION >= 120000 /*
gcc >= 12.0 */\n";
> > + pr "#define LIBNBD_ATTRIBUTE_NONNULL(s) __attribute__((__nonnull__
s))\n";
>
> This definition is what requires us to pass in our own (). That is,
> our end result is going to be one of:
>
> __attribute__((__nonnull__(1) ))
> __attribute__((__nonnull__(1, 2) ))
>
> but the difference is whether we must pass exactly one macro argument,
> and where that argument must include () even when there is only one
> parameter to be marked (what you coded):
>
> LIBNBD_ATTRIBUTE_NONNULL((1))
> LIBNBD_ATTRIBUTE_NONNULL((1, 3))
>
> vs. ease-of-use in supplying the () as part of the macro definition
> itself by using #define MACRO(...) and __VA_ARGS__:
>
> LIBNBD_ATTRIBUTE_NONNULL(1)
> LIBNBD_ATTRIBUTE_NONNULL(1, 3)
I'm not sure I understand - what does the second definition look like?
Using a shorter name for testing:
$ cat foo.c
#define my(...) __attribute__((__nonnull__(__VA_ARGS__)))
extern void foo (char *a) my (1);
extern void bar (char *a, char *b) my (1, 2);
$ 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"
extern void foo (char *a) __attribute__((__nonnull__(1)));
extern void bar (char *a, char *b) __attribute__((__nonnull__(1, 2)));
$ gcc -o foo.o -c foo.c
$ # compiled, so we satisfied gcc's attribute syntax
and similarly,
#define LIBNBD_ATTRIBUTE_NONNULL(...) /* no-op */
when disabling the macro.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3266
Virtualization:
qemu.org |
libvirt.org