Using printf in C++

P

Pavel

Luca said:
Now that I thought of it more, the case seems to be deserving deeper
analysis (I myself was a moderate proponent of exceptions before and now
I am having a second thought). This is because the exception-based
method of error reporting seems to break one of the declared principles
of C++ design: you only pay for the features you use. From this
viewpoint, this is what concerns me:

There are some cases in which error reporting via exceptions is essential to
make the code *faster* than non-exception-based alternatives, for example:

while (i < N) {
__try {
for (; i < N; i++)
a = log(b * c); // floating point math
} __catch(exCode() == OVERFLOW) { // overflow ex. from the hw
a = log(b) + log(c); // redo without overflow
++i;
}
}

There the time to make support for the exception handling is negligible, since
there is only one function call in the critical loop. The exception is costly,
when it occurs, but we assume that the occurrence is rare.
Alternatives to look for errors are less efficient, for example checking for
overflow inside the loop requires two floating point comparisons, which make the
solution relatively costly; while using log(a) + log(b) directly would
double the number of calls to log().

MSVC "structured" exceptions are different; I don't know whether they unwind the
stack same way as C++ exceptions do. If they do, I would like to look at the
disassembled piece of the binary you built with this snippet: the
innocently-looking "try" block may compile into something quite unexpected (and
more expensive that the regular error processing, especially if you replace
log() with a C++ function that might create C++ objects and throw exceptions of
its own).

As for the cost of the non-exception-based error processing, it never *has* to
be higher than a single "read" memory access plus a single conditional jump
(based on testing an integer or whatever is the fastest-checked type on a
system) on a "successful" code path; of course, poorly designed APIs can make it
arbitrarily high.

-Pavel
 
L

Luca Risolia

Eh? What's __try?

I think you are mixing windows managed stuff with C++ exceptions.

__try/__except (not catch as I wrote, sorry) is a microsoft-specific
extension that let you catch hardware-based exceptions. In principle it
behaves as a normal try/catch statement. Anyway, the point was to show
an example where checking for errors in a loop might be less efficient
than using exceptions to catch them.
 
I

Ian Collins

Look, the code snippets above doe not check for errors at all. For code like
this, (such as s[n]printf to an automatically allocated buffer or C++ output of
few hundreds bytes), I think it is typical, and correct. This code pays price
for exceptions those.

