The printf family are one of the most important C functions but the lack of the portability of the format specifiers especially between Windows and Posix systems can be be very annoying. All the required workarounds and adjustments cost the programmers a lot of time.
This implementation not only behaves the same on all systems and supports the most of recent format specifiers but also ignores the locale settings to prevent any kind of random output variations that depends on environment settings.
Who wants to mess around with different prefixes or format strings on every new platform? OK - there are a plenty of other sprintf implementations already but many of them don't really care about performance and the portability or lack floating point and especially long double support.
This implementation also converts wide character (wchar_t) Unicode strings
to UTF-8 for making the printing of those a nobrainer.
It doesn't use any internal locks, unnecessary allocations and other strange things which may slow down the code where every microsecond counts. callback_printf uses the stack of the calling thread only and neither any locks nor allocations. It allows you to debug and fix problems very easily if something doesn't work as expected.
I guess that every programmer who really likes C hates the trouble with printf like functions and even more the problems and the difficulties if he want to use the argument format for some own output functions or wants own extensions of that format. If it comes to me it was always wished to get rid of all this trouble and all the portability issues that most programmers are struggling with.
And I did want to add some extra length modifiers for using arguments of type
int8_t, int16_t, int32_t and int64_t which can be found since Posix 98
in inttypes.h and also in stdint.h since the C 11 standard. In C 23 there exist
a first standard enhancement for that now but not all compilers are supporting
that already.
The supported format specifiers are b, B, d, i, o, u, x, X,
a, A, e, E, f, F, g, G, s, c, p, n, and % for a
percent character, and the Microsoft specific
specifiers C and S for wide characters.
The supported size prefixes for the integer formats b, B, d, i, o, u,
x, and X are currently hh, h, l, ll, t, z, and j according to
the C standard as well as the Microsoft specific I, I8, I16, I32, and I64.
Additionally some own prefixes l1, l2, l4 and l8 are supported which
specify the byte width of the provided integer arguments. The length modifiers
w8, w16, w32, and w64 of the new C 23 standard are supported now too.
The supported lenght modifiers for the formats s and c are l for wchar_t
arguments and l1 for 1 byte ISO Latin 8 strings, l2 for 2 byte wide Unicode
characters and l4 for 4 bytes wide unicode characters.
For the floating point formats a, A, e, E, f, F, g, and G the
prefix L for long double arguments is supported as well.
Additionally special prefixes for specifying the specific numeric base of integer
or floating point numbers is supported. It's r0 for base 10, r1 for base 16 and
r2 ... r9 for the bases 2 til 9. For other bases r* can be used. In that case
the base needs to be specified by an additional argument of type int just
before the integer or floating specifier or it's optional length specifier. The
highest supported numeric base is 36.
The floating point exponents of numeric bases higher than 14 are prefixed by a
tilde ~ istead of the letter e which is a member of the common regular digits
of those bases.
Sometimes you need to print the same complex data types quite often e.g. IPv6
addresses or times. The common way in C would be to write a function that
writes that to a character buffer and returns a pointer to that buffer but you
need an variable for a buffer wherever you need to print them. For improving
things like that callback_printf supports the options %v and %V for variable
argument types now. %v expects a function pointer and a data pointer as related
arguments where the function generates the output for the random data pointer
and calls the write callback. %V expects a single pointer to a struct that
contains the mentioned function and a pointer pointer as argument and may be a
member of other structs that need to be written in a special format. That way
it's possible to print an unlimited number of data types and data in special
formats and also mixed up in the same output if required. However that's all
still an early state an the function prototypes may still change a bit.
Another common problem is that you need to prepend or append additional
information like a date or the time or the thread ID to data within a logging
function. I did add an additional option %@ now that expects a format string
and a va_list within the arguments to be printed at the position of the %@
within the output. A minimum width can be specified for a %@ output as well.
There are some samples of that within the regression tests.
The implementation also uses the great printf parameter validation features of
the gcc which are a great thing for preventing program crashes within printf
like functions. However that may trigger those warnings in case of using any
printf format enhancements and you may need to silence those warnings by using
the unchecked function versions which names are prefixed by an underscore _.
An interesting internal feature is the fast generic mantisse and exponent calculation for the several numeric bases that enables the generic support of different numeric bases and the pretty fast floating point output.
The little benchmark vsprintf_bench.c is an easy way for checking the performance. Just execute that file in a shell of a Posix system and have a look on the outpout.
All callback_printf based wrappers are prefixed with an s for 'speed' and for 'security' because you won't share your string data with compiler libraries which can be a security issue in some special use cases where you need to prevent sniffing.
The license is kind of a mix of BSD and Apache conditions but in opposite to those it prohibits a usage for weapons and spyware and any secret monitoring of other people without their agreement or their health or life being endangered. That's fine for most software but usually not for military devices, weapons or spyware. It would be great if more projects would adapt this license as well.