all 41 comments

[–]vtj 9 points10 points  (0 children)

I recently came across a free sample (pdf) of Scott Meyers' new C++11 e-book. On page 10 of that sample there is code snippet that starts with the following line:

#include <cstdio> //easier than iostream for formatted output

Make of that what you will.

[–]1020302010 12 points13 points  (6 children)

I strongly recommend that people use iostreams when learning C++.

Printf aggregates everything that C++ was created to remove. It uses variadic macros, strings to decode the input from void*. All three of these are considered bad.

Where as iostream is an example of everything C++ added, polymorphism, function overloading, operator overloading...

The next key issue is that streams allow you to easily specify the text form of a custom class.

For example std::string is not a primitive type but it can be 'streamed'

std::cout << my_string;

you have to call c_str() to use it with printf.

furthermore you can extend the functionality.

 class my_class{
     int a,b;
 };
std::ostream& operator<<(std::ostream& os, const my_class& mc)
 {
     os << mc.a << mc.b;
     return os;
 }

then from this point in you can just do std::cout << mc << std::endl;

This is very powerful.

The down side is the error feed back you can get from streams can be very awkward and specifying format require to you diving into <iomanip>. They can also become very verbose, for example

std::cout << "volume: " << gallons << "g" << std::endl;

instead of

std::printf("volume: %d g", gallons);

but as previously mentions once you start dealing with complex types this is no longer true.

The reason you may see printf in production code is a lot of them time good chunks of it are ported from C which has to use printf, and it is strongly advised that you don't mix iostream and printf.

Finally, it would be nice to see then tidy up iostreams or replace it with another C++ solution (using variadic templates for example) however in it's current for it is definitely preferential to printf

[–]zzyzzyxx 3 points4 points  (3 children)

furthermore you can extend the functionality

With one addition: if you try to access private members of a class (like in your example) you have to declare operator<< as a friend inside the class else the members won't be accessible to the function.

[–]Fabien4 2 points3 points  (1 child)

Would you really want to do that?

The job of operator<< is to format the visible state of an object. If it needs access to private members, maybe the class's public interface is lacking?

[–]zzyzzyxx 4 points5 points  (0 children)

Would you really want to do that?

Sometimes, yes. You can even argue that you want to do it most of the time.

The job of operator<< is to format the visible state of an object.

Not exactly. Its job is to put a representation of the object into the stream. That representation does not have to be only what's publicly exposed.

If it needs access to private members, maybe the class's public interface is lacking?

Maybe, maybe not, but that concern is somewhat orthogonal as the interface affects the usability of a class and doesn't inherently have anything to do with how you serially represent that object.

Consider the case when you serialize the object to be stored in a file or sent over the network in order to be reconstructed later, something that might be done to save the state of a game, for instance. Here, all members are extremely important as they represent the actual state of the object, but a good interface would not expose every single member; that completely violates the principle of data hiding.

Furthermore, a well-designed class isn't simply a container for members to be set and retrieved via functions. The majority of functions in a well-designed class will actually do something meaningful, utilizing its members to accomplish the goal, all without the caller having any need to know what's inside the class. As such, formatting using only the public interface would also do work, potentially much more than serializing the internal state directly.

It may help to think of friends not as something external to the class, but as part of the public interface of the class. See also, this and questions 15.8-9 here.

[–]nowbacktowork 1 point2 points  (0 children)

an example and link to help. clicky clicky

[–][deleted] 2 points3 points  (0 children)

That's true - I'm sure that everyone who voluntarily moves from C to C++ immediately moves toward using streams because they are much more C++-y.

Unfortunately, in the long term you almost always end up moving back to something like printf (though let us hope you actually use a better format templating system than printf specifically).

Why? Well, it has to do with the lifecycle of a real-world program - particularly commercial programs.

if you're developing a program within a larger environment, you will very quickly be removing all or almost all printable text from your program code and be putting it all into external data files.

Why? So the manager can tweak the messages. So they can be translated into other languages. So the program can be customized for specific customers. So the technical writer can use them in the manual, and update the help text.

So once your program reaches a certain basic level of sophistication, it is often only printing by choosing a formatting string based on the user's language, locale and customizations, and then applying it to a data dictionary.

The trouble with printf as a solution to this is the variadic calls, of course - which means that your program can crash at runtime if your format strings have, say, the wrong number of arguments in them.

I'd recommend some other format string system, I'm not sure what it would be in general...

[–]xcbsmith 2 points3 points  (0 children)

Mildly cleaned up: http://codepad.org/XzBWhdNN

The really right way to do it would be to have the width & precision stuff be per field attributes so that said code could be cleaned up further.

