home blog reviews contact

2020-02-20

Why iostream is Awful

Pretty much every piece of iostream is damaged beyond repair. I honestly can't believe people still recommend it for IO in C++. (At the very least, it's what's taught in intro C++ courses.) I'm going to just assume anyone reading is familiar with iostream and just dive right in.

Global State

The first obvious problem is the global state. Whose brain-dead idea was this? Consider the following code:

cout << hex << 0x10 << endl;
f();
cout << dec;

Seems straightforward, but what if f throws an exception? Then we're fucked. Everything is in hex because that last line never runs. Or, more realistically, what if we simply forget that last line?

Compare this with printf:

printf("%x\n", 0x10);
f();

Nice and simple. No possible way to forget to reset to decimal, because there's no global state we have to worry about.

Performance

Look up some benchmarks of iostream vs. printf. I'll wait. One reason for that? Every << is a separate virtual function call, compared with the single function call for printf. And each of those involves syscalls.

Syncing iostream with stdio by default obviously doesn't help things, but you'll see a performance difference either way.

Obviously, IO is normally a bottleneck anyways, so this point is fairly minor compared to some other ones below.

Bad Syntax

Say I've got a bitfield and I want to mask it and print the result. With printf, that looks like this: printf("%x\n", f & m);. Fairly straightforward. With iostream, we need to do this: cout << hex << f & m << endl << dec;.

Except, wait a minute. That doesn't compile. Why not? Oh, right << is a bitshift operator as well, so it has the wrong precedence compared with &. Oops. Maybe the Java people are right about operator overloading.

Thread Safety

Under POSIX, any single call to printf is an atomic operation, meaning you'll never have output from one thread intermixed with output from another. With iostream, something like cout << "A" << "B" << endl; is 3 function calls, and you could get arbitrary output from another thread printed in between any of them. Perfect.

"But printf isn't Type-safe"

It's $current_year. I've never used a compiler that will let me (a) compile printf with anything besides a string literal and (b) compile any type-mismatches in printf. (Yes, this is with -Wall -Werror, but if you're not compiling with those already you have bigger problems.) I've literally never had a type error from using printf. This is, by far, the weakest excuse for not using printf I've ever heard.

Extensibility

This is the only advantage of iostream as far as I'm concerned. Just overload << for whatever type you need, boom, custom printing for your type. Quite elegant. Except for then you need to use iostream. Dammit.

In practice, I've rarely ever actually overloaded << for custom printing, but that's besides the point.

"So You Want Me to Use printf?"

Not necessarily. While I would prefer using printf to using iostream, these aren't the only options. Lots of libraries support a type-safe and extensible version of printf. C++20 is bringing us some really nice formatting options.

Conclusion

All I'll say here is that it's pretty telling that most languages have something close to printf, but I can't think of a language that copied iostream.