I'd like to file a complaint /s

shawn

Well-known member
(For those that haven't seen the symbol before, "/s" means "sarcasm".)

To whom it may concern:

I'd like to file a complaint against my code. I know something's amiss because
Code:
uint16_t u = 16;
printf("%" PRIu16 " (%04" PRIx16 "h)\r\n", u, u);
prints
Code:
257 (0010h)

Alignment issues? Buffer problems? This is my WTF of the day.

(Obviously this isn't the whole code, and I'm not asking for assistance, but damn this is an annoying one because it's messing up my logging, and I had nowhere else to complain.)
 
Last edited:
You may have guessed it, but this:
Code:
uint16_t u = 16;
printf("%04" PRIx16 "h (%" PRIu16 ")\r\n", u, u);
prints this:
Code:
0101h (16)

Ever have one of those impossible-to-debug days?

Feel free to add your own complaints here.
 
Last edited:
Both printfs seem to be missing variables? (They only have the format string as a single argument)
 
It's not related, and I might make a list of newlib-nano gotchas at some point, but one thing that bit me about a week ago was assuming that newlib-nano gracefully handled printf's "%s" with a NULL string the same way that newlib does. Nope. It crashes. (Regular newlib prints "(null)".)
 
Tried it here on Teensy 4.1.

Code:
void setup() {
  while (!Serial) ;
  uint16_t u = 16;
  Serial.printf("%" PRIu16 " (%04" PRIx16 "h)", u, u);
}

void loop() {
}

I get "16 (0010h)" in the Arduino Serial Monitor.
 
It's not related, and I might make a list of newlib-nano gotchas at some point, but one thing that bit me about a week ago was assuming that newlib-nano gracefully handled printf's "%s" with a NULL string the same way that newlib does. Nope. It crashes. (Regular newlib prints "(null)".)
Was it "%s\n", or some other variation containing %s? I could understand the former causing a crash if the compiler silently changed the printf() to puts(), as it tends to do...
 
If I use just "printf" rather than "Serial.printf", I get nothing printed. Maybe that's somehow a clue?

Believe me, I’m trying to come up with a simple example that demonstrates. (Thank you for the reply.) It’s not actually this simple, but almost. So I opted to provide levity until such time as I have a useful example. :)
(I actually use templates with parameter packs for variable numbers of arguments, and then farm that out to a Print object's printf()). There's literally only one call that doesn't work, and it's weird.)
 
Last edited:
Was it "%s\n", or some other variation containing %s? I could understand the former causing a crash if the compiler silently changed the printf() to puts(), as it tends to do...

Newlib-nano just straight-up assumes the char* is valid, eg. for strlen() use and things. Well, according to the newlib-cygwin source code I found here: git://sourceware.org/git/newlib-cygwin.git

Update: I just looked at my diffs and some %s's (don't you just love an apostrophe'd plural with "%s"?) had subsequent "\r\n" and some didn't. The line that had crashed for me I think had a "\r\n" following the NULL "%s".

Update 2: And, oh! I did not know about the potential puts() substitution.
 
Last edited:
Was it "%s\n", or some other variation containing %s? I could understand the former causing a crash if the compiler silently changed the printf() to puts(), as it tends to do...

The format string had lots more than just one %s; it had some %u's and a %d and other things too.
 
5 hours later... (posting this later than that because the forum was down for a bit)

I finally found it. Always obvious in retrospect, isn't it? The clue was the argument before the argument that was messing up, and one of the details I didn't include above. I had recently changed some things to use std::optional. Guess where the one place I was using the value that I didn't call value() on? Yes, you guessed it: the argument that wasn't printing properly.

Here was the approximate original code:
Code:
uint16_t u = 16;
std::optional<size_t> index = getStuff();
// Verify index.has_value()
printf("Stuff... %u %" PRIu16 "(<-this one screws up) (%04" PRIx16 "h), index, u, u);

The corrected approximate code:
Code:
uint16_t u = 16;
std::optional<size_t> index = getStuff();
// Verify index.has_value()
printf("Stuff... %u %" PRIu16 "(<-no longer screws up) (%04" PRIx16 "h)", index.value(), u, u);

Then I started wondering: why isn't the compiler giving me an error or warning when trying to printf() an object in place of a number? In my case I want to print a size_t with %u. (Note: not %zu because I'm using newlib-nano, and the version included with Teensyduino doesn't have the C99 extensions enabled.) I surmised that it's because the std::optional<size_t> is technically static_cast-able to a size_t, so I tried some experiments with printing other object types for the %u, including IPAddress and String. Neither led to a compiler warning or error.

The next experiment I tried was building with the regular libraries (i.e. not "smallest"), but that didn't give me a warning either. So now I pose the question: How to convince the compiler to warn or error on bad printf() arguments? It does this sometimes but clearly not in this case.

And in other good news, I managed to save about 4-something-k bytes during my debugging process by converting some functions that used variadic template arguments to use the C-style va_list instead.
 
So now I pose the question: How to convince the compiler to warn or error on bad printf() arguments? It does this sometimes but clearly not in this case.
I know I've started seeing some warnings about printf arguments not matching their format specifiers (typically when a uint32_t/int32_t is passed for %u/%d without a long specifier)... is that not a thing that is turned on with the default Teensyduino build switches?
 
I know I've started seeing some warnings about printf arguments not matching their format specifiers (typically when a uint32_t/int32_t is passed for %u/%d without a long specifier)... is that not a thing that is turned on with the default Teensyduino build switches?

I’ve been seeing warnings for some type mismatches for a while. I’m curious, what do you see from the compiler when you try to pass some object as a %u?

Update: I might know what the problem is. I’ll add a reply when I’m certain.
 
I erroneously assumed the compiler checked printf arguments that are relayed to printf, and not just directly sent to printf. In retrospect, that was silly. I need a function attribute to help with this. (“format”)

The second problem was that I didn’t accurately outline the problem. I’ll take my several lessons learned and maybe write a post about it. My working title is, “How to write a bad post.” Subtitle: “Even if you’re just venting with levity.”
 
If following correctly this would give proper warnings when the provided params don't match the specifier and get munged to fit?

This bit here where code ran happily hiding needed info when %ld was not specified and debug output finally called into question when forced/expected errors failed to provide needed feedback. That was LittleFS Integrity checking. Might have also caught float 32/64 error in posted SDRAM test code?
 
If following correctly this would give proper warnings when the provided params don't match the specifier and get munged to fit?

This bit here where code ran happily hiding needed info when %ld was not specified and debug output finally called into question when forced/expected errors failed to provide needed feedback. That was LittleFS Integrity checking. Might have also caught float 32/64 error in posted SDRAM test code?

Is the word “bit” supposed to have a link?
 
We tried __attribute__ format printf years ago in beta versions. It was pulled before a release because it's far too pedantic about 32 bit integers.
The main problem is both int and long are both 32 bits. To use this on Teensy 4.x, we need a way to suppress the warnings for things like Serial.printf("%d", (long)number)) and Serial.printf("%ld", (int)number)). If these cases give warnings, I'm not going to put it into the core library headers.