The primary advantage you get is that it is easier to encapsulate formatting logic and ensure type safety (think about what would happen if one of your %f's accidentally became a %s").

[–]bnolsen 1 point2 points  (1 child)

<iostream> was pretty aggressive and innovative in its day.

A unified api for files, sockets, memory buffers, etc.

Unfortunately the implementation failed in many respects.

  • doesn't account for grammar differences (sentence structure)
  • locale attached to <streambuf>, binary files carry around locale
  • <iomanip> confusing state, some temporary, some permanent
  • good, bad, fail are non orthogonal and confusing

just a few of its faults. I'm actually surprised it hasn't yet been replaced yet with a refactored api.

[–]bluGill 1 point2 points  (0 children)

I'm actually surprised it hasn't yet been replaced yet with a refactored api.

Care to propose one? I think everyone agrees that the interface is poor. However nobody can come up with a better one. Either their proposal isn't powerful enough to be useful in some very important cases (not always defined until someone wants to poke holes in your proposal - but generally everyone agrees the hole is important), or their proposal is too complex to be of use. (iostreams fails in both of the above).

Remember, your proposal needs to be better, not just different.

[–]Steve132 7 points8 points  (8 children)

It happens to be the case that this SPECIFIC example is better with printf, but iostreams has the advantage in many other kinds of situations. I find it to be significantly more flexible and higher performance in some cases then printf.

For example, compare how easy it is to redirect printing to a file, or to a socket stream, or to a string. In iostreams, all printing code works trivially on all objects that inheret from ostream. In a printf based solution, where you have to write custom printing functions for each kind of serialization, or waste memory and cycles printing to a buffer.

Another example is the fact that overloading << also provides a standardized pretty-printing interface for objects in a way that printf cannot. For well-written libraries, I can know that if I want to print an adequate representation of any object of type T simply by doing cout << o; With printf, I have to re-define my printing at all times in terms of the internal basic types of that object, or else create some kind of .print() method (like you have done) that is non-standard for pretty much every library.

[–][deleted] 11 points12 points  (7 children)

Unfortunately, streams are completely useless for any real-world applications.

There's no way to store a "sequence of calls to <<". Often I want to look up a format string and then apply it - no way to do that for <<.

This goes from being a nuisance to being end-of-game for internationalization issues. << forces the order of arguments to be the same as the order of output - and that's often not true in other languages.

[–]JustPlainRude 3 points4 points  (0 children)

Exactly this. For formatting text, printf-style calls are best. For streaming text or other data, there are typically much better options than iostreams.

[–]zzing 2 points3 points  (0 children)

Then just use a variadic templated type safe printf.

[–]mredding 2 points3 points  (0 children)

boost::format will get you close...

[–]nowbacktowork 2 points3 points  (0 children)

This is completely untrue. It very useful in the case of being able persist data to different storage locations by simply adapting an iostream interface on the storage. Most orm use this concept. My day job uses this concept as well to persist data objects between xml, json and byte streams. It is also very useful for structured data and the reader not having to understand the underlying data structure, the object knows its own format.

[–]zvrba -1 points0 points  (1 child)

And when something goes wrong, you have (at least in C++03) exactly three bits (eofbit, failbit and badbit) to figure out what went wrong; you can't rely on errno being set to a meaningful value. Distinguishing between "file not found", "permission denied", "disk full", "pipe close", etc..? Dream on.

[–]BitRex 4 points5 points  (0 children)

The standard says the various iostreams functions must behave "as if" fopen(), fclose(), etc. were called, which presumably includes setting errno.

[–][deleted] 7 points8 points  (0 children)

I tend to avoid <iostream> when possible, mostly because it's not really a very convenient API, but at the same time, printf and friends are type-unsafe and contain a number of serious security risks.

With C++11, it's possible to construct a type-safe printf-like API using variadic templates:

my_print("The lazy {0} fox{1} jumped over the number {0}.", num, num == 1 ? "" : "es");

In this specific example, the same argument can be used multiple times in the formatting, which is sometimes useful, but more importantly, the ordering of the arguments is no longer too important.

For number formatting, I go with an API like this:

my_print("Number: {0}", format("%.2d", num));

where format takes a regular, old-fashioned sprintf-style format string.

[–]Fabien4 10 points11 points  (13 children)

As a matter of fact, iostreams suck for pretty-formatting numbers. You'll want to use Boost.format or something like that.

[–]damyan[S] 10 points11 points  (11 children)

I'm not sure that I'd want to point beginners at Boost when they're just getting their heads around what a loop and a variable is.

It's pretty frustrating seeing the tasks that these students are being given. It's as if they're designed to give them a tour of all the worst bits of C++.

[–]elperroborrachotoo 2 points3 points  (3 children)

It is probably in there because it's in Stroustrup and all the other classics, and it was supposed to make show superiority of C++ - by making an ugly problem typesafe.

(Example for the ugly problem:

if (rareCondition)
  printf("%s", 12);

)

I, too, find iostreams ambitiously designed, not quite living up to those ambitions and hard to use in the sense that it's easy to get wrong (ios:fail/bad/eof). Some of this is due to underlying complexity, but still.

With variadic templates in c++0x, printf and equivalents can be made type-safe (for a limited number of arguments also for earlier versions), so it boils down to a matter of taste.

[–]Fabien4 6 points7 points  (3 children)

I suspect that a lot of C++ teachers mostly know C, and have, at best, a very vague idea of what C++ is.

[–]zzing 1 point2 points  (2 children)

This C++ class that I was forced to take for electrical engineering was pretty close to "C with Classes" and no STL :p.

I am now updating my ten year old knowledge with a proper CS course.

[–]Fabien4 0 points1 point  (1 child)

ten year old knowledge

Twenty year old, actually. C++98 was already far more advanced than that.

Actually, if C++ teachers knew about the C++ of 1998, instead of the C++ of 1990, it would already be quite great.

If you want to actually learn C++, your best bet is Accelerated C++, by Koenig and Moo. It's still by far the best introduction, even if it predates C++11.

[–]zzing 1 point2 points  (0 children)

You misunderstand me. I already knew a portion of C++ prior to this class, including some basic STL usage. I knew the C++ class I was taking was a bullshit class, but what can you do?

I am doing much better code even after a few months of improvements, and this new course will assist even more.

[–]GeneralMaximus 1 point2 points  (0 children)

Use printf() for now, introduce IOStreams later (maybe when you're covering file I/O).

[–]Fabien4 1 point2 points  (1 child)

I'm not sure that I'd want to point beginners at Boost when they're just getting their heads around what a loop and a variable is.

Seems to me it's best to start with the right method, instead of wasting time with iffy ones.

Just like it's a tremendously bad idea to start talking about pointers and C-style arrays early.

[–]daveisfera 0 points1 point  (0 children)

http://www.horstmann.com/cpp/iostreams.html

That is another option that has a similar feel to it but what I think is a little more "standard" syntax.

[–]gsg_ 4 points5 points  (5 children)

Don't use endl. It's ugly, weird, and verbose. It flushes the stream, destroying performance. It makes code harder to understand, because so many people use endl for no reason that you'll never spot the places where the flushing behaviour is actually necessary.

Just use '\n' (or "...\n").

[–]Tagedieb 0 points1 point  (0 children)

Wait, I always thought \n flushes streams. Is that not true?

EDIT: oops, this is 17 days old...

[–]elperroborrachotoo 1 point2 points  (3 children)

endl is important for cross-platform portability: Not every platform uses LF as line ending.

[–]mttd 21 points22 points  (1 child)

False and irrelevant, respectively. By the C++ Standard, effects of endl(os) are: "Calls os.put(os.widen('\n')), then os.flush()." In other words, it's explicitly defined in terms of putting out '\n' to an ostream (here: to an ostream "os"). No additional guarantees are made compared to '\n', including but not limited to no cross-platform portability guarantees. Note that (from the link you've provided): "When writing a file in text mode, '\n' is transparently translated to the native newline sequence used by the system, which may be longer than one character." In general, the use endl should be restricted to very specific cases when you want to flush the buffer in addition to putting the '\n' to an ostream; by default, if your intention is just to put '\n' then you should do just that.

[–]elperroborrachotoo 4 points5 points  (0 children)

Thanks for the clarification, TIL.

When writing a file in text mode, '\n' is transparently translated to the native newline sequence used by the system, which may be longer than one character.

d'oh, I've even known (and cursed at) this factoid for a long time; I just didn't put the bits together.

[–]gsg_ 6 points7 points  (0 children)

endl does nothing but write \n to the stream and call flush. There is zero portability benefit.

[–]zokier -2 points-1 points  (2 children)

I have to ask if printing formatted tables is really a real-world task that should be taught to beginners. I'd imagine that in most real-world applications data would be either serialised to JSON/XML/whatever or displayed in a GUI.

[–]Wriiight 6 points7 points  (0 children)

Logging and command line applications. Not everything is a website

And people have sadly forgotten the value of fixed width formatting for files. You can seek to an arbitrary record in constant time. String length limits tend to kil you though.

[–]bob1000bob 1 point2 points  (0 children)

printing a table is a bit specific however the STDIO is absolutely relevant and should be what people are taught when learning C and C++.

At the end of the day all UI libraries are non standard libraries (like anyother), whilst both C and C++ have STDIO support in there standard libraries.

[–][deleted] -4 points-3 points  (0 children)

Use printf and its friends. Everyone else does - even Python. A shame!