Using printf in C++

A

Adam Skutt

actually, I would personally like more formatting options, as the
current options are a little sparse and require some manual treatment.

examples: printing output with variable indentation (take argument and
print that many spaces or tabs), ability to use certain POSIX
extensions, ability to print a string automatically adding quotes and
character escapes, ...

So write them. This is one of the biggest advantages of iostreams
compared to printf and C FILE I/O. You can write as many manipulators
as you need.

Adam
 
A

Adam Skutt

yep.

it is also notable that, for the most part, languages which followed
after C++ still tended towards more C-like interfaces regarding printing
(function or method, often with some sort of formatting string, and
arguments following after said string).

I'm not sure what languages you're talking about, but if yo're taking
about Java, it's not true. Java didn't add any general-purpose string
formatting until Java 5. If you wanted textual I/O before then, you
converted everything to a string manually, including invoking the
formatters if necessary.

Java added formatting strings because they were better than what was
originally provided. I'm not sure you can use that to conclude
superiority over anything else. I suspect there was a considerable
amount of appealing to the masses, too. They also go beyond mere C
printf format strings.

Many languages don't have anything close to how iostreams looks to the
user programmer because it's simply not possible for those language to
do much of what iostreams does. Java lacks operator overloading and
template specialization; C# lacks the latter. You can't recreate
anything approaching C++ iostreams without a lot of extra pain,
overhead, and ugliness.

Also, most "post-C++" object-oriented languages do pick up many of the
bits from iostreams that they could, like the ability to extend the
class hierarchy to support additional I/O sources and locale /
encoding support. As such, they "took" what they could and they're
not really all that C-like as a result. Even when talking about
format strings, they provide additional (runtime) type safety over C
which provides an inherently superior solution.

Adam
 
M

Martin B.

It's also not the least bit comparable to what your alternative code
is doing. To dismiss it based on this analysis alone is either
suggests that you don't know what iostreams does out of the box, nor
how to change the default behavior. Regardless, this comparsion
doesn't pass anything resembling intellectual honesty, nor can it
ever, really.

Your argument is fundamentally fallacious because you're taking a tiny
minority of programs and pretending they are the general case. Yes, C+
+ iostreams will still have more overhead compared to C FILE I/O,
which has more overhead compared to OS-specific I/O. However, that
overhead gets you plenty of extra functionality, (...)

The problem here is that for the "normal" iostreams/cout case, 90% of
programmers don't need the extra functionality 90% of the time!
Just because you don't need those features
and can't afford the overhead does not let you conclude "cout is
useless in real applications," and it will never let you do so.

The problem here is that apparently iostreams does not follow C++'s
philosophy of You Dont't Pay For What You Don't Use.

Note that *I'm* not saying iostreams is useless. There are enough
applications where the fact that it is in the standard lib and it's
extra type-safety more than make up for any potential (and, most likely,
ignorable) performance overhead they *may* incur.

It's just that iostreams (the part of it vs. *printf-family) *seems* to
be one of the parts of the standard library that is rather more
controversial than others. (In my limited experience at least.)

cheers,
Martin
 
A

Adam Skutt

The problem here is that for the "normal" iostreams/cout case, 90% of
programmers don't need the extra functionality 90% of the time!

What features exactly do you think they don't need? The automatic
synchronization with C stdio? The ability to extend the hierarchy
with custom streams and buffers? The enhanced locale support? The
enhanced narrow/wide support? The ability to define custom
manipulators? The exception handling? The type safety?
The problem here is that apparently iostreams does not follow C++'s
philosophy of You Dont't Pay For What You Don't Use.

It's not really true, anyway, which was my whole point. You can
switch off a good bit of the overhead, some badly behaved
implementations aside.

It's just that iostreams (the part of it vs. *printf-family) *seems* to
be one of the parts of the standard library that is rather more
controversial than others. (In my limited experience at least.)

This is because there are too many brain-damaged C programmers out
there.

Adam
 
M

Martin B.

What features exactly do you think they don't need? The automatic
synchronization with C stdio? The ability to extend the hierarchy
with custom streams and buffers? The enhanced locale support? The
enhanced narrow/wide support? The ability to define custom
manipulators? The exception handling? The type safety?

From my personal experience, I absolutely *don't* need custom streams
and buffers, enhanced locale support, what enhanced narrow/wide support
btw.(?), custom manipulators, exception handling (what do you mean by
that exactly?).

