This is the mail archive of the newlib@sourceware.org mailing list for the newlib project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Regression with printf 8-bit format macros


On 01/04/2016 11:05 AM, Ambroz Bizjak wrote:
Hi Andre,

I understand that defining PRI*8 to "*" rather than "hh*" is not
elegant, but it is correct.

This is the relevant text from the C99 draft (7.8.1):

Each of the following object-like macros expands to a character string
literal containing a conversion specifier, possibly modified by a
length modifier, suitable for use within the format argument of a
formatted input/output function when converting the corresponding
integer type. These macro names have the general form of PRI
(character string literals for the fprintf and fwprintf family) or SCN
(character string literals for the fscanf and fwscanf family),
followed by the conversion specifier, followed by a name corresponding
to a similar type name in 7.18.1. In these names, N represents the
width of the type as described in 7.18.1. For example, PRIdFAST32 can
be used in a format string to print the value of an integer of type
int_fast32_t.

So all that is required is that the definition is suitable for use in
combination with that type. There is nothing that specifically implies
that PRI*8 needs to be "hh*".
Well, actually there is, but it is "hidden" in the fprintf hh definition:
"hh Specifies that a following d, i, o, u, x, or X conversion specifier applies to a
signed char or unsigned char argument (the argument will have
been promoted according to the integer promotions, but its value shall be
converted to signed char or unsigned char before printing); or that
a following n conversion specifier applies to a pointer to a signed char
argument."
There are actually 2 reasons why it must contain "hh".
Firstly then, according to the standard (the part in parentheses), printf("%"PRId8, 1000) really should print 232--1000 is not OK (as suggested in the first email in this chain) because the passed value gets converted before printing. Secondly, it also makes a difference for %n--it is critical that the pointer is to char and not int. (While you might think that %n is really a scan--rather than a print--specification, the standard is explicit (as quoted above by Ambroz) that the PRI* macros go with the fprintf family and SCN* with the scanf family. While this is probably an oversight/mistake in the standard (i.e. SCN* really should go with printf's %n), that is what it does say. I also checked C11, and they did not change the language concerning this.).

For example, if PRId8 is defined to "d", the following code will
behave as expected. This is because x is implicitly promoted to int,
and int is the valid type to use with %d.
uint8_t x = 255; printf("%"PRId8, x);

I think that one of these three things should be done:
- Revert the changes so that PRI*8 is again defined to "*".
- Provide the new definitions ("hh*") when C99 printf is supported,
but the old ones ("*") if it's not.
- Do not define the PRI*8 macros when C99 printf is not supported.
The third option is the correct approach according to the standard. From 7.8.1: "For each type that the implementation provides in <stdint.h>, the corresponding fprintf macros shall be defined and the corresponding fscanf macros shall be defined unless the implementation does not have a suitable fscanf length modifier for the type." If we're saying that we don't have a suitable modifier (i.e. we're choosing to not support hh when _WANT_IO_C99_FORMATS is not defined), then PRI*8 should be left undefined.

I do agree that it is weird that for the SCN*8 macros to be guarded with _WANT_IO_C99_FORMATS, whilst the PRI*8 are not.
This is likely because the old PRI*8 definitions were known to be
correct regardless of C99 printf support. And the reason that the
SCN*8 macros must be guarded is because the scanf implementation needs
to specifically aware of the exact type that the passed pointers point
to, so that the outputs can be correctly stored. There is no implicit
promotion to "save us" in the case of scanf.

You might be surprised how Linux defines these macros - at least on
the machine I tested on, PRI*8 was "*".
(This is bad for the reasons noted above.)
Craig


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]