13

I've never been much to use C++ I/O streams and have always opted for what I know. i.e. the printf functions.

I know there are some benefits to using I/O streams, but I'm looking for some tips from the stackoverflow community to help me (or convince me) to switch. Because I still prefer printf and I think the printf style is so much easier to read and quicker to type.

I would still like to be familiar with it even if I still continue to use printf.


Edit. Interestingly, google C++ coding style forbids the use of streams except for logging. See: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml

Streams

Use streams only for logging. Definition: Streams are a replacement for printf() and scanf().

Pros: With streams, you do not need to know the type of the object you are printing. You do not have problems with format strings not matching the argument list. (Though with gcc, you do not have that problem with printf either.) Streams have automatic constructors and destructors that open and close the relevant files.

Cons: Streams make it difficult to do functionality like pread(). Some formatting (particularly the common format string idiom %.*s) is difficult if not impossible to do efficiently using streams without using printf-like hacks. Streams do not support operator reordering (the %1s directive), which is helpful for internationalization.

Decision: Do not use streams, except where required by a logging interface. Use printf-like routines instead.

There are various pros and cons to using streams, but in this case, as in many other cases, consistency trumps the debate. Do not use streams in your code.

Extended Discussion

There has been debate on this issue, so this explains the reasoning in greater depth. Recall the Only One Way guiding principle: we want to make sure that whenever we do a certain type of I/O, the code looks the same in all those places. Because of this, we do not want to allow users to decide between using streams or using printf plus Read/Write/etc. Instead, we should settle on one or the other. We made an exception for logging because it is a pretty specialized application, and for historical reasons.

Proponents of streams have argued that streams are the obvious choice of the two, but the issue is not actually so clear. For every advantage of streams they point out, there is an equivalent disadvantage. The biggest advantage is that you do not need to know the type of the object to be printing. This is a fair point. But, there is a downside: you can easily use the wrong type, and the compiler will not warn you. It is easy to make this kind of mistake without knowing when using streams.

cout << this;  // Prints the address 
cout << *this;  // Prints the contents 

The compiler does not generate an error because << has been overloaded. We discourage overloading for just this reason.

Some say printf formatting is ugly and hard to read, but streams are often no better. Consider the following two fragments, both with the same typo. Which is easier to discover?

cerr << "Error connecting to '" << foo->bar()->hostname.first
     << ":" << foo->bar()->hostname.second << ": " << strerror(errno);
fprintf(stderr, "Error connecting to '%s:%u: %s",
      foo->bar()->hostname.first, foo->bar()->hostname.second,
      strerror(errno)); 

And so on and so forth for any issue you might bring up. (You could argue, "Things would be better with the right wrappers," but if it is true for one scheme, is it not also true for the other? Also, remember the goal is to make the language smaller, not add yet more machinery that someone has to learn.)

Either path would yield different advantages and disadvantages, and there is not a clearly superior solution. The simplicity doctrine mandates we settle on one of them though, and the majority decision was on printf + read/write.

hookenz
  • 36,432
  • 45
  • 177
  • 286
  • 1
    The title says "guide to switching" but the text says "convince me to switch". Which is it? The question is kind of ambiguous. – Mark Ransom Dec 02 '10 at 20:31
  • Fair comment, I've updated my question title. – hookenz Dec 02 '10 at 20:32
  • It can be a good holywar here. Read about manipulators http://www.cplusplus.com/reference/iostream/manipulators/ My opinion is that C statements must be in C, and C++ statements must be in C++ inspite of they are beauty or awfulness. – Vladimir Lagunov Dec 02 '10 at 20:35
  • So use `printf`! And a special dedication: http://www.joelonsoftware.com/items/2009/09/23.html – ruslik Dec 02 '10 at 20:37
  • see: http://stackoverflow.com/questions/119098/which-i-o-library-do-you-use-in-your-c-code/119194#119194 – Martin York Dec 02 '10 at 21:02
  • 1
    Neither is great: printf is not typesafe (and maybe in an exploitable way), and iostreams produce unreadable code. Also the code needed for iostreams may cause problems for apps that must be translated. That's easier with format strings. –  Dec 02 '10 at 21:05

