Adding Print class printf() format string vs arguments checking

Status
Not open for further replies.

bperrybap

Well-known member
It is possible to get gcc to sanity check that the arguments handed to the Print class printf() functions match the format string.
All you have to do is add a format attribute to their declarations in Print.h
I created a github issue for this:
https://github.com/PaulStoffregen/cores/issues/485

It is nice to get the warnings for mismatched format strings and arguments.

--- bill
 
It is nice to get the warnings for mismatched format strings and arguments.

Let's give this a try in 1.54-beta1 and see if the general consensus is "nice" or "extraneous".

The main issue is int and long are both 32 bit on Teensy, but this format warning is pretty pedantic about wanting %d for int versus %ld for long. Lots of uint32_t are used, which are technically defined as unsigned long. My guess is a lot of needless warnings are going to appear.
 
I've started using %ld ... painfully ... there was a sketch once on something that failed until I did that ...
 
bummer.
I just did some testing on linux and it has become a bit difficult to create portable printf format strings.
We now have good portable types in <stdint.h> and now there are some portable printf format helper types/macros in <inttypes.h> but they are clunky.

Then there are somewhat hidden types that are inconsistent across implementations
Example, size_t and sizeof()
There is no way to know what size these are. (sizeof() now returns a size_t instead of an int)
Combine that with int and long int can be the same size and it starts to become difficult to write truly portable code.
IMO, the printf() format checking is a bit broken as it is being too strict since it reports an issue for int vs long int when they are the same size.
IMO, if they are the same size and the same signedness, then they are functionally the same so there should be no warning since they would be both be interpreted the same, and formatted the same.
i.e. there is no stack frame issue since the two types are functionally the same.
Maybe this is actually an issue that could/should be solved with <stdint.h> ?
since if int and long int are the same then uint32_t could have been defined as unsigned int instead of a long unsigned int.

There is no simple/easy answer since implementations can define things differently and behave differently.
The only way to handle it is to use the format helper macros in <inttype.h> whenever types from <stdint.h> are used.
For example:
Code:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
uint32_t u32x = 1;
uint64_t u64x = 1;

int main(int n, char *argv[])
{
        printf("uint32_t var : %"PRIu32"\n", u32x);
        printf("uint64_t var: %"PRIu64"\n", u64x);
}

but it is clunky.
 
> printf("uint32_t var : %"PRIu32"\n", u32x);

Nice, thanks. But I tried it on a teensy and memory usage went way up.
 
> printf("uint32_t var : %"PRIu32"\n", u32x);

Nice, thanks. But I tried it on a teensy and memory usage went way up.

Tried what? Compared to what?

using PRIu32 vs using the a hard coded format type changing memory usage doesn't make sense to me.
What exactly were you comparing.
Can you show the two sketches?

--- bill
 
> Tried what? Compared to what?

Agreed, wasn't clear.

Serial.printf("%"PRIu32"\n", u32x);
vs
Serial.println(u32x);

But the memory use is a printf() issue, not the use of "PRIu32".

> it is clunky.

Agreed - having to go find the type of a variable just to print it is very inconvenient.
 
> Tried what? Compared to what?

Agreed, wasn't clear.

Serial.printf("%"PRIu32"\n", u32x);
vs
Serial.println(u32x);

But the memory use is a printf() issue, not the use of "PRIu32".
Obviously it will be larger since xxprintf() has lots of formatting capabilities whereas the Arduino Print class has very limited formatting capabilities.

But back to the issue that Paul brought up, it is highly annoying that the printf() format checking routines are not smart enough
to treat functionally equivalent types as equivalent.
i.e. if unsigned int and unsigned long are the same then it seems a bit pedantic for printf() consider it an issue if not using the exact formatting code since in that case they both will behave identically.


> it is clunky.

Agreed - having to go find the type of a variable just to print it is very inconvenient.

True, But at least if using types from <stdint.h> you will know the corresponding format macro from <inttypes.h>

i.e. a uint32_t uses a format type of PRIu32
vs trying to figure out whether to use the integer format or the long integer format which can change depending on implementation.
 
But back to the issue that Paul brought up, it is highly annoying that the printf() format checking routines are not smart enough
to treat functionally equivalent types as equivalent.
i.e. if unsigned int and unsigned long are the same then it seems a bit pedantic for printf() consider it an issue if not using the exact formatting code since in that case they both will behave identically.

Yes, "annoying" and "pedantic" are exactly the 2 words I would use.

My current thinking is this feature does more harm than good and we should probably not use it (for a non-beta release) until a newer toolchain offers a better implementation.

But I'm willing to listen to opinions on this. Now's the time to seriously discuss whether this belongs in the upcoming 1.54 release.
 
As I see it, it encourages people to write portable code and helps find errors in poor code from elsewhere.

If someone doesn't like the "PRIu32" style, then there is:

printf(" %u\n", (unsigned) uint32_variable);

This makes it clear that one is willing to accept truncation (on some systems).
 
Last edited:
Status
Not open for further replies.
Back
Top