I don't see a price when I test the code. With exceptions enabled,
there is a test when string calls new.
Well, let me give you an example of Symbian (proudly listed by the [The
Programming Languages
Beacon](http://www.lextrait.com/vincent/implementations.html) cited above by
Martin B. as written in C++).

Being familiar with the system quite intimately in the past, I can attest that,
true, Symbian (originally, EPOC32) was a rare example of an operating system
(very well-performing at that), almost entirely written in C++, BUT:

They did not use standard C++ exceptions in the OS. At that, they badly wanted
to use RAII (and, consequently, exceptions) and they achieved their noble goal
by creating (quite an ugly) way requiring client code cooperation via so called
"clean-up stack". That is, for the objects that you wanted to be RAII and
exception-safe, you would create an object with a (throwing, if I recall it
correctly) operator new in dynamic memory and manually put it onto so called
"cleanup stack". In case your code did not throw, you would need to clean up
your cleanup stack manually; otherwise, runtime would take care of it. You could
NOT create an exception-safe "auto" RAII object -- quite a divergence from the
standard C++ RAII implementation practice, isn't it?

If they were writing that with today's compilers, their choice would no
doubt be different. Some older compilers had pretty awful exception
implementations.
(They did not use templates either but I am not sure about the reason for that
-- probably the templates were not mature enough yet in a low-common-denominator
of MSVC (that they used to build emulator binaries) and g++ (that they used to
build production binaries) -- but exceptions certainly were)

Their excuse for the "non-standard exception" mess? -- performance concerns for
using standard C++ exceptions. I tend to believe them because the guys obviously
knew everything there is to know about the design-for-performance and then some
(I am judging by the end result of their effort: at ~1998 they were selling
products built on a rock-solid hard real-time system with preemptive
multitasking, extensive support for priorities, order-of-tens-microsecond
response times for app communicating via serial and IRDA ports and perfectly
responsive nice half-VGA-size pen-interface GUI -- all powered by a poor little
7-MHz ARM CPU. I have yet to see a better-performing RTOS -- and uglier RTOS API).

My comment above still stands 90s compilers were a different beast from
today's.
 
P

Pavel

Adam said:
What features exactly do you think they don't need? The automatic
synchronization with C stdio?
If there were only C stdio, there would be no need to synchronize with it.
The ability to extend the hierarchy with custom streams and buffers?
Yes, 90% of the time (if not 99%) people do not use custom streams and buffers
The enhanced locale support?The
enhanced narrow/wide support?
Yes, considering the cost the correct design decision would be to build a
separate set of streams for these.
The ability to define custom
manipulators?
Yes, 90% of the timem people certainly do not define custom manipulators
The exception handling?
Yes, if I/O does not throw exceptions, there is no need to handle them.
The type safety?
Type safety would be nice; it is achieved at the performance cost of multiple
extra function calls though.
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.
IMHO, Martin's statement is completely correct. Why do you want to pay for
exception handling when the algorithm you want to execute is not supposed to
throw any exceptions?
This is because there are too many brain-damaged C programmers out
there.
Brain-damaged C programmers certainly did not invent iostreams so your last
statement does not seem coherent.

-Pavel
 
P

Pavel

Jorgen said:
Well, the iostream library is one of the very first C++ libraries. I
would call it broken by design. The fumbling with the shift operators
primarily makes the code unreadable,

IME it makes the code /readable/, once you deal with the types which
printf() can't print.

const FooClient& c = ...
const in6_addr& addr = ...

os<< "Client "<< c<< " connected from "<< addr<< '\n';

compared to

char buf[INET6_ADDRSIZE];
std::fprintf(f, "Client %s connected from %s\n",
to_string(c).c_str(),
inet_ntop(INET6_ADDR, (void*)addr, buf, sizeof buf));
This is not apples-to-apples: you obviously used user-defined operator<< in your
C++ code. C code then should be allowed to use print_in6_addr(FILE*, in6_addr*)
and print_foo_client(FILE*, FooClient*) functions. With these, C code would be
more readable than C++ even though slightly wordier.

I never got familiar with that stuff, either. Perhaps others like it.

/Jorgen

-Pavel
 
I

Ian Collins

On 05/21/12 04:40 PM, Pavel wrote:

Please include some blank lines in your replies, that a hard to read.
If there were only C stdio, there would be no need to synchronize with it.
Eh?
Yes, 90% of the time (if not 99%) people do not use custom streams and buffers

I do, for sending data though things that aren't files.
Yes, considering the cost the correct design decision would be to build a
separate set of streams for these.
Yes, 90% of the timem people certainly do not define custom manipulators

But they are very convenient when you do.
Yes, if I/O does not throw exceptions, there is no need to handle them.

But it might if the underlying device does.
Type safety would be nice; it is achieved at the performance cost of multiple
extra function calls though.

How would they compare with the cost of actually formatting the data?
Or more to the point, flushing it to a file or some other device?
IMHO, Martin's statement is completely correct. Why do you want to pay for
exception handling when the algorithm you want to execute is not supposed to
throw any exceptions?

What if the code is changed to output to some device that does throw
exceptions? You still haven't quantified the cost.
 
A

Adam Skutt

If there were only C stdio, there would be no need to synchronize with it..

Cute. If there wasn't C stdio, there wold also be no need to
synchronize with it. And oh look, you can turn it off if you know you
never use C I/O routines with the standard streams.
Yes, 90% of the time (if not 99%) people do not use custom streams and buffers

The 10% - 1% still find it quite a useful feature. That's adequate
enough to justify their inclusion. Especially seeing as everyone else
either pays a very small cost, or no cost at all.

The enhanced locale support?The

Yes, considering the cost the correct design decision would be to build a
separate set of streams for these.

Utter nonsense. Locale handling is something that should be automatic
and transparent to the user; it is something you ought to do whenever
you are doing formatted output. Unsurprisingly, printf does it as
well. But the costs of the enhanced support (so it can be extended)
are small / non-existent if you don't use them.

Ditto the correct handling of wide characters if you have them. And
thanks to templates, a proper implementation means you only pay
conversions costs if you need to do so.

The ability to define custom> manipulators?

Yes, 90% of the timem people certainly do not define custom manipulators
The exception handling?
Yes, if I/O does not throw exceptions, there is no need to handle them.

So you don't handle them then, and you pay nothing for it in a sane
implementation besides a larger executable (if that). This is almost
always not a BFD, even when people mistakenly believe it is.

The type safety?

Type safety would be nice; it is achieved at the performance cost of multiple
extra function calls though.

That entirely depends on the compiler and a few other considerations.
IMHO, Martin's statement is completely correct. Why do you want to pay for
exception handling when the algorithm you want to execute is not supposedto
throw any exceptions?

I don't pay for it.
Brain-damaged C programmers certainly did not invent iostreams so your last
statement does not seem coherent.

But they do think that printf is superior to anything else, despite
its legion of problems, and will use any excuse to deride alternatives
even when they plainly do not understand them at all.

Adam
 
B

BGB

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.


I might eventually write a "printf replacement" anyways, and might also
consider some sort of plug-in interface for it.

the biggest personal disadvantage of iostreams in my case is that it is
a primarily C codebase. most functionality needs to work in C, and C++
is used sometimes mostly for "optional cosmetic improvements".

decided to leave out a lot of it, but the major issue is mostly related
to the use of special code-processing tools (which do their own parsing,
....), and which can't deal with full C++ (meaning plain C and/or using
C++ subsets which some of the tools will accept).