8 Answers8

8

Use boost::format. It's got the best of both worlds.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
  • 6
    Minus the performance, of course. But yeah, otherwise, it is the best of both worlds. – jalf Dec 03 '10 at 06:48
7

I'm not a big user of streams myself, so I'll only list what I think about them. This is really subjective, I'll understand if my answer is voted for deletion.

  • I like : homogeneity

I may have a enum, a class or anything else, making my user defined type printable is always done by providing the same operator<< next to my type :

std::ostream &operator<<(std::ostream &, const MyType &);

You may ask yourself if a type is printable, but never how it is printable.

  • I like : abstraction

Obviously, it is incredibly easy to provide 'streaming capacities' to a user defined type. It's also a great to be able to provide our own implementation of a stream and have it fit transparently in an existing code. Once your operator<< are appropriately defined, writing to standard output, a memory buffer or a file is trivially changeable.

  • I dislike : formatting

I've always thought iomanip to be a mess. I hate writing things such as (I'm just throwing random manipulators here) :

std::cout << std::left << std::fixed << std::setprecision(0) << f << std::endl;

I think it was much easier with printf, but Boost.Format is helpful here.

icecrime
  • 74,451
  • 13
  • 99
  • 111
  • That's exactly how I feel about ipmanip myself. It's so much code to write for something simple. Provided you pass good parameters to printf who's to say that iostreams is better? the big reason that I haven't switched. Curious about boost::format though – hookenz Dec 02 '10 at 20:58
  • 1
    @Matt H: iostream are better in that if a call to `std::cout << x;` compiles, then it'll probably work as expected, regardless of the type of `x` :) – icecrime Dec 02 '10 at 21:00
  • 1
    @icecrime That's not exactly true. Suppose you wrote `cout << ptr` and meant `cout << *ptr`. Both will compile. – Brian Vandenberg Aug 06 '15 at 17:00
6