Yet, as far as I understand the design of iostreams, I *pay* for all of
these. (Negligible as the cost may be depending on the circumstances.)
It's not really true, anyway, which was my whole point. You can
switch off a good bit of the overhead, some badly behaved
implementations aside.

People go with the defaults and if you've got a bad implementation, most
of the time you simply have to deal with it.
This is because there are too many brain-damaged C programmers out
there.

The brain damaged C programmers (of which I'm not sure how many there
are anyway) also don't like any other part of C++, so that doesn't
explain why iostreams get less love than other parts of the std lib.

cheers,
Martin
 
L

Luca Risolia

From my personal experience, I absolutely *don't* need custom streams
and buffers, enhanced locale support, what enhanced narrow/wide support
btw.(?), custom manipulators, exception handling (what do you mean by
that exactly?).

Yet, as far as I understand the design of iostreams, I *pay* for all of
these. (Negligible as the cost may be depending on the circumstances.)

I think you don't know what you are talking about. With regard to
standard streams, exceptions are disabled by default and can be enabled
via ios::exceptions() optionally; manipulators can be easily expanded
in-line by any modern compiler with no performance loss, and finally
buffers are supposed to improve the efficiency compared to the classic
I/O in C, which cannot be guaranteed to use buffers. (Yet you can
flush() the C++ stream buffers whenever you need to). Not to mention
what has been already said elsewhere about type-safety and better
readability that C++ streams generally offer.
 
J

jacob navia

Le 19/05/12 21:11, Luca Risolia a écrit :
manipulators can be easily expanded
in-line by any modern compiler with no performance loss,


As you (may) know, "expanding in line" produces precisely
the code bloat people are complaining about.

Before saying that

"you don't know what you are talking about"

make sure YOU know what you are talking about. Code bloat
fills the caches of the CPU with less dense code, so more
wait cycles must be used to load more code into them.

Code size is very important for cache usage... If your
code is "expanded inline", instead of a few instructions
making a function call you have enormouse sequences of
almost identical code that is replicated all around...

Typical C++ problem... abusive usage of "inline".

But behind this questions is a philosophy, or better
"attitude" towards programming.

The C++ way is to complexify ad nauseaum everything.

Why use printf when you can use overloaded operators
with exceptions, traits, manupilators, what have you?

jacob
 
D

Dombo

Op 19-May-12 21:11, Luca Risolia schreef:
I think you don't know what you are talking about. With regard to
standard streams, exceptions are disabled by default and can be enabled
via ios::exceptions() optionally; manipulators can be easily expanded
in-line by any modern compiler with no performance loss, and finally
buffers are supposed to improve the efficiency compared to the classic
I/O in C, which cannot be guaranteed to use buffers. (Yet you can
flush() the C++ stream buffers whenever you need to). Not to mention
what has been already said elsewhere about type-safety and better
readability that C++ streams generally offer.

I agree with most of what you say here, but I think the readability part
is a matter of opinion. When it comes to formatting I find the C++
stream approach generally less readable than the more concise way this
aspect is handled with printf().
 
D

Dombo

Op 19-May-12 21:34, jacob navia schreef:
Le 19/05/12 21:11, Luca Risolia a écrit :

As you (may) know, "expanding in line" produces precisely
the code bloat people are complaining about.

Not always. Sometimes putting the function arguments in the right
registers and or on the stack and calling the function results in more
code at the call side than just inlining the function body.

Also inlining is just a hint to the compiler. The compiler may ignore
the hint, or even inline code for which there is no explicit hint from
the programer to inline, based on criteria decided by whoever made the
compiler. C++ compilers and linkers got a lot "smarter" the last couple
of years, things that used to be always true may no longer be always true.
Before saying that

"you don't know what you are talking about"

make sure YOU know what you are talking about. Code bloat
fills the caches of the CPU with less dense code, so more
wait cycles must be used to load more code into them.

Code size is very important for cache usage... If your
code is "expanded inline", instead of a few instructions
making a function call you have enormouse sequences of
almost identical code that is replicated all around...

Typical C++ problem... abusive usage of "inline".

