[HN Gopher] Generalizing Printf in C
___________________________________________________________________
Generalizing Printf in C
Author : oliverkwebb
Score : 21 points
Date : 2025-11-23 23:54 UTC (4 days ago)
(HTM) web link (webb.is-a.dev)
(TXT) w3m dump (webb.is-a.dev)
| theamk wrote:
| On GNU systems, if you want to generalize printf, all you need is
| vfprintf - because there is:
|
| "fmemopen(3)" that creates FILE* that writes to pre-allocate
| dbuffer
|
| "open_memstream(3)" that creates FILE* that writes to auto-
| allocated buffer;
|
| and if that's not sufficient, there is "fopencookie(3)" which
| takes general callbacks and creates FILE* that redirects all
| operations to those callbacks.
|
| If that does not work for some reason, then having custom
| callback with user-passed 3 parameters is too much. Why add
| dedicated FILE* or "size" parameters which are only ever used in
| one specific case? Do a generic "void * context" argument ("int (
| _write)(char_ data, void * context) " + "void * context") and let
| user figure out how to use it.
| pizlonator wrote:
| Yeah
|
| Pretty sure a vfprintf-like function sits at the bottom of the
| printf stack in all of the libc's I've surveyed (which includes
| BSDs). And yeah, BSDs also support memstream APIs, for example
| https://man.openbsd.org/fmemopen.3
| nwellnhof wrote:
| fmemopen and open_memstream are both part of POSIX, so they're
| not restricted to GNU systems and can be used portably.
| fopencookie is a GNU extension, though.
| kazinator wrote:
| sprintf can be safely used.
|
| - For some conversions, you can establish an upper bound on how
| many characters they will produce. E.g. a positive decimal
| integer not more than 9999 does not consume more than four
| characters.
|
| - It's possible to specify truncation. e.g. "%.64s" prints at
| most 64 characters from the string argument.
|
| - There are enirely static cases that can be worked out at
| compile time, e.g. char
| big_enuf_buf[BIG_ENUF_BUF_SIZE]; sprintf(big_enuf_buf,
| "%x-%04x-%04x", MAJOR, MINOR, BUILD); // preprocessor constants
|
| Even if the buffer isn't big enough, and the behavior is formally
| undefined, it is entirely analyzable at compile time and we have
| support for that: the compiler can work out that the conversion
| needs, e.g., 13 bytes, including null termination, but the buffer
| only has 12.
|
| The reasons for analyzing to it wouldn't necessarily just be for
| diagnostics, but possibly for compiling it down to a literal:
| char big_enuf_buf[BIG_ENUF_BUF_SIZE] = "A1-0013-000A";
| kevin_thibedeau wrote:
| idx should be a size_t.
| einpoklum wrote:
| Actually, there are historical reasons why `int` may be used.
| Look at the definition of the %n format specifier - it expects
| an `int *` argument. And all of the famirly functions return
| `int`'s ... see also:
|
| https://stackoverflow.com/q/45740276/1593077
| einpoklum wrote:
| A popular standalone printf-family library in the embedded world
| is, well, printf :
|
| https://github.com/eyalroz/printf
|
| which is independent of a C standard library (it doesn't actually
| do any I/O itself). Originally by Marco Paland, now maintained,
| or 'curated' by myself (so, this is a bit of a self-plug, even
| though I can barely claim authorship). It offers this
| generalization : int fctprintf(void (*out)(char
| c, void* extra_arg), void* extra_arg, const char* format, ...);
| int vfctprintf(void (*out)(char c, void* extra_arg), void*
| extra_arg, const char* format, va_list arg);
|
| The library is not performance-oriented, but rather small-code-
| size-oriented. The family of functions therefore all have a
| single backing implementation. You might think that
| implementation must use the function generalization quoted above,
| but actually it uses a gadget with some more functionality:
| typedef struct { void (*function)(char c, void*
| extra_arg); void* extra_function_arg; char*
| buffer; printf_size_t pos; printf_size_t
| max_chars; } output_gadget_t;
| jmclnx wrote:
| And in the old days, there was disp_printf() from Zortech. That
| was a very nice printf. You supplied the row and column to allow
| printing anywhere on the terminal.
___________________________________________________________________
(page generated 2025-11-28 23:00 UTC)