I'm using a lot of printf for tracing/logging purposes in my code, I've found that it's a source of programming error. I always found the insertion operator (<<) to be somewhat of a odd thing but I'm beginning to think that by using it instead I could avoid some of these bugs.

Anyone ever had a similar revelation or I'm just grasping at straws here?

Some take away points

My current line of thinking is that type-safety outweighs any benefit
of using printf. The real problem is the format string and the use of
non type-safe variadic functions.

Maybe I won't be using << and the stl output stream variants but I will certainly look into using a type-safe mechanism which is very similar.

A lot of the tracing/logging is conditional but I'd like to always run the code to not miss bugs in tests just because it's a rarely taken branch.

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
If this question can be reworded to fit the rules in the help center, please edit the question.

@user827992: Are you missing the fact that the C++ standard includes the C standard library by reference? It's perfectly legal to use printf in C++. (Whether it's a good idea is another question.)
–
Keith ThompsonAug 16 '12 at 19:10

@KeithThompson yes, but why using a language like a Matryoshka ?
–
user827992Aug 16 '12 at 19:16

2

@user827992: printf does have some advantages; see my answer.
–
Keith ThompsonAug 16 '12 at 19:24

1

This question is pretty borderline. "What do you guys think" questions are often closed.
–
dbraceyAug 16 '12 at 19:37

9 Answers
9

printf, particularly in cases where you might care about performance (such as sprintf and fprintf) is a really strange hack. It constantly amazes me that people who pound on C++ due to miniscule performance overhead related to virtual functions will then go on to defend C's io.

Yes, in order to figure out the format of our output, something that we can know 100% at compile time, let's parse a fricken format string at runtime inside a massively weird jump table using inscrutable format codes!

Of course these format codes couldn't be made to match the types they represent, that would be too easy... and you're reminded every time you lookup whether it's %llg or %lg that this (strongly typed) language makes you figure out types manually to print/scan something, AND was designed for pre-32bit processors.

I'll admit that C++'s handling of format width and precision is bulky and could use some syntactic sugar, but that doesn't mean you have to defend the bizarre hack that is C's main io system. The absolute basics are pretty easy in either language (although you should probably be using something like a custom error function/error stream for debug code anyways), the moderate cases are regex-like in C (easy to write, hard to parse/debug), and the complex cases impossible in C.

(If you use the standard containers at all, write yourself some quick templated operator<< overloads that allow you to do things like std::cout << my_list << "\n"; for debug, where my_list is of type list<vector<pair<int,string> > >.)

The problem of standard C++ library is, that most incarnations implement operator<<(ostream&, T) by calling... well, sprintf! The performance of sprintf is not optimal, but due to this, performance of iostreams is generally even worse.
–
Jan HudecAug 17 '12 at 11:44

@JanHudec: That hasn't been true for about a decade at this point. The actual printing is done with the same underlying system calls, and C++ implementations often call into C libraries for that... but that's not the same thing as routing std::cout through printf.
–
jkerianAug 17 '12 at 15:01

Mixing C-style printf() (or puts() or putchar() or ...) output with C++-style std::cout << ... output can be unsafe. If I recall correctly, they can have separate buffering mechanisms, so the output might not appear in the intended order. (As AProgrammer mentions in a comment, sync_with_stdio addresses this).

printf() is fundamentally type-unsafe. The type expected for an argument is determined by the format string ("%d" requires an int or something that promotes to int, "%s" requires a char* which must point to a correctly terminated C-style string, etc.), but passing the wrong type of argument results in undefined behavior, not a diagnosable error. Some compilers, such as gcc, do a reasonably good job of warning about type mismatches, but they can do so only if the format string is a literal or is otherwise known at compile time (which is the most common case) -- and such warnings are not required by the language. If you pass the wrong type of argument, arbitrarily bad things can happen.

C++'s stream I/O, on the other hand, is much more type-safe, since the << operator is overloaded for many different types. std::cout << x doesn't have to specify the type of x; the compiler will generate the right code for whatever type x has.

On the other hand, printf's formatting options are IMHO much more convenient. If I want to print a floating-point value with 3 digits after the decimal point, I can use "%.3f" -- and it has no effect on other arguments, even within the same printf call. C++'s setprecision, on the other hand, affects the state of the stream, and can mess up later output if you're not very careful to restore the stream to its previous state. (This is my personal pet peeve; if I'm missing some clean way to avoid it, please comment.)

Both have advantages and disadvantages. The availability of printf is particularly useful if you happen to have a C background and you're more familiar with it, or if you're importing C source code into a C++ program. std::cout << ... is more idiomatic for C++, and doesn't require as much care to avoid type mismatches. Both are valid C++ (the C++ standard includes most of the C standard library by reference).