Code bloat is not my main concern with "inline", though it is something
to watch out for. A bigger concern with inlining (and thus also
templates) for me is that the definitions of all the inline functions
must be visible to each compilation unit in which may use them directly
or indirectly. This easily results in enormous amounts of code that
needs to be included and processed by each compilation unit. Though this
does not necessarily result in large(r) executables, it increases build
times, which can become quite painful for large projects which may take
hours to build because of this. Tricks to improve build times like
pre-compiled headers may help, but can also be worse than the problem
they try to solve.
But behind this questions is a philosophy, or better
"attitude" towards programming.

The C++ way is to complexify ad nauseaum everything.

Why use printf when you can use overloaded operators
with exceptions, traits, manupilators, what have you?

After reading this I wonder why you even bother with C++.
 
A

Alf P. Steinbach

Hi,

I'm new to C++, but I already did some work with C (in fact, I do my
every day work with Fortran).

I really like 'printf' (i.e., I got used to it) and I'm not liking
'cout' that much.

In the book I'm following (C++ Primer Plus) the author advocates the
power of cout, but he doesn't say that using printf in C++ is wrong.

So I ask: Is it wrong or not-advisable to use printf (more precisely
fprintf) in C++ programs to do the I/O? My work involves constantly
reading/writing data files with specific number formatting. Should I
learn how to use cout more effectivelly (RTFM to the end) or can I
stick with printf.

Write your own abstraction, or check out Boost.

That's because printf etc. are unsafe, and iostreams are inefficient,
complex and verbose, not to mention unreliable and non-portable.

Example of unreliability and non-portability: as of version 4.6.1 MinGW
g++ still does not implement iostream locale support in Windows.

Another example of unreliability and non-portability: as of version 11
Visual C++ still does not implement wide-to-narrow conversion correctly.

I.e., you cannot in general rely on iostream-based code that works with
one compiler and platform, to work with another compiler and platform.


Cheers & hth.,

- Alf
 
L

Luca Risolia

Code size is very important for cache usage... If your
code is "expanded inline", instead of a few instructions
making a function call you have enormouse sequences of
almost identical code that is replicated all around...

Typical C++ problem... abusive usage of "inline".

Typically you don't need to explicitly care about function in-lining,
because typically you want to profit by or rely on the compiler
optimizations, in which case the compiler is free to decide whether or
not to in-line any (non-member) function call (no matter what you
specified). This is the case with manipulators, where the compiler can
take the costs of actually expanding the call into account before deciding.
 
L

Luca Risolia

in which case the compiler is free to decide whether or
not to in-line any (non-member) function call

...sorry, just to be clear, member functions defined INSIDE the body of
the class are "inline" implicitly.
 
I

Ian Collins

Le 19/05/12 21:11, Luca Risolia a écrit :


As you (may) know, "expanding in line" produces precisely
the code bloat people are complaining about.

It often produces the exact opposite.
Before saying that

"you don't know what you are talking about"

make sure YOU know what you are talking about. Code bloat
fills the caches of the CPU with less dense code, so more
wait cycles must be used to load more code into them.

Code size is very important for cache usage... If your
code is "expanded inline", instead of a few instructions
making a function call you have enormouse sequences of
almost identical code that is replicated all around...

Which is why compilers have target specific optimisation heuristics and
options. Performance focussed compilers also offer profile feedback so
the application's performance can be tuned to match its workload.
Typical C++ problem... abusive usage of "inline".

inline is a hint, nothing more.
But behind this questions is a philosophy, or better
"attitude" towards programming.

The C++ way is to complexify ad nauseaum everything.

Why use printf when you can use overloaded operators
with exceptions, traits, manupilators, what have you?

I've answered that several times: how else to do print something inside
a function template?

I somehow know you won't respond to that.
 
J

jacob navia

Le 19/05/12 23:26, Ian Collins a écrit :
It often produces the exact opposite.

Look. A function call with two arguments produces
movq arg1, %ecx
movq arg2, %edx
call foo

in a modern processor:

[0000000] 8b0d00000000 mov 0,%ecx
[0000006] 8b1500000000 mov 0,%edx
[0000012] e800000000 callq foo
[0000017] 90 nop

17 bytes then. So the call instruction will ALWAYS win (for a function
with 2 arguments) unless the code of the inlined function is smaller
than 17 bytes!

Note that a simple move is already 6 bytes, and you need two at least
to make a simple addition.

So, you are saying that "often" the body of an inlined function is
less than 16 bytes. I would like to know where do you get your
data from.

