printf() and friends… yes, really :-)
When we learn C programming for the first time, we often have the idea that “hey, I could implement that myself!” - about those library features that don’t require OS-specific voodoo (like terminal emulation and such). This is especially true case for string-related functions, like strcpy()
, or strcat()
or atoi()
and atof()
. And then there’s printf()
(and its family: sprintf()
, vsprintf()
, snprintf()
etc.). This is a bit more hefty, but we tend to think of its implementation as just some busy-work that someone has saved us the trouble of doing.
Well, that’s absolutely not the case. First, an innovative implementation of (parts of) printf()
can very well land you a well-cited paper - because efficient and optimal conversion of a base-2 floating-point value into a base-10 value is quite challenging; and even more so if you want to use the inaccurate floating-point operations different kinds of hardware provide you with.
Still, one would ask - why both at all? Surely this is a solved problem, right? Who needs your custom printf
-functions implementations, when the standard C library is available basically everywhere? Well, again, this is a misconception: Many platforms for which you may compile code do not actually have a C library for you to use. The hardware would simply not support it. Only some of it, parts of it, are usable - and are often unavailable. In these cases, a stand-alone implementation of printf()
is often called for.
A particular example is GPU programming: You can use printf()
in GPU code, but you don’t have sprintf()
available, neither in OpenCL nor CUDA; and printf()
itself is missing some of its standard-library functionality. This which explains my motivation of getting into the “printf()
business”.
A larger category of use cases is small embedded systems: microcontrollers and extremely-low-resource CPUs. For these scenarios, even if a C library could be ported - it would be unusable: Way too much code. Programs intended for such devices (often written to their firmware) are in need of extremely small implementations, possibly with only a part of the features implemented. But developers do not want to write their own printf()
, and risk introducing their own bugs; they would rather rely on a popular - if not quite standard - implementation which has had any bugs and kinks worked out.
Hence:
printf - A standalone printf/sprintf formatted printing function library for embedded systems
I was going to “rely on a popular implementation” of sprintf()
for use in my GPU code. I was first disappointed - though not surprised - that a decent one was not available which supports use in CUDA. I then turned to C-standard-libary-independent printf()
-family libraries targetting embedded devices - and chose the most popular: libprintf
, or just printf
, by Macro Paland.
To my chagrin, Mr. Paland had effectively abandoned his popular library after 5 years of work, and it had languished between 2019 and 2021, with quite a few bug reports piling up. And as with other repositories in the past, here too I saw the familiar scene: A dozen forks, each fixing its pet bug, none merged back into the original repository.
So, I created my own forked, and proceeded with merging. While doing so, and trying to streamline the code and expand the test suite, I found… well, I found dozens of additional bugs and other issues myself. And when it was apparent I was an effective and responsive maintainer, other developers started making requests of their own - for features, compatibility and configurability. Within a few months I was approaching 200 (!) different commits on a library for nothing but the “trivial” task of
printf()
-ing - after 5 years of work by other developers. I am still amazed :-)
The code is now somewhat longer, somewhat featureful, more widely compatible, compiles without warnings, much more robust, more accurate, and last but not least - significantly more readable too.