It's probably best to use std::cout << ... for the sake of other C++ programmers who may work on your code, but you can use either one -- especially in trace code that you're going to throw away.

And of course it's worth spending some time learning how to use debuggers (but that might not be feasible in some environments).

No mention of mixing in the original question.
–
dbraceyAug 16 '12 at 19:36

1

@dbracey: No, but I thought it was worth mentioning as a possible drawback of printf.
–
Keith ThompsonAug 16 '12 at 19:40

5

For the synchronization issue, see std::ios_base::sync_with_stdio.
–
AProgrammerAug 16 '12 at 19:56

+1 Using std::cout to print debug info in a multithreaded app is 100% useless. At least with printf things aren't as likely to be interleaved and unparsable by man or machine.
–
JamesAug 16 '12 at 20:59

@James: Is that because std::cout uses a separate call for each item that's printed? You could work around that by collecting a line of output into a string before printing it. And of course you could also print one item at a time with printf; it's just more convenient to print a line (or more) in one call.
–
Keith ThompsonAug 16 '12 at 21:35

The Google style guide is nice, BUT it contains quite a few items that are not suitable for a general purpose guide. (which is ok, because after all it's Google's guide for code running at/for Google.)
–
Martin BaAug 17 '12 at 7:38

Your problem is most likely coming from the mixing of two very different standard output managers, each of which has it's own agenda for that poor little STDOUT. You get no guarantees about how they are implemented, and it is perfectly possible that they set conflicting file descriptor options, both try to do different things to it, etc. Also, the insertion operators have one major over printf: printf will let you do this:

printf("%d", SomeObject);

Whereas << will not.

Note: For debugging, you don't use printf or cout. You use fprintf(stderr, ...) and cerr.

No mention of mixing in the original question.
–
dbraceyAug 16 '12 at 19:36

Of course you can print the address of an object but the big difference is that printf isn't type-safe and I'm current line of thinking is that type-safety outweighs any benefit of using printf. The problem is really the format string and the not type-safe variadic function.
–
John LeidegrenAug 17 '12 at 10:45

@JohnLeidegren: But what if SomeObject isen't a pointer? YOur going to get arbitrary binary data that the compiler decides represents SomeObject.
–
LinuxiosAug 17 '12 at 14:50

The typesafeprintf script can preprocess your printf-style calls to make them type-safe.

Libraries such as Boost.Format and FastFormat let you use printf-like format strings (Boost.Format's in particular are almost identical to printf) while keeping iostreams' type safety and type extensibility.

Printf syntax is basically fine, minus some of obscure typing. If you think it's wrong why C#, Python and other languages use the very similar construct? The problem in C or C++: it's not part of a language and thus not checked by compiler for correct syntax(*) and not decomposed into series of native calls if optimizing for speed. Note that if optimizing for size, printf calls might turn out more efficient!
C++ streaming syntax is imho anything but good. It works, type-safety is there, but the verbose syntax... bleh. I mean I use it, but with no joy.

(*) some compilers DO this checking plus almost all static analysis tools (I use Lint and never had any problem with printf since).

There is Boost.Format that combines the convenient syntax (format("fmt") % arg1 % arg2 ...;) with type-safety. At cost of some more performance, because it generates stringstream calls that internally generate sprintf calls in many implementations.
–
Jan HudecAug 17 '12 at 11:48

However, where you may want to use the CPP << operator is when you overload it for a particular method... for example to get a dump of an object that holds the data of a particular person, PersonData....

You are not supposed to use printf in C++. Ever. The reason is, as you correctly noted, that it's a source of bugs and the fact that printing custom types, and in C++ almost everything should be custom types, is pain. C++ solution is the streams.

However there is a critical problem that makes streams unsuitable for any and user-visible output! The problem is translations. Borrowing example from the gettext manual say you want to write:

Now the German translator comes along and says: Ok, in German, the message should be

n Zeichen lang ist die Zeichenkette 's'

And now you are in trouble, because he needs the pieces shuffled around. It should be said, that even many implementation of printf have problem with this. Unless they support the extension so you can use

printf("%2$d Zeichen lang ist die Zeichenkette '%1$s'", ...);

The Boost.Format supports printf-style formats and has this feature. So you write:

cout << format("String '%1' has %2 characters\n") % str % str.size();

Unfortunately it carries a bit of performance penalty, because internally it creates a stringstream and uses the << operator to format each bit and in many implementation the << operator internally calls sprintf. I suspect more efficient implementation would be possible if really desired.