| ▲ | st_goliath 3 days ago |
| There are `asprintf` and `vasprintf` (takes a va_list argument). Those allocate a sufficiently sized buffer that can be released with `free`. Yes, it's a GNU extension, but it's also supported by various BSDs [1][2][3], and yes, Musl has it too. It's present in pretty much any sane C library. [1] https://man.openbsd.org/man3/printf.3 [2] https://man.netbsd.org/vasprintf.3 [3] https://man.freebsd.org/cgi/man.cgi?query=vasprintf&sektion=... |
|
| ▲ | rwmj 3 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. |
| |
| ▲ | 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.) |
|
|
| ▲ | wahern 3 days ago | parent | prev | next [-] |
| asprintf and vasprintf are part of POSIX, now. |
|
| ▲ | bluGill 2 days ago | parent | prev | next [-] |
| Thanks, first I've heard of them and they happen to solve a real problem I'm working on today. Always nice when you can learn something new... |
| |
| ▲ | lelanthran 2 days ago | parent [-] | | > Thanks, first I've heard of them and they happen to solve a real problem I'm working on today. Always nice when you can learn something new... You don't really need to, TBH. I pretty much always wrote a malloc-memory `sprintf` alternative if the system didn't have one. it's only a few lines of code, that'll take maybe 10m of your day the first time you realise `sprintf` on the platform doesn't exist. Here is a sample from more recently: https://github.com/lelanthran/libds/blob/b5289f6437b30139d42... | | |
| ▲ | bluGill 2 days ago | parent [-] | | I know, that is what I was planning on doing. (and might be what I end up doing anyway since I need to truncate the utf-8 string if it is > 1024 bytes...) Still it is nice to have other options - this code is run in some tight loops so I will be profiling all the options. |
|
|
|
| ▲ | ygritte 3 days ago | parent | prev | next [-] |
| Came here to say exactly this. The lost art of RTFM. |
| |
| ▲ | panbd 2 days ago | parent [-] | | I've actually learned a few little tricks reading the fucking gcc manual. If you're coding C (or C++) regularly, the manual is a good learning source and is well-written. |
|
|
| ▲ | showdeaduser 2 days ago | parent | prev [-] |
| [dead] |