1

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?

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Fraggle
  • 55
  • 4
  • 1
    **C18 - 5.1.2.1 Freestanding environment** Any library facilities available to a freestanding program, other than the minimal set required by Clause 4, are implementation-defined. – David C. Rankin Apr 02 '22 at 22:05

2 Answers2

2

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.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • I don't have a broad enough view to evaluate your expectation that compilers for freestanding environments will provide `inttypes.h` despite not being required to do so, but I do note that the standard explicitly describes it as an extending `stdint.h` with features for hosted environments. – John Bollinger Apr 02 '22 at 20:00
  • "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." I'm unclear on whether you mean the caller or the implementer of printf should be doing this. If the caller, isn't that basically palming the exact same task onto them with the exact same problems? If the implementer, don't you still need to manually inspect stdint.h (or the compiler docs) to do that, making the implementation non-portable? – Fraggle Apr 03 '22 at 14:52
  • 1
    I meant `int32_t var; printf("%ld", (long)var);` – KamilCuk Apr 03 '22 at 14:53
  • Ok, I think I understand. A bit icky, but I think the thrust of your answer is use the compiler provided inttypes.h anyway, even for freestanding because otherwise you're out of luck. – Fraggle Apr 03 '22 at 15:03
  • `8_t` to `char` – if *me* chooses `8_t` as data type I'm usually interested in having a small numeric data type, so I'd be interested in numeric output as well, thus I'd cast to `int` instead... When interested in characters (text) I'd use plain `char` right from the start (luckily a different type than signed and unsigned char...). – Aconcagua Jun 01 '23 at 16:19
  • `int` – or `unsigned` – met a system once with `sizeof(int) == sizeof(char)`, signed `int` for `unsigned char` would have badly failed there for large values... – Aconcagua Jun 01 '23 at 16:25
0

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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • "except inasmuch as you might find it convenient to write definitions of those macros in conjunction with implementing printf." Working on the assumption that callers are expecting a standards-compliant function (and assuming that fixed-width types are supported), I think it's necessary for a provided printf to also provide the relevant macros. Otherwise, you are just handing the problem over to the caller, no? – Fraggle Apr 03 '22 at 14:59
  • @Fraggle, "necessary" is far too strong a characterization, because callers indeed do have viable workarounds that (at least on the `printf` side) are not very painful. But let's indeed do talk about expectations for a moment: anyone writing for a freestanding implementation and expecting a standards-compliant `printf` has an unreasonable expectation in the first place. In the same way, but altogether separately, anyone writing for a freestanding implementation and expecting to have the macros available also has an unreasonable expectation. – John Bollinger Apr 04 '22 at 12:13
  • I can see how you assumed I was stating that callers are expect a standard printf from pure freestanding, but that was not my hypothetical. My question is about how the callee, starting from a freestanding implementation, can *offer* a standards-compliant printf with PRI macros to the caller. As far as I can make out the answer is "Standard C provides no mechanism to facilitate this specific use case". Fair enough. – Fraggle Apr 09 '22 at 13:30
  • @Fraggle, I was responding to "Working on the assumption that callers are expecting a standards-compliant function". The point is that it is not reasonable for callers to have such an expectation, so the assumption is a bit far-fetched. But if you are determined to provide a `printf` implementation and format macros that conform (but for location of declarations) with the standard library's specs, yes, I already answered that there is no standard mechanism that would provide for defining the macros generically. Conditional compilation or an external code generator are the best choices. – John Bollinger Apr 09 '22 at 17:59