The massive advantage that iostreams offer is safety. printf() is an inherently unsafe function. Not just that, but it's trivial to overload << for your own types, whereas it's realistically impossible to extend printf()- this has the added advantage of instantly overloading for output to files as well, and anything else connected to a stream. In combination, these make printf() unusable in high-quality code. I've also noticed absolutely no performance difference, although I see many people posting about how slow they are.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Since I respect your knowledge of C++ and know you use it extensively, and you don't see slowness in iostreams, and I do and have profiler data to prove it's the iostream library adding the overhead, let's compare notes to see why we're getting such different results. I'm thinking of Visual C++, versions 2008 and 2010. And I'm talking about buffer-intensive operations such as the numeric extraction and insertion operators or `fstream::write` of a few bytes, not bulk transfers. – Ben Voigt Dec 02 '10 at 21:12
  • It's not a secret that the C++ IO streams are slower than C's. I never measured Boost.Format's performance though. – wilhelmtell Dec 02 '10 at 21:41
  • 1
    I've created a question to discuss C++ iostream overhead and performance of particular implementations: http://stackoverflow.com/questions/4340396/ – Ben Voigt Dec 02 '10 at 21:58
  • @Ben: It's strange, when I tested this myself, std::cout was just as performant as the other solutions, but now that I've gone back to run the same benchmark, my cout performance is crippling. – Puppy Dec 03 '10 at 18:13
  • @DeadMG: I assume you're using the shell to redirect output to a file? What OS, what compiler? – Ben Voigt Dec 03 '10 at 19:56
  • @Ben: No, why would I do that? The OP's question concerns printf. That means cout, which means console output. Why would any other output be relevant? – Puppy Dec 03 '10 at 20:24
  • Because if you want to compare the speed of `cout` vs `printf`, you need to remove external bottlenecks. Also because `printf` doesn't mean console output, it means standard output, which could be console, file, or pipe. If `cout` and `printf` both output 1 million characters per second to a console, but `printf` outputs 50 million characters per second to a file while `cout` is the same 1 million characters per second, then I'd call that a significant performance difference. Lots of applications write to stdout without any expectation of a console, especially on unix-like systems. – Ben Voigt Dec 04 '10 at 00:36
  • @Ben: cout prints to stdout just as much as printf does. The overhead of outputting to a console for printf is going to be the same as that for cout. There's no reason that printf and cout would be faster or slower comparitively to each other, when outputting to a file over outputting to a console. – Puppy Dec 04 '10 at 11:39
  • @DeadMG: Do you agree that even if `printf` was one thousand times faster than `cout` (it completes its processing in 1/1000th the time), if both were flushing to a 9600 baud serial port then the observed performance would be the same, 960 bytes/second? If you impose an external bottleneck, then you are no longer able to compare the relative performance of the two methods. – Ben Voigt Dec 04 '10 at 15:27
  • @Ben: That can only work if you can demonstrate that `printf` and `cout` have the same performance, and even then, it would only hold that `cout` does not have substantially worse performance than printf, as the I/O bottleneck is far higher. If console I/O were limiting cout, then it's performance should be the same as `printf`, give or take, but it isn't. – Puppy Dec 04 '10 at 16:00
  • 1
    @DeadMG: If you're seeing `cout` slower than `printf` on console, then I agree it must actually be slower. However, I'm pretty sure that `printf` to console IS limited by the console, so in order to get a real idea of the performance ratio, you would have to redirect to something that doesn't bottleneck `printf` either. Also, I disagree with your bar for "substantially worse". Even if `cout` and `printf` are both bottlenecked by I/O, I still care whether 96% of my CPU is in use inside `cout`, or free to do other tasks and/or sleep and save battery life because I used `printf`. – Ben Voigt Dec 04 '10 at 16:24
  • @Ben: Funny. `printf`: 50ms. `WriteConsole`: 45ms. `cout`: 1100ms. – Puppy Dec 04 '10 at 18:06
  • @DeadMG: That's an even worse ratio than I'm measuring in [my question comparing `stringstream` to `sprintf`](http://stackoverflow.com/questions/4351371/c-performance-challenge-integer-to-stdstring-conversion-closed). Do you have *sync-with-stdio* and *unit-buffering* both turned off? – Ben Voigt Dec 04 '10 at 20:31
  • @Ben: Yep. No change. It's strange, because now WriteConsole is taking longer than printf. I cry inside. – Puppy Dec 04 '10 at 21:24
  • @DeadMG: I'm going to take a wild guess and say that `WriteConsole` has a higher constant and lower per-byte cost than `printf`. An even fairer test would be `WriteConsole` vs `puts`, since they have about the same capabilities. `printf` and `puts` definitely are buffered in the run-time, which means that they could actually be faster. – Ben Voigt Dec 04 '10 at 21:34
5

@Matt, I'm with you. I've always hated streams. Sure, I can use them. Make them do pretty much anything I want. But I like printf because I prefer the syntax.

I even wrote a strprintf that worked exactly the same as sprintf except returned a std::string instead of writing to a char buffer.

But gradually, begrudgingly, I have almost completely stopped using sprintf. Because, simply speaking, I write too damned many bugs and I get sick and tired of going over and over my same mistake time & time again. stringstreams type safety saves me from myself.

The bugs I'm talking about for me come in 2 forms, mainly:

  1. I picked the wrong magic number for my output buffer. Say I come up with char buf_[256] to format up a little something. Well, just like Bill Gates famously attributed comment that "256KB of memory ought to be enough for anybody," I'm wrong on the low side enough to catch my eye. On the other hand, what am I going to do? char buf_[1024*64]? Extreme, but you get the point. There's no perfect magic number. You either expose yourself to more crashes, or you waste memory.

  2. I sprintf-ed a string, but sent it a float. Do this all the time. Well, not all the time. For every 100 calls to sprintf, I probably do this once or twice. For production code, that's a lot.

With streams neither of these can ever happen. So I use streams now, and my code never crashes. Well... there, anyway.

Some will say that streams are slower than sprintf. Eh, maybe. For the sake of argument, I'll even just go along with it. Doesn't matter much though. I work on real-time stock market servers that routinely process 3 million messages per second, all day long. And I've never had a problem with the speed of streams. Maybe it's a little slower, but I've got bigger fish to fry.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
3

You cannot extend printf with new format specifiers to handle your own types.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 1
    Actually, you can (overload the name `printf` that is). What you can't do is extend `printf` with new format specifiers to handle your own types. – Ben Voigt Dec 02 '10 at 20:38
2

You get more protection against type errors with C++ I/O streams, but they are SLOW. So it mostly depends on how important performance is.

I/O streams are also polymorphic, unlike the C stdio functions, the user of a stream doesn't need to know whether it's connected to a console, file, or some other logic. But this isn't important for most applications.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 2
    @Ben: much less slow than we are led to believe (not that it matters anyway, most of the time). They are mainly slow because they sync their buffers with the C streams. You can disable that feature safely (`cout.sync_with_stdio(false)`). – Konrad Rudolph Dec 02 '10 at 20:42
  • @Konrad: I'm talking about `fstream` objects, not standard streams. On my laptop, parsing decimal numbers from a comma-separated value file is 10 times slower using iostreams. And I'm not talking micro-optimization of code that's I/O-limited anyway, I'm saying the throughput of my program got 10 times faster when I eliminated iostreams in favor of memory-mapped files and `atof`. – Ben Voigt Dec 02 '10 at 20:52
  • And of course, avoid `endl` in I/O stream if performance if your goal! – chrisaycock Dec 02 '10 at 20:53
  • @Ben: You’re comparing apples and oranges then. Of course using mmap is faster than default file streams. But you could encapsulate them in C++ stream objects as well. – Konrad Rudolph Dec 02 '10 at 20:56
  • @Konrad: Sorry, there's not really enough space in comments to really provide context. I had run a profiler on the code. The code using iostreams was CPU-bound and spending >90% of its time in the iostreams library. That tells me the problem was inefficiency in the iostreams library, not blocking vs memory-mapped I/O (inefficient I/O would be low CPU usage while waiting for the disk, high CPU usage could only have been caused by inefficient buffer management and/or other library overhead). – Ben Voigt Dec 02 '10 at 21:06
  • Also, it could be the Visual C++ (Dinkumware-provided, I think) I/O streams implementation that's so horribly slow, and not true of the API in general. But when one of the selling points of the API is supposed to be portability, that's really undercut when the performance isn't reasonably similar across platforms. – Ben Voigt Dec 02 '10 at 21:07
1

I switched to iostreams after I learned C++ 10 years ago. Several years ago I was convinced that it was a bad choice so I switched back. I did not regret the switch. For a good C++ programmer you need to know both.

Google C++ style guide prefers to use printf/sprint/snprint over iostreams. It says to use iostreams only for logging.

In terms of iostreams benefits:

  • Type safety. Compiler (gcc) can detect type errors, as well as all static analysis tools. Even there is a type error, one can spot the error easily from the print out.
  • Extensibility. Yes iostreams have overloading but all data members eventually go to POD types.
  • No buffer overrun. Use snprintf to overcome the buffer size issue.

Now come the benefits of sprintf:

  • Much better readability. "Record(%d): %s\n" is much easier to read than os << "Record(" << i << ") " << msg << endl;
  • Performance. If you are doing a lot of iostreams stuff, changing them significantly improves performance. I once worked on a library that uses stringstream to convert int/doubles to strings. I replaced with sprintf and performance has improved a lot (there are a lot of calls to the conversion routine). For the record, boost::format has even worse performance.

My conclusion is that to use iostreams sparely. I use it to read memory buffer, or write to memory buffer occasionally. For other work, I use plain C function.

Michał Wróbel
  • 684
  • 4
  • 10
cuteCAT
  • 2,251
  • 4
  • 25
  • 30
0

To follow up on Noah's answer, boost's format library allows for several different syntaxes, including printf like syntax. It works by overloading the % operator, and so at first appears a little strange. But, something I rather like, it allows you to reuse arguments. For example

cout << format("%1% %2% %1%") % 1 % 2 << endl;

will print

1 2 1

It's pretty versatile and if you can get used to the % signs everywhere, gives you the best of both worlds. As I agree, printf style is often much much easier.

http://beta.boost.org/doc/libs/1_43_0/libs/format/doc/format.html

pythonic metaphor
  • 10,296
  • 18
  • 68
  • 110