dimka said:
Here is my situation: I have a char buf[1024]; that is used as a buffer
for a log output.
You should immediately cringe whenever you find yourself in situations
where you are declaring large arrays like that, basically in hopes that
1024 is close enough to infinity.
[...] Normally, I do:
rc = snprintf( buf, sizeof(buf), some_format, some_args );
This makes the formatting my output and then I flush the buffer.
In 90% of cases this works well, exept the cases when the format or the
arguments require more then 1024 bytes of formatted input.
There is no easy way to re-run snprintf() form 'last' position.
Right, in fact you can't. The arguments are always considered formed
once at compile time. You cannot even process the same va_list more
than once (the standards committee added va_copy to C99, but C99 has
not been widely adopted).
Suppose that using of asprintf(), another buffer and other kinds of
malloc() is not an option. Suppose the buffer must be preprocessed
before it hits the actual io.
Is there any open/closed libraries that solve the problem? Are there
any discussions held on the subject? Please provide the references.
The regulars in this group *cannot* help you. They will cite the
standard, and cannot read well enough to see that you say specifically
that you cannot call malloc() or asprintf().
Ok, the only straight forward "in standard" solution that is possible
for you is to use vfprintf to write the output to a file, then read
back that file in chunks that you can then send to your real output.
Of course this involves the file system and is very likely to be highly
distasteful (what are you supposed to do when the disk is full?).
Another approach, of course, is to go find some open source snprintf's
out there and hack on them to make them do what you want. There are
numerous such snprintf packages out there. However, its also possible
to write your own. Either way, what you're going to want to do is to
change interface to something like:
long vpprintf (long (*output) (const char * fragment, int sz,
void * ctx),
void * ctx, const char * fmt, va_list arg);
The idea is that you would incrementally generate the formatted output
into fragments that you would then emit to the output() function
pointer in pieces (with the length passed in; this is required so that
you can output %c, with a value of '\0'). As a bonus, you would make
it so that if output() returned a negative number vpprintf would stop,
and re-return this value (to do some sort of error propogation.)
Otherwise you would return the total positive number of characters
"written" to the output callback function. Such a solution would
always use a finite amount of memory but be able to process an
arbitrarily long amount of formatted output.
Keep in mind that its not impossible to write this code from scratch
yourself. Primarily you have to break down the formatting and
arguments (via va_arg) yourself, but for the actual conversion you can
generally fall back on just plain old sprintf(). Obviously, you would
detect formatting widths beyond a certain size, and perform some
specially slicing and dicing and iterate it through a single reasonably
sized auto buffer. So there is not need to solve the difficult task of
converting floating point a to string or similar kinds of things.
If you write this code, you will basically be able to solve any problem
you ever have with C-style formatted output just using this interface.
What this says about the C standard committee (i.e., the fact that this
function or equivalent (i.e., allow for FILE *'s to wrap function
callbacks) doesn't already exist) I will leave for you to decide.