9

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.
16
  • 4
    printf in the C++ world? I'm missing something here?
    – user827992
    Aug 16, 2012 at 19:03
  • 10
    @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.) Aug 16, 2012 at 19:10
  • 2
    @user827992: printf does have some advantages; see my answer. Aug 16, 2012 at 19:24
  • 1
    This question is pretty borderline. "What do you guys think" questions are often closed.
    – dbracey
    Aug 16, 2012 at 19:37
  • 1
    @vitaut I guess (thanks for the tip). I'm just a bit perplexed by the aggressive moderation. It doesn't really foster interesting discussions about programming situations which is what I would like to have more of. Oct 24, 2015 at 15:18

9 Answers 9

2

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> > >.)

2
  • 1
    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 Hudec
    Aug 17, 2012 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.
    – jkerian
    Aug 17, 2012 at 15:01
16

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).

6
  • No mention of mixing in the original question.
    – dbracey
    Aug 16, 2012 at 19:36
  • 1
    @dbracey: No, but I thought it was worth mentioning as a possible drawback of printf. Aug 16, 2012 at 19:40
  • 6
    For the synchronization issue, see std::ios_base::sync_with_stdio. Aug 16, 2012 at 19:56
  • 1
    +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.
    – James
    Aug 16, 2012 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. Aug 16, 2012 at 21:35
2

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.

4
  • No mention of mixing in the original question.
    – dbracey
    Aug 16, 2012 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. Aug 17, 2012 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.
    – Linuxios
    Aug 17, 2012 at 14:50
  • I think I read your answer backwards... nvm. Aug 17, 2012 at 14:56
1

There are many groups - for example google - that don't like streams.

http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Streams

(Pop open the triangle thingy so you can see the discussion.) I think the google C++ style guide has LOTS of very sensible advice.

I think the tradeoff is that streams are safer but printf is clearer to read (and easier to get exactly the formatting you want).

1
  • 2
    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 Ba
    Aug 17, 2012 at 7:38
1

printf can cause bugs due to the lack of type safety. There are a few ways of addressing that without switching to iostream's << operator and more complicated formatting:

  • Some compilers (such as GCC and Clang) can optionally check your printf format strings against the printf arguments and can display warnings such as the following if they don't match.
    warning: conversion specifies type 'int' but the argument has type 'char *'
  • 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.
1

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).

1
  • 1
    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 Hudec
    Aug 17, 2012 at 11:48
0

printf is, in my own opinion, a far more flexible output tool for dealing with variables than any of the CPP stream outputs. For example:

printf ( "%d in ANSI = %c\n", j, j ); /* Perfectly valid... if a char ISN'T printing right, I'd just check the integer value to make sure it was okay. */

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....

ostream &operator<<(ostream &stream, PersonData obj)
{
 stream << "\nName: " << name << endl;
 stream << " Number: " << phoneNumber << endl;
 stream << " Age: " << age << endl;
 return stream;
}

For that, it would be far more effective to say (assuming a is an object of PersonData)

std::cout << a;

than:

printf ( "Name: %s\n Number: %s\n Age: %d\n", a.name, a.number, a.age );

The former is far more in-line with the principle of encapsulation (No need to know the specifics, private member variables), and is also easier to read.

0

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:

cout << "String '" << str << "' has " << str.size() << " characters\n";

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.

-1

You are doing a lot of useless work, beside the fact that stl is evil or not, debug your code with a series of printf only add 1 more level of possible failures.

Just use a debugger and read something about Exceptions and how to catch and throw them; try to not be more verbose than you actually need to be.

PS

printf is used in C, for the C++ you have std::cout

1
  • You don't use tracing/logging instead of a debugger. Aug 17, 2012 at 10:46

Not the answer you're looking for? Browse other questions tagged or ask your own question.