going and fixing all my tools to be able to accept C++ (or, in some
cases, even the full form of C: some make impositions on things like
declaration syntax and formatting due to the use of naive line-by-line
parsers) has not really been justifiably worth the effort.


or such...
 
B

BGB

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.

I was thinking more things like "Console.WriteLine" and similar in C#.

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.

yes, but they have a similar style, and don't borrow the iostreams
overloaded << and >> funkiness.

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.

yes, but even without operator overloading, they could have hard-coded
<< and >> to do certain things with certain types (like, make it do
something special if the left-hand type is an "OutputStream" or
"InputStream" or similar).


Java already did something similar with many types and "+" (special
behavior if the left-hand type is a string, ...).

more likely, it was because this style was "not very appealing" in
general (vs other possible options).

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.

what defines a C-like formatting string interface is not it lack of
extensibility or type-safety: these are limitations more related to how
C handles variable argument lists and similar, and not in the design of
the interface itself.


meanwhile, many of these languages have adopted something comparable to
C's interface (format strings followed by arguments), as opposed to the
"cout << whatever;" style of iostreams.

or, at least those languages which didn't instead use more shell-like or
basic-like printing interfaces:
echo "foo " something " bar"
or:
print "foo "; something; " bar"
or:
....
 
J

Jorgen Grahn

Jorgen said:
On 11.05.12 20.05, Seneika wrote:
I really like 'printf' (i.e., I got used to it) and I'm not liking
'cout' that much.

Well, the iostream library is one of the very first C++ libraries. I
would call it broken by design. The fumbling with the shift operators
primarily makes the code unreadable,

IME it makes the code /readable/, once you deal with the types which
printf() can't print.

const FooClient& c = ...
const in6_addr& addr = ...

os<< "Client "<< c<< " connected from "<< addr<< '\n';

compared to

char buf[INET6_ADDRSIZE];
std::fprintf(f, "Client %s connected from %s\n",
to_string(c).c_str(),
inet_ntop(INET6_ADDR, (void*)addr, buf, sizeof buf));
This is not apples-to-apples: you obviously used user-defined operator<< in your
C++ code.

Yes -- the kind of thing you do *once* per type in your program, close
to the definition of the type (or in this case, in some "my socket C++
stuff" file). Then it works automatically in the hundreds or
thousands of places where it's used.

That's why I felt I didn't have to add the definition of os << addr
to the "cost" of the printout.
C code then should be allowed to use print_in6_addr(FILE*, in6_addr*)
and print_foo_client(FILE*, FooClient*) functions. With these, C code would be
more readable than C++ even though slightly wordier.

We're not talking C though, are we? I assumed we talked iostreams
versus printf in C++; my fprintf example is clearly not C.

I also really don't see how a print_in6_addr(FILE*, const in6_addr&)
would help. Do you find this readable?

std::fputs("Client ", f);
print_foo_client(f, c);
std::fputs(" connected from ", f);
print_in6_addr(f, addr);
std::fputs("\n", f);

/Jorgen
 
A

Adam Skutt

Will you favor us with enlightenment as to the "legion of problems" associated
with snprintf, fprintf, printf?

Lack of type safety. Poor and inconsistently extended
implementations. Inability to use directly with custom types.
Inconsistent and unusable return values. Some legacy platforms have
broken snprintf implementations, so you can't just blindly call the
function and assume the "right" thing happened. Maximum portability
requires bringing your own.

That's just off the top of my head.

Adam
 
A

Adam Skutt

On 5/18/2012 11:46 PM, Adam Skutt wrote:

yes, but they have a similar style, and don't borrow the iostreams
overloaded << and >> funkiness.



yes, but even without operator overloading, they could have hard-coded
<< and >> to do certain things with certain types (like, make it do
something special if the left-hand type is an "OutputStream" or
"InputStream" or similar).

Java already did something similar with many types and "+" (special
behavior if the left-hand type is a string, ...).

No, they did not. The only type to have special behavior is
java.lang.String and only with the '+' operator.
what defines a C-like formatting string interface is not it lack of
extensibility or type-safety:

No, I think it does. Python and Java are both perfectly happy to let
me omit the type specifiers and rely on string conversion to do what I
want; likewise, they can also do the necessary coercion in limited
situations. This is simply impossible in C with mere printf and
friends. This is not a minor difference in the least. You only have
to bother with types in the formatting strings when you actually care
about the types, usually because of how you're formatting that
particular argument.
meanwhile, many of these languages have adopted something comparable to
C's interface (format strings followed by arguments), as opposed to the
"cout << whatever;" style of iostreams.

Yes, but reducing the comparisons to such a (ultimately) superficial
thing is rather absurd and shows a serious lack of understanding of
all the interfaces being discussed.

Adam
 
J

jacob navia

Le 21/05/12 20:24, Adam Skutt a écrit :
Lack of type safety.

Most compilers complain when they detect a printf format error.
This is moot.
Poor and inconsistently extended
implementations.

Excuse me but Alf P Steinbach writes about C++ iostreams
(in this same thread):

<begin quote>
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.
<end quote>

Bad implementations aren't only a problem with printf.
Inability to use directly with custom types.

So what? You format them piecewise into a string, then you print
the string. Not a big deal.
Inconsistent and unusable return values.

??? Is an exception a better behavior?

Some legacy platforms have
broken snprintf implementations, so you can't just blindly call the
function and assume the "right" thing happened. Maximum portability
requires bringing your own.

Well, the same holds for ANY feature of C++ since you will always find
some compiler that screws it up somewhere in a legacy platform.

That's just off the top of my head.

That shows. Just out of the top of your head.
jacob
 
A

Adam Skutt

Which compiler?   g++ litters code with calls to _Unwind_Resume throughthe PLT
which touchs several additional cache lines unnecessarily.  May not matter
in the typical user application, but very bad in a RTOS.

_Unwind_Resume is only called if you get an exception or forced
unwind, so one imagines it wouldn't actually show up in the
instruction cache all that often, unless it happened to share a line
with code that actually executes, yes?


An implementation of exceptions that doesn't need to support forced
unwinding (e.g., thread cancellation) wouldn't necessarily need to
generate those calls, though it would have other tradeoffs. As such,
you can even see calls to _Unwind_Resume in C code, as the exception
mechanism isn't used just for C++ exceptions on some platforms.

Adam
 
A

Adam Skutt

Le 21/05/12 20:24, Adam Skutt a crit :



Most compilers complain when they detect a printf format error.
This is moot.

No, if you're going to complain that some C++ implementations have
bugs then this cannot be a valid retort. Your choice as to which
standard you want.
Excuse me but Alf P Steinbach writes about C++ iostreams
(in this same thread):

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

Sadly, AFAIK there's no requirement for support of anything but the C
locale. This is unfortunate for application programmers, but not
really relevant here, as I can detect that situation in my code. The
only way to find out (for certain) which format characters are valid
for printf is to read the manpage and code carefully.
Another example of unreliability and non-portability: as of version 11
Visual C++ still does not implement wide-to-narrow conversion correctly.

So do some of the C routines. It's not a meaningful rebuttal. Going
to standard C routines may not save me either.
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.
<end quote>

Bad implementations aren't only a problem with printf.

My ability to mitigate these problems is far superior than with printf
and friends. The interfaces are defined better, and functional
implementations are vastly more compliant.
So what? You format them piecewise into a string, then you print
the string. Not a big deal.

Others have shown why that is ugly and undesirable. Even the Java /
C# / Python solution of implicitly calling the string conversion
method is superior to what C forces me to do.
??? Is an exception a better behavior?

Well yes, but I fail to see the relevance here. Exceptions aren't
mandatory with iostreams.
Well, the same holds for ANY feature of C++ since you will always find
some compiler that screws it up somewhere in  a legacy platform.

It's probably not a compliant C++ (or even "close enough") so I'm not
sure if I care. People targeting such things must be aware of it
upfront. Which, to be fair, is the same of snprintf too. But it also
means the story with safe string formatting in C is more than "use
snprintf".

Adam
 
A

Adam Skutt

Writing a method  to format a user-defined type (e.g. a toString() style method)
is just as viable as overloading the << operator, and much more readable.

Given the inherent difficultly of doing that in C, I really don't
think so. All other language let me hide the mess of dealing with the
temporary string, while C does not. Even Java / C# / Python don't
make me do what C does. If we're going to base it on popular opinion,
you're in the minority.

Adam
 
I

Ian Collins

Dombo said:
Op 20-May-12 2:02, Ian Collins schreef:
On 05/20/12 09:48 AM, jacob navia wrote:
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)); }

So, why would a programmer even do this?

Have you ever used a constant value to index a std::vector?
A contrived example that shows the optimizer removing useless
code doesn't really show anything.

It was in response to the call sequence already posted and illustrates
how inline helps the optimiser do a better job. The standard library is
full of trivial functions that get optimised away.
There are reasons to use inline functions, for example in a hot
function where you'll have a handful of consecutive icache lines
rather than the discontiguous lines you'll see with a function
call. There are also pipeline stalls and stack usage associated
with a function call that are eliminated by careful inlining.

Thanks for reinforcing my point.
 
I

Ian Collins

Le 21/05/12 20:24, Adam Skutt a écrit :

Most compilers complain when they detect a printf format error.
This is moot.

Does yours?

I agree that they (or the accompanying lint) should but it's a QoI issue
rather than a requirement.
Excuse me but Alf P Steinbach writes about C++ iostreams
(in this same thread):

<begin quote>
<end quote>

Bad implementations aren't only a problem with printf.

Agree here.
So what? You format them piecewise into a string, then you print
the string. Not a big deal.

For the 101st time, it is a show stopper if you are performing I/O in a
template.
??? Is an exception a better behavior?

Better than unexpected crashes when you don't check the return, certainly.
Well, the same holds for ANY feature of C++ since you will always find
some compiler that screws it up somewhere in a legacy platform.

Agree here.
 
I

Ian Collins

Writing a method to format a user-defined type (e.g. a toString() style method)
is just as viable as overloading the<< operator, and much more readable.

In languages without generic templates (such as JavaScript) that works
fine, but with templates you have to provide a mechanism that works with
both built in and user defined types.

This is at the heart of the C++ deign philosophy, one should be able to
use a user defined type as if it were built in type.
 
A

Adam Skutt

Which, given the density of the x86 instruction set, happens quite often.

Fine. Why should I believe that the instructions that would replace
the landing pad have more benefit?
With -fno-exceptions, the text is reduced by 1-2%.   For a large application,
this is  not insignificant.  For an operating system or hypervisor, this is very
significant.

If your only concern is cache, I'm not aware of any particular reason
why you couldn't put the exception handling code somewhere else
entirely, so it never hits the cache unless an exception actually
propagates. This is something that certainly could be supported, even
though I'm not 100% sure the current ABIs actually support it.
There's nothing about the technique, however, that prevents it.

Adam
 

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

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,111
Latest member
VetaMcRae
Top