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;