You can say anything in a newsgroup Ian, even that inlining reduces code
size.
I've answered that several times: how else to do print something inside
a function template?

I somehow know you won't respond to that.

Of course! But that wasn't the example that we are discussing!
The example had no templates, just a normal table printing, nothing
fancy or needing operator overloading + several dozen function
calls.
 
J

jacob navia

Le 19/05/12 22:21, Dombo a écrit :
Op 19-May-12 21:34, jacob navia schreef:

Not always. Sometimes putting the function arguments in the right
registers and or on the stack and calling the function results in more
code at the call side than just inlining the function body.

When that will be the case?

Look. A function call with two arguments produces
movq arg1, %ecx
movq arg2, %edx
call foo

in a modern processor:

[0000000] 8b0d00000000 mov 0,%ecx
[0000006] 8b1500000000 mov 0,%edx
[0000012] e800000000 callq foo
[0000017] 90 nop

17 bytes then. So the call instruction will ALWAYS win (for a function
with 2 arguments) unless the code of the inlined function is smaller
than 17 bytes!

Note that a simple move is already 6 bytes, and you need two at least
to make a simple addition.

So, you are saying that "sometimes" the body of an inlined function is
less than 16 bytes. I would like to know where do you get your
data from.
After reading this I wonder why you even bother with C++.

I do not "bother" with C++
 
I

Ian Collins

Le 19/05/12 23:26, Ian Collins a écrit :
It often produces the exact opposite.

Look. A function call with two arguments produces
movq arg1, %ecx
movq arg2, %edx
call foo

in a modern processor:

[0000000] 8b0d00000000 mov 0,%ecx
[0000006] 8b1500000000 mov 0,%edx
[0000012] e800000000 callq foo
[0000017] 90 nop

17 bytes then. So the call instruction will ALWAYS win (for a function
with 2 arguments) unless the code of the inlined function is smaller
than 17 bytes!

Note that a simple move is already 6 bytes, and you need two at least
to make a simple addition.

So, you are saying that "often" the body of an inlined function is
less than 16 bytes. I would like to know where do you get your
data from.

You can say anything in a newsgroup Ian, even that inlining reduces code
size.

You aren't taking all the other optimisation possibilities into account.
If the compiler can see the code for the called function, it can,
amongst other things, reuse registers and optimise the caller+callee as
a whole.

Anyway, if you want a simple counterexample of a common case, try a
simple index operator:

struct X {
int data[8];

int operator[](unsigned n) { return data[n]; }
};

void g(int);

void f() {
X x;

g( x[0] );
}

The inline version will simply pass x.data[0] to g. So all of the call
set up in f and function preamble in operator[] disappears. In this
example, the situation improves further when simple error checking is added:

int operator[](unsigned n) { assert(n<8); return data[n]; }

The with inline compiler knows the n is constant in this case, so
because it can optimise the caller+callee as a whole, the assertion
disappears and the generated code is no different than before. The same
applies if n > 8, the operator call is simply replaced with a call to
__assert.

So yes Jacob, inlining can (and often does) reduce code size.
Of course! But that wasn't the example that we are discussing!
The example had no templates, just a normal table printing, nothing
fancy or needing operator overloading + several dozen function
calls.

You snipped your own question that I was attempting to answer "Why use
printf when you can use overloaded operators with exceptions, traits,
manupilators, what have you?"
 
I

Ian Collins

Le 19/05/12 22:21, Dombo a écrit :
Op 19-May-12 21:34, jacob navia schreef:

Not always. Sometimes putting the function arguments in the right
registers and or on the stack and calling the function results in more
code at the call side than just inlining the function body.

When that will be the case?

Look. A function call with two arguments produces
movq arg1, %ecx
movq arg2, %edx
call foo

in a modern processor:

[0000000] 8b0d00000000 mov 0,%ecx
[0000006] 8b1500000000 mov 0,%edx
[0000012] e800000000 callq foo
[0000017] 90 nop

17 bytes then. So the call instruction will ALWAYS win (for a function
with 2 arguments) unless the code of the inlined function is smaller
than 17 bytes!

Note that a simple move is already 6 bytes, and you need two at least
to make a simple addition.

So, you are saying that "sometimes" the body of an inlined function is
less than 16 bytes. I would like to know where do you get your
data from.

Your example above with immediate data is the perfect case where
inlining a function that performs a simple addition is a big win. The
compiler will simply replace all of the above with 0. Try it for your self:

void g(int); // to prevent optimisation to nothing!

int add( int a, int b ) { return a+b; }

void f0() { g(add(0,0)); }

Inline off (g++ -xO1):

movl $0, 4(%esp)
movl $0, (%esp)
call add
movl %eax, (%esp)
call g

Inline on (g++ -xO2):

movl $0, (%esp)
call g

Even where the data isn't immediate, the compiler can perform the
addition of the variables off the stack, or better still, in registers.
I do not "bother" with C++

Maybe you should make the effort to understand it before running it down
all the time.
 
M

Martin B.

I think you don't know what you are talking about. With regard to
standard streams, exceptions are disabled by default and can be enabled
via ios::exceptions() optionally; manipulators can be easily expanded
in-line by any modern compiler with no performance loss, and finally
buffers are supposed to improve the efficiency compared to the classic
I/O in C, which cannot be guaranteed to use buffers. (Yet you can
flush() the C++ stream buffers whenever you need to). Not to mention
what has been already said elsewhere about type-safety and better
readability that C++ streams generally offer.

What I do know is that all iostreams implementations I've checked will
fall back to one of the *printf functions at their core to do the actual
work. (If you know one that does not, feel free to add it here:
http://stackoverflow.com/q/7684472/321013)

cheers,
Martin
 
D

Dombo

Op 20-May-12 2:02, Ian Collins schreef:
Le 19/05/12 22:21, Dombo a écrit :
Op 19-May-12 21:34, jacob navia schreef:
Le 19/05/12 21:11, Luca Risolia a écrit :
manipulators can be easily expanded
in-line by any modern compiler with no performance loss,

As you (may) know, "expanding in line" produces precisely
the code bloat people are complaining about.

Not always. Sometimes putting the function arguments in the right
registers and or on the stack and calling the function results in more
code at the call side than just inlining the function body.

When that will be the case?

Look. A function call with two arguments produces
movq arg1, %ecx
movq arg2, %edx
call foo

in a modern processor:

[0000000] 8b0d00000000 mov 0,%ecx
[0000006] 8b1500000000 mov 0,%edx
[0000012] e800000000 callq foo
[0000017] 90 nop

17 bytes then. So the call instruction will ALWAYS win (for a function
with 2 arguments) unless the code of the inlined function is smaller
than 17 bytes!

Note that a simple move is already 6 bytes, and you need two at least
to make a simple addition.

So, you are saying that "sometimes" the body of an inlined function is
less than 16 bytes. I would like to know where do you get your
data from.

Your example above with immediate data is the perfect case where
inlining a function that performs a simple addition is a big win. The
compiler will simply replace all of the above with 0. Try it for your self:

void g(int); // to prevent optimisation to nothing!

int add( int a, int b ) { return a+b; }

void f0() { g(add(0,0)); }

Inline off (g++ -xO1):

movl $0, 4(%esp)
movl $0, (%esp)
call add
movl %eax, (%esp)
call g

Inline on (g++ -xO2):

movl $0, (%esp)
call g

Even where the data isn't immediate, the compiler can perform the
addition of the variables off the stack, or better still, in registers.

There is also a somewhat hidden cost when making a function call, they
tend to get in the way of the optimizer. When calling a function the
optimizer can no longer decide which registers to use for that function
given the call context and it has to be conservative with assumptions
about the register contents after the function returns. With inlining
compiler can be a bit smarter about register allocation for both the
calling function and the function being inlined, which can save both
code and improve performance (and yes have seen that inlined code is not
merely repeated but that the inlined code was different for different
call sites). Especially with simple function such as getters and setters
inlining often reduces the code size compared to function calls.

To the question "where I do get my data from" from Jacob Navia my answer
is: I get that data by analyzing the compiler output from mostly MS
Visual C++ 6.0, 2005, 2008 and 2010, and to a lesser degree from GCC
(till about version 4.4). One of the things I've learned is that C++
compilers (and linkers) got a lot smarter the last decade and simple
optimization rules that used to be always true, do no longer always
apply. So Jacob Navia, where do you get your data from?
 
J

jacob navia

So Jacob Navia, where do you get your data from?

Look, I am a stupid C programmer and you are a smart C++ programmer, you
are right and I am wrong.

inlining functions reduces code size, and C++ is the best thing
since invented since sliced bread
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top