Implementing PRI macros 'portably'

840 views Asked by At

The C99 specified inttypes.h header includes macros for providing format specifiers for the fixed-width integer types provided by stdint.h (beginning with PRI). While stdint.h is on the 'freestanding' list of headers and thus is always provided by the implementation, inttypes.h is not.

This means that when compiling for an unhosted environment (say bare-metal), you must provide inttypes.h yourself if you want to provide a standards-compliant printf that can handle fixed-width types.

However, I cannot figure out how to correctly determine at compile-time what each macro should be without manually inspecting the implementation's stdint.h, a task that would need to be repeated not just for each target platform, but for every supported compiler.

I tried an implementation via C11's _Generic but discovered one of the limitations of that feature.

Is there a standard way to do this?

2

There are 2 answers

6
KamilCuk On BEST ANSWER

It is not possible to do that. In essence, inttypes.h exists to give you portability, you can't "guess" it. It has to be given by the compiler. On top of that, theoretically, there just might not exist a printf format specifier to print the types from stdint.

a standard way to do this?

Yes - contact your compiler provider and make a feature request. While waiting for it, read your compiler documentation and/or source or contact upstream for more information and create your own myinttypes.h with the information that you need.

standards-compliant printf that can handle fixed-width types.

That's simple - cast the types to standard types before printing. 64_t to long long, 32_t to long, 16_t to an int and 8_t to char and print them using standard %lld...%hhd format specifiers.

you must provide inttypes.h

That there is no guarantee that inttypes.h is provided, that is not a guarantee that it is not provided - it might be. The question sounds more like a theoretical question - I do not think any common compiler in the wild comes without inttypes.h.

4
John Bollinger On

This means that when compiling for an unhosted environment (say bare-metal), you must provide inttypes.h yourself if you want to provide a standards-compliant printf that can handle fixed-width types.

No, it does not mean that at all.

Perhaps you have the wrong idea about the fixed-width types. Those that are provided by a given implementation (which need not be any of them at all) are usually standard integer types of that implementation. The type names declared in stdint.h are then aliases for appropriate standard types, not designations of extended integer types.

Thus, the macros in question define string literals that ordinarily contain a standard conversion specifier, possibly with a length modifier, such as "d", "hu", or "lx". If you want to provide a printf compatible with the one a hosted environment would provide, then you need to support all the standard conversion specifiers and all the length modifiers relevant to each one, entirely without regard whether those macros are defined or what their replacement text may be.

I guess it's possible that you would run into an implementation (even a hosted one) that provides fixed-width types that it in fact does not use among its standard integer types. To support such types with your printf, you would probably need to extend it to provide new conversion specifiers, but in that case, that's part of your printf implementation, so you know how to define the corresponding macros.

Any way around, the macros are relevant only to printf callers, not implementers, except inasmuch as you might find it convenient to write definitions of those macros in conjunction with implementing printf.

I cannot figure out how to correctly determine at compile-time what each macro should be without manually inspecting the implementation's stdint.h [...] Is there a standard way to do this?

No. That's why the macros are provided by the implementation in the first place. However, callers don't necessarily need to know. They can instead convert their fixed-width types to guaranteed-wide-enough standard types of their implementation, and use the correct standard conversion specifier for that type. That's cheap and pretty easy with printf. If you also mean to provide a scanf, however, then the corresponding adapter code is a little less convenient.