Our goal here is good user experience for a huge ecosystem of Arduino code and libraries developed across a wide range of hardware. Compiler warnings are only useful to the extent they help people who have a wide range of skill levels. Different people have different philosophy about what is genuinely helpful. My opinion (for merging into the core library) is a warning that gives too many false alarms not only wastes people's time, it also erodes their attention to all the other warnings.

The pedantic problem is particularly difficult because of types like uint32_t. Some platforms define it as unsigned long, others as unsigned int. Both are 32 bits on pretty much all 32 bit microcontrollers. So a pedantic warning that essentially means "you used a 32 bit thing as some other functionally identical 32 bit thing" isn't even consistent across various 32 bit boards. To strictly conform, code would need platform-specific ifdef checks to use "%ld" on some boards and "%d" on others!

If you can find a compiler arg or other way to make the __attribute__ format printf less pedantic, specifically so "%d", "%x", "%ld", "%lx" give no pedantic warnings when used with uint32_t, int, long, then I'll put it into 1.60-beta1 and we can see how well it does. But if these cases give pedantic warnings, please accept that the answer is a hard no. Strict conformace to language specs is not the goal. Good end user experience across a large ecosystem of Arduino code & libraries is the overriding goal.
 
I hear the reasoning.

Since “Serial” seems to be a special case, see my second commit in that PR (now a draft) for one approach. However, it introduces some boilerplate and duplicate code, since printf() isn’t virtual. I’ll ponder some more if that type of change isn’t to your liking (“compiled size”-wise).
 
Is the word “bit” supposed to have a link?
No, bolded BIT is past tense of BITE ... not a binary digit :(

Indeed, noisy warnings for published library code that can't be changed is a major turn off and long term distraction.

Though a user sketch that fails without warning is a "short term" nightmare.
 
see my second commit in that PR (now a draft) for one approach

I'm still struggling to understand. Are you suggesting we should turn on this overly pedantic warning for everything inheriting from Print class, and then turn it off only for Serial?
 
Back
Top