The snprintf solution is a good compromise, but does require figuring out how much buffer or stack space is enough. If your buffer ends uo smaller than needed at times with snprintf, your output just truncates (I suppose you could keep track of max needed and adjust the buffer size if on the stack). A simple example (one of many possible variations)-
https://godbolt.org/z/5doPMra3PLike most anything else, creating a function is the way to go when using snprintf for this purpose (create once, then easy to use anywhere). One problem with newlib's stdio is it is hard to understand what is going on as the source code is filled with defines/aliases/ifdef's/etc. Using snprintf, at least you do not need much understanding to make it work. It will still use a FILE struct in the process (on the stack), which is quite large (~100 bytes for newlib), so the stack dips down some more in addition to a buffer you may have put on the stack (if that matters).
Compile size is usually not a big deal unless your mcu is on the low end. The above simple example (no uart setup, so does not function) compiles to about 3.1k on an stm32. The same example below, actually functioning using a uart buffer w/isr, using my own c++ cout style equivalent code in a c++ header (~300 lines), compiles to about 2.7k. The compiled sizes will be similar enough, even when 'printing' extensively, so compiled size is not necessarily a reason to favor one or the other.
#include "MyStm32.hpp" //uart inline instance created in there
int main(){
u32 v = 0;
while(1){
uart << FG ORANGE "Hello World " << FG CYAN << Hex0xpad(8) << v++ << endl; //Hello World 0xNNNNNNNN
//print( uart, FG ORANGE "Hello World ", FG CYAN, Hex0xpad(8), v++, endl ); //alternate syntax if tired of looking at chicken footprints
}
}
The advantage to doing your own is you have complete control and can see/understand/modify the source code. I tried to figure out newlib stdio at one time, but got tired of going in circles reading the source, plus it seems to me the FILE struct is unreasonably large for the mcu's I use. I started to create a printf replacement, but switched to a c++ cout style instead as it seemed easier to write/read/modify. The advantage is I now have something I can use for any mcu with a c++ compiler, and I still have something that matches a (c++) standard way of formatting.
From what I gather searching online (although what I found looks like older code), TI has their own printf (UARTprintf) and is not using newlib to do any of it. Which is why I stated previously if you want putc/puts and are already using UARTprintf, then you just need find what the TI equivalent is for those. Here is an example of TI code (someones clone of TI code, first result from a search)-
https://github.com/ussserrr/maglev-ti-rtos/blob/master/uartstdio.c