About those snprintf invocation macros - Mailing list pgsql-hackers

From Tom Lane
Subject About those snprintf invocation macros
Date
Msg-id 22709.1535135640@sss.pgh.pa.us
Whole thread Raw
List pgsql-hackers
Commit 8ecdefc26 reminded me of something I'd intended to whine about
and forgot.  port.h has this in its USE_REPL_SNPRINTF stanza:

/*
 *    The GCC-specific code below prevents the pg_attribute_printf above from
 *    being replaced, and this is required because gcc doesn't know anything
 *    about pg_printf.
 */
#ifdef __GNUC__
#define vsnprintf(...)  pg_vsnprintf(__VA_ARGS__)
#define snprintf(...)   pg_snprintf(__VA_ARGS__)
#define sprintf(...)    pg_sprintf(__VA_ARGS__)
#define vfprintf(...)   pg_vfprintf(__VA_ARGS__)
#define fprintf(...)    pg_fprintf(__VA_ARGS__)
#define printf(...)     pg_printf(__VA_ARGS__)
#else
#define vsnprintf       pg_vsnprintf
#define snprintf        pg_snprintf
#define sprintf         pg_sprintf
#define vfprintf        pg_vfprintf
#define fprintf         pg_fprintf
#define printf          pg_printf
#endif

In the first place, it seems like now that we're requiring C99 (and hence
__VA_ARGS__), we could drop the conditional.  However, there's more here
than meets the eye.  That comment doesn't explain what it's worried about
very well, but it's this: if we have defined PG_PRINTF_ATTRIBUTE as just
"printf" then writing "#define printf pg_printf" would break later uses of
pg_attribute_printf, since "pg_printf" is certainly not a known printf
archetype.

(BTW, if you're wondering whether this can still happen at all, yes it
can: the "gnu_printf" archetype seems to be a pretty recent invention,
because there are lots of gcc-using buildfarm animals that still pick
"printf" as the archetype.  Moreover, there are lots of machines with
system headers containing format(printf) attributes.  So we can't just
dismiss this hazard as not being a hazard anymore.)

AFAICS, though, that problem concerns only printf itself and not any
of its siblings.

Moreover, there's a reason *not* to use the (__VA_ARGS__) construction
except where we have to.  Precisely because the macros with that usage
don't expand for references that lack parens, we'd fail to replace
attempts to take a pointer to printf or one of its siblings.  So if
you did something like

    my_printf_hook = vsnprintf;

you would silently get a pointer to the system's vsnprintf not ours.
Right now this is a hazard only on Windows builds using GCC (which is bad
enough), but it'd become a hazard everywhere with my proposal to start
using our snprintf.c everywhere.

Fortunately, it doesn't seem very likely that people would want such
pointers to printf per se.  fprintf, or vsnprintf, say, seem like
much more likely candidates to be targets of function pointers.

So I propose that what we should do here is replace the above-quoted
fragment with

/*
 * We use __VA_ARGS__ for printf to prevent replacing references to
 * the "printf" format archetype in format() attribute declarations.
 * That unfortunately means that taking a function pointer to printf
 * will not do what we'd wish.  (If you need to do that, you must name
 * pg_printf explicitly.)  For printf's sibling functions, use
 * parameterless macros so that function pointers will work unsurprisingly.
 */
#define vsnprintf       pg_vsnprintf
#define snprintf        pg_snprintf
#define sprintf         pg_sprintf
#define vfprintf        pg_vfprintf
#define fprintf         pg_fprintf
#define printf(...)     pg_printf(__VA_ARGS__)

and make attendant simplifications in the plperl and plpython headers
that concern themselves with these macros.

I've not tested this, but does anyone see a flaw in the reasoning?

            regards, tom lane


pgsql-hackers by date:

Previous
From: Pavan Deolasee
Date:
Subject: Accidental removal of a file causing various problems
Next
From: Tom Lane
Date:
Subject: Re: Windows vs C99 (was Re: C99 compliance for src/port/snprintf.c)