Remix.run Logo
rwmj 3 days ago

And combine it with __attribute__((cleanup)) to get the string automatically freed at the end of your function (if that's the right thing to do). Looks like cleanup with be standardized finally in the next C2x.

wahern 2 days ago | parent | next [-]

> And combine it with __attribute__((cleanup)) to get the string automatically freed at the end of your function (if that's the right thing to do). Looks like cleanup with be standardized finally in the next C2x.

The problem is that on error the buffer pointer value is undefined, so you can't just unconditionally call free on the pointer. There's at least one proposal for C2x that avoids adopting asprintf for this reason despite it already being added to POSIX.

This undefined'ness is a vestige of the original glibc implementation. The proper solution is to either require that the pointer value be preserved on error (thus preserving NULL if the caller initialized it) or require the implementation set it to NULL. IIRC, when added by the BSDs (1990s) and later Solaris they explicitly documented it to set the pointer to NULL. And it seems that late last year glibc finally adopted this behavior as well.[1]

[1] https://sourceware.org/git/?p=glibc.git;a=commit;h=cb4692ce1...

tom_ 3 days ago | parent | prev [-]

Another tip: don't use normal asprintf as-is, but write your own very similar helper!

1. have it free the passed-in buffer, so that you can reuse the same pointer

2. have it do step 1 after the formatting, so the old value can be a format argument

3. when getting the size of the full expansion, don't format to NULL, but do it to a temp buffer (a few KB in size) - then if the expansion is small enough, you can skip the second format into the actual buffer. Just malloc and memcpy. You know how many chars to memcpy, because that's the return value from snprintf

(Don't forget to check for errors and all that.)