3 ms·
Why is it every framework handling run loops or communication makes its own implementation of printf? While printf is ubiquitous, I'd hardly call its semantics
by codehero 13y ago
Why is it every framework handling run loops or communication makes its own implementation of printf? While printf is ubiquitous, I'd hardly call its semantics or syntax perfect. Why does everyone have to copy the same mistakes over and over again?
- zzzcpan 13y agoTo make it more portable and consistent, something you can rely on. To avoid issues with locales (LC_*) and save some CPU in the meantime. To gain control. It would be wrong not to do that for such tiny amount of code.
- to3m 13y agoTo back this up, some specific printf issues I've seen in the past: - printf calls malloc - printf calls FPU emulator - platforms differ over whether %p's output includes a leading 0x - platforms differ over how you print int64_t/uint64_t - platforms differ over how you print size_t - some platforms have almost-ISO-but-not-quite semantics Additionally, on top of the difficulty of adding extra types in a cross-platform fashion, the printf system is tied to the FILE . FILE s are usually not extensible, and on Windows don't work with sockets. This is daft, and surprisingly shortsighted (perhaps it's the word "FILE" that causes people to come over all unimaginative?), because you could provide some system like Mac OS X's funopen, and then use fprintf for everything - maybe even replacing snprintf with it! - but functionality like this isn't as widely available as it should be. Anyway, if you write your own printf, you can fix all of this.
- wezfurlong 13y agoAll of these are factors in our choice for printf here. Another fun one: FILE on Solaris can only be used with file descriptors whose value fits in 8 bits due to an astonishing degree of backwards ABI compatibility. Also on Solaris, printf("%s", NULL) -> crash but on other systems will print "(null)". In our implementation we couldn't solve the frustrating size_t uint64_t stuff without disabling the compile time parameter checking that gcc provides; I value that more than the slight annoyance of PRIu64.
- codehero 13y agoMost of these are all just implementation or platform divergence issues, though what was the FPU emulator issue? That actually seems fundamental to formatted output strategies.
- to3m 13y agoWell, regarding the emulation issue, you've got me there, slightly, because that item was going by what I remember of what my teammate told me in the pub about 12 years ago :) The platform was the Playstation2 and the issue was (as I recall) that the system's FPU supported floats but not doubles, and the supplied libc wasn't fully compatible with the compiler flag that effectively did a typedef float double. (I assume printf was affected because of the traditional varargs promotion rules.) I suspect it was easier to write a new printf than figure out how to rebuild libc, assuming you were even allowed to link the final game to your own libc in the first place... (As for the rest being simply differences between one platform and the next, that's quite true. (And you can usually work around to one degree or another - believe it or not I've worked on a number of multi-platform projects that didn't rewrite printf, though funnily enough every single one had to wrap it.) But then, for what reason does one do this sort of thing, except to remove these differences? You might as well rail against #define stricmp strcasecmp and the like - writing your own printf is just a difference of degree.)
- codehero 13y agoThanks for the response. Hearing a real world example of a type promotion trap was illuminating. I pick on printf in particular because most seasoned programmers consider copying and pasting code a smell, but is accepted for printf and friends.
- wezfurlong 13y agoI'm not hot for reimplementing printf, but I did need an interface that made it easy to print diagnostics for various objects; rendering them to the stack and then passing them to the underlying printf implementation makes for a lot of boilerplate code. In addition to reducing boilerplate and aiding portability, having our own printf implementation aids in consistent behavior across platforms, and allows for a deeper integration with our streams and buffers so that we don't need to make a series of clunky calls to measure how much storage is needed before passing the formatted data into the lower layers.
- codehero 13y agoI know the scope of your project isn't to break ground on formatted output. I've seen the same thing you've done in fastcgi. and in nginx. A pattern that recurs because printf is a clunky interface. And of course each copy has its own little syntax variation. But people accept the printf approach because they were indoctrinated, starting from "Hello World". I know there's something better out there.
- TheZenPsycho 13y agoYou know there's something better? What is it?
- escaped_hn 13y agomaybe because printf is blocking and will block the event loop.
- codehero 13y agoI don't know if that's the reason, but there's no reason formatted output HAS to: 1) block at the caller, ever 2) have its data parameters pushed onto the stack 3) print all or nothing to a single buffer 4) identify the data type or modifier by the first letter of its English name (int, long, etc) 5) have its core modified to add functionality
- otterley 13y agoUnlikely, unless the output stream's buffer is full, in which case you pretty much have no choice but to block. stdout/stderr are two channels one generally should not write to in a nonblocking fashion.
- est 13y agoPython also had this pprint.pprint() and pprint.pformat()