Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Using printf in C++

267 views
Skip to first unread message

Seneika

unread,
May 11, 2012, 2:05:20 PM5/11/12
to
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.

Thanks,
Seneika.

Gareth Owen

unread,
May 11, 2012, 2:19:16 PM5/11/12
to
Seneika <not.s...@today.com> writes:

> So I ask: Is it wrong

Certainly not.

> or not-advisable to use printf (more precisely fprintf) in C++
> programs to do the I/O?

"Not-advisable" depends on who you ask for advice. Some people are
strong proponents of iostreams. If you use templates or try and write
generic code, you'll find iostreams make that much easier.

If the things you're printing are basic, fixed types - ints, doubles,
etc printf will work very well.

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

You absolutely can stick with printf. What you shouldn't do is mix the
two indiscriminately on the same stream,

e.g. Don't mix std::cerr<< and fprintf(stderr,...)

Seneika

unread,
May 11, 2012, 2:29:28 PM5/11/12
to
Thanks, G.O.

Scott Lurndal

unread,
May 11, 2012, 2:34:05 PM5/11/12
to
Seneika <not.s...@today.com> writes:
>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.

Use it and be happy. cout is useless in real applications.

scott

Marcel Müller

unread,
May 11, 2012, 2:55:35 PM5/11/12
to
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, especially in conjunction with
formatting.

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

printf is still part of the C++ runtime. But take care about it's
variants. While printf and fprintf is usually safe, sprintf is
error-prone. Using inconsistent format characters is not the primary
problem. Some compilers print warnings about that. But buffer overflows
are difficult to handle with sprintf. In general one should avoid char*
for strings in C++. (const char* in contrast is often fine.)

In fact I prefer printf syntax in almost all cases of logging and human
readable formatting. To be save I use a sprintf variant that safely
prints into a string class with matching storage, std::string or
whatever. vsnprintf, supported by many platforms makes this quite easy
to implement.

> So I ask: Is it wrong or not-advisable to use printf (more precisely
> fprintf) in C++ programs to do the I/O?

If it fits your needs: use it.

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

You won't get happy with the iostream library if you inter-operate with
files of Fortran number crunchers.


Marcel

Paavo Helde

unread,
May 11, 2012, 4:02:28 PM5/11/12
to
Gareth Owen <gwo...@gmail.com> wrote in news:87obpuy...@gmail.com:

> You absolutely can stick with printf. What you shouldn't do is mix the
> two indiscriminately on the same stream,
>
> e.g. Don't mix std::cerr<< and fprintf(stderr,...)

Why? Can't you do this without much problems if you call

std::ios::sync_with_stdio(true);

first?

p.

Paavo Helde

unread,
May 11, 2012, 4:07:48 PM5/11/12
to
Seneika <not.s...@today.com> wrote in news:4fad54e1$0$291$14726298
@news.sunsite.dk:
>
> 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.

Nothing wrong with fprintf. It would be wise though to check your code with
a compiler which warns you when you get the format strings wrong (e.g.
g++).

Cheers
Paavo

BGB

unread,
May 11, 2012, 5:03:33 PM5/11/12
to
On 5/11/2012 11:55 AM, Marcel Müller wrote:
> 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, especially in conjunction with
> formatting.
>

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 personally much prefer printf as well, but granted I have also written
much more C code than I have C++ code, which could be a factor.


>> 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.
>
> printf is still part of the C++ runtime. But take care about it's
> variants. While printf and fprintf is usually safe, sprintf is
> error-prone. Using inconsistent format characters is not the primary
> problem. Some compilers print warnings about that. But buffer overflows
> are difficult to handle with sprintf. In general one should avoid char*
> for strings in C++. (const char* in contrast is often fine.)
>

generally agreed.

although, I think the issue isn't so much about "char *" vs "const char
*" vs "std::string" or similar, but often the (generally unsafe)
practice of regarding strings as mutable buffers.

in my case, I tend to regard strings as immutable by default, but
usually the const keyword is not used given the compiler often taking
issue with const and non-const char-pointers being mixed (implicit
conversions, ...), so more often the "const" is implied. this could be
fixed (by switching to consistently using "const char *", or probably a
typedef thereof), but the codebase is large enough to where doing so
would be a hassle.


however, sadly, a lot of code in my case still uses a lot of temporary
char arrays as buffers as well, which albeit unsafe, there are few
"good" alternatives in the general case (at least in plain C land).

however, it is a convention in my case to (usually) make some effort to
avoid running past the end of the buffer (typically by comparing the
current target-pointer against a target-buffer-end pointer). this is at
least generally safer (if applied consistently), without some of the
potentially steep costs of some other options (higher implementation
complexity, poor performance, ...).

for, example rather than simply something like:
while(*s)*t++=*s++;
a person can instead write something like:
while(*s && t<te)*t++=*s++;
which is not particularly more complicated, or significantly slower, but
can have the advantage of not running past the end of the buffer (given
by 'te').


or, if the code in question is C++, a person can also just use std::string.


> In fact I prefer printf syntax in almost all cases of logging and human
> readable formatting. To be save I use a sprintf variant that safely
> prints into a string class with matching storage, std::string or
> whatever. vsnprintf, supported by many platforms makes this quite easy
> to implement.
>

yeah.

snprintf and vsnprintf are good idea, but have the drawback of being
absent on some compilers (including MSVC last I checked).

it is possible to work around this and some of the other issues by
implementing a custom version of the printing function, but this is
admittedly kind of a hassle (as is implementing all of the various
formatting options).


>> So I ask: Is it wrong or not-advisable to use printf (more precisely
>> fprintf) in C++ programs to do the I/O?
>
> If it fits your needs: use it.
>

yep.


I personally believe there is no real "right" or "wrong" in regards to
programming, rather it is more about costs and benefits, ...

one thing I ran across before was a convention known as "SWOT"
(Strengths, Weaknesses, Opportunities, Threats), which is IMO a
moderately more useful convention for classifying various development
options.

I often use this internally, but often end up expressing them more in
terms of a pros/cons convention (so: pros=strengths+opportunities, and
cons=weaknesses+threats), usually because this is easier to express
(don't need 4 categories).

so, the "good" option is that with more good points, and the "bad"
option is that with more bad points, rather than some sort of law etched
in stone somewhere ("all X much be done using Y" or similar).

similarly, anything which can be implemented is possible (even if
potentially counter-intuitive), because programming is about writing
things (and not merely invoking language features), ...


granted, I have gotten into pointless arguments with people about these
sorts of things. ( I wrote some about it, but decided to leave it out as
it was starting to look like a bit of a pointless rant, which will often
cause people to try to argue about it, which isn't really productive. )


>> 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.
>
> You won't get happy with the iostream library if you inter-operate with
> files of Fortran number crunchers.
>

yep, among other things...

Nobody

unread,
May 11, 2012, 5:48:21 PM5/11/12
to
On Fri, 11 May 2012 15:05:20 -0300, Seneika wrote:

> So I ask: Is it wrong or not-advisable to use printf (more precisely
> fprintf) in C++ programs to do the I/O?

The main downside is that fprintf() only works for a FILE*, while a C++
ostream can be an fstream, stringstream, an implementation-specific
extension or a user-defined class. You can build your own ostream on top
of a FILE* but you can't do the reverse without using platform-specific
extensions such as fopencookie() or funopen().

Another advantage of C++ streams is that their locale handling is more
flexible. Being able to imbue() a locale to a stream is preferable to
having to continually switch the global locale to avoid writing files with
decimal commas. And the C locale can't be sync'd to a C++ locale which
uses custom facets (or, for that matter, which sets individual facets
rather than categories).

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

Personally, I tend to define reader/writer classes to avoid spreading
implementation details all around the code. Changing the implementation
from <iostream> to <cstdio> or vice versa would be trivial.

Ian Collins

unread,
May 11, 2012, 5:49:00 PM5/11/12
to
You can, but why would you want to?

Mixing C and C++ streaming operations is just as much bad style as bad
practice.

--
Ian Collins

Ian Collins

unread,
May 11, 2012, 5:53:35 PM5/11/12
to
On 05/12/12 06:34 AM, Scott Lurndal wrote:
>
> cout is useless in real applications.

That's nonsense.

How would you use printf reliably in templates? Or aren't templates
part of "real applications"?

How would you use printf with user defined types (even something a
simple as std::complex)? Or aren't user defined types part of "real
applications"?

--
Ian Collins

BGB

unread,
May 11, 2012, 8:54:57 PM5/11/12
to
I don't actually agree with the quoted statement either, so this is not
a defense of his position.


but, it is actually possible to write an intermediate function to
convert the data into a string, which may be in turn printed with printf
or similar (this is actually fairly common in many C applications).

so, something like:
printf("%s\n", FooType_toString(obj));

or, in my case:
printf("%s\n", dyToString(obj));

where dyToString uses a custom dynamic typesystem (obj needs to be a
dynamically-typed object handle), and looks up the vtable for the type,
and invokes its toString method (if available, otherwise it uses a
"default" handler, which spits out something like "#<typename:address>").


so, the apparent assertion that printf can't be used to print
user-defined types isn't really correct either.

Ian Collins

unread,
May 12, 2012, 1:28:13 AM5/12/12
to
On 05/12/12 12:54 PM, BGB wrote:
> On 5/11/2012 2:53 PM, Ian Collins wrote:
>> On 05/12/12 06:34 AM, Scott Lurndal wrote:
>>>
>>> cout is useless in real applications.
>>
>> That's nonsense.
>>
>> How would you use printf reliably in templates? Or aren't templates part
>> of "real applications"?
>>
>> How would you use printf with user defined types (even something a
>> simple as std::complex)? Or aren't user defined types part of "real
>> applications"?
>>
>
> I don't actually agree with the quoted statement either, so this is not
> a defense of his position.
>
> but, it is actually possible to write an intermediate function to
> convert the data into a string, which may be in turn printed with printf
> or similar (this is actually fairly common in many C applications).
>
> so, something like:
> printf("%s\n", FooType_toString(obj));
>
> or, in my case:
> printf("%s\n", dyToString(obj));

But why would you want to?

> so, the apparent assertion that printf can't be used to print
> user-defined types isn't really correct either.

I didn't intend to claim that it can't, merely that it's cumbersome. I
think you have just proved my point!

--
Ian Collins

BGB

unread,
May 12, 2012, 1:36:35 AM5/12/12
to
On 5/11/2012 10:28 PM, Ian Collins wrote:
> On 05/12/12 12:54 PM, BGB wrote:
>> On 5/11/2012 2:53 PM, Ian Collins wrote:
>>> On 05/12/12 06:34 AM, Scott Lurndal wrote:
>>>>
>>>> cout is useless in real applications.
>>>
>>> That's nonsense.
>>>
>>> How would you use printf reliably in templates? Or aren't templates part
>>> of "real applications"?
>>>
>>> How would you use printf with user defined types (even something a
>>> simple as std::complex)? Or aren't user defined types part of "real
>>> applications"?
>>>
>>
>> I don't actually agree with the quoted statement either, so this is not
>> a defense of his position.
>>
>> but, it is actually possible to write an intermediate function to
>> convert the data into a string, which may be in turn printed with printf
>> or similar (this is actually fairly common in many C applications).
>>
>> so, something like:
>> printf("%s\n", FooType_toString(obj));
>>
>> or, in my case:
>> printf("%s\n", dyToString(obj));
>
> But why would you want to?
>

because this strategy also works in C, and doesn't require implementing
an alternative printing interface.


>> so, the apparent assertion that printf can't be used to print
>> user-defined types isn't really correct either.
>
> I didn't intend to claim that it can't, merely that it's cumbersome. I
> think you have just proved my point!
>

it works, and there are plenty more cumbersome things a person could be
doing.

Jorgen Grahn

unread,
May 12, 2012, 3:58:15 AM5/12/12
to
On Fri, 2012-05-11, Marcel Müller wrote:
> 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));

> especially in conjunction with formatting.

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

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

nick_keigh...@hotmail.com

unread,
May 12, 2012, 5:14:34 AM5/12/12
to
iomanip always looks pretty cumbersome to me

nick_keigh...@hotmail.com

unread,
May 12, 2012, 5:12:26 AM5/12/12
to
I've printed latitudes and longitudes and havine an operator<< makes life a lot easier

Jeff Flinn

unread,
May 12, 2012, 10:39:42 AM5/12/12
to
Oh, let's say I'm using several 3rd party libraries that I've no control
over.

Jeff

Paavo Helde

unread,
May 12, 2012, 2:32:06 PM5/12/12
to
Ian Collins <ian-...@hotmail.com> wrote in news:a15fqcF58bU2
@mid.individual.net:

> On 05/12/12 08:02 AM, Paavo Helde wrote:
>> Gareth Owen<gwo...@gmail.com> wrote in news:87obpuy...@gmail.com:
>>
>>> You absolutely can stick with printf. What you shouldn't do is mix
the
>>> two indiscriminately on the same stream,
>>>
>>> e.g. Don't mix std::cerr<< and fprintf(stderr,...)
>>
>> Why? Can't you do this without much problems if you call
>>
>> std::ios::sync_with_stdio(true);
>>
>> first?
>
> You can, but why would you want to?

Laziness? Corporate rules which prohibit making any changes to the code
which have no benefit to the end user?

>
> Mixing C and C++ streaming operations is just as much bad style as bad
> practice.

Agreed it's bad style.

Cheers
Paavo

Scott Lurndal

unread,
May 12, 2012, 2:50:37 PM5/12/12
to
Ian Collins <ian-...@hotmail.com> writes:
>On 05/12/12 06:34 AM, Scott Lurndal wrote:
>>
>> cout is useless in real applications.
>
>That's nonsense.
>
>How would you use printf reliably in templates? Or aren't templates
>part of "real applications"?

Actually, I'd use snprintf, and return string objects.

(and many real applications do avoid templates, for whatever reasons,
like code footprint, for example).

scott

Rui Maciel

unread,
May 12, 2012, 4:58:14 PM5/12/12
to
Scott Lurndal wrote:

> Use it and be happy. cout is useless in real applications.

Can you provide an example where cout is useless and printf represents a
better option?


Rui Maciel

Ian Collins

unread,
May 12, 2012, 6:09:11 PM5/12/12
to
On 05/13/12 06:50 AM, Scott Lurndal wrote:
> Ian Collins<ian-...@hotmail.com> writes:
>> On 05/12/12 06:34 AM, Scott Lurndal wrote:
>>>
>>> cout is useless in real applications.
>>
>> That's nonsense.
>>
>> How would you use printf reliably in templates? Or aren't templates
>> part of "real applications"?
>
> Actually, I'd use snprintf, and return string objects.

In a template? How?

> (and many real applications do avoid templates, for whatever reasons,
> like code footprint, for example).

Code footprint is only an issue if you don't know what you are doing.

--
Ian Collins

Ian Collins

unread,
May 12, 2012, 6:15:46 PM5/12/12
to
On 05/12/12 05:36 PM, BGB wrote:
> On 5/11/2012 10:28 PM, Ian Collins wrote:
>> On 05/12/12 12:54 PM, BGB wrote:
>>> On 5/11/2012 2:53 PM, Ian Collins wrote:
>>>> On 05/12/12 06:34 AM, Scott Lurndal wrote:
>>>>>
>>>>> cout is useless in real applications.
>>>>
>>>> That's nonsense.
>>>>
>>>> How would you use printf reliably in templates? Or aren't templates part
>>>> of "real applications"?
>>>>
>>>> How would you use printf with user defined types (even something a
>>>> simple as std::complex)? Or aren't user defined types part of "real
>>>> applications"?
>>>>
>>>
>>> I don't actually agree with the quoted statement either, so this is not
>>> a defense of his position.
>>>
>>> but, it is actually possible to write an intermediate function to
>>> convert the data into a string, which may be in turn printed with printf
>>> or similar (this is actually fairly common in many C applications).
>>>
>>> so, something like:
>>> printf("%s\n", FooType_toString(obj));
>>>
>>> or, in my case:
>>> printf("%s\n", dyToString(obj));
>>
>> But why would you want to?
>>
>
> because this strategy also works in C, and doesn't require implementing
> an alternative printing interface.

But this strategy breaks in templates. Consider a specialised numeric
type: with streaming operators it behaves like a built in type (which is
one of the original design goals of C++), with your strategy it does not.

--
Ian Collins

Dombo

unread,
May 12, 2012, 7:08:51 PM5/12/12
to
Op 12-May-12 22:58, Rui Maciel schreef:
IMO the big advantage of printf over std::cout is formatting, with
std::cout formatting is so clumsy and messy that I consider it to be
somewhere between impractical and useless.

For just about every other aspect I prefer std::cout (or streams), but
mostly because of the type safety and extendability this mechanism
provides. The boost format library seams to be a nice compromise, though
I don't have first hand experience with it.

BGB

unread,
May 12, 2012, 10:47:59 PM5/12/12
to
admittedly, thus far I have rarely used templates much either.

most of what I had done in C++ had been making use of operator
overloading and making use of classes.

Luca Risolia

unread,
May 12, 2012, 10:53:05 PM5/12/12
to
On 11/05/2012 20:55, Marcel Müller wrote:
> 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, especially in conjunction with
> formatting.

The inserter "<<" and the extractor ">>" had been deliberately chosen
for a number of reasons:

1. it was not possible to invent new lexical elements
2. other operators like "=" or ">" and "<" were considered inappropriate
for I/O. In particular, the former has a bad order of operator
association i.e. cout=a=b means cout=(a=b) instead of (cout=a)=b.
3. many preferred two distinct operators for insertion and extraction,
and ">>" and "<<" are symmetric symbols as the operations they represent.
4. operator >> and operator << have a priority low enough to write
expressions such as cout << a+b*c << '\n' without the need of parenthesis.
5. they are rarely used with base types

That said, in my opinion they do make the code more readable and
compact, above all in presence of both standard and user-defined
manipulators, For example:

cout << "My secret is: " << decrypt(pswd) << my_secret << clear << '\n';

Obviously, there is no way to do that with printf() in a single line.

Juha Nieminen

unread,
May 13, 2012, 3:11:28 AM5/13/12
to
Scott Lurndal <sc...@slp53.sl.home> wrote:
> (and many real applications do avoid templates, for whatever reasons,
> like code footprint, for example).

"Real application" and "application using idiotic design principles"
are not mutually exclusive things...

Juha Nieminen

unread,
May 13, 2012, 3:20:03 AM5/13/12
to
A format string is much handier in situations like when having all the
text of the program in separate localization files (ie. files that
specify all the text used in the program for different languages).

A parameter specifier (in other words, eg. "%2$i") is not standard,
but most systems support it. It can become especially handy in such a
situation.

You could devise your own format string and use some programmatical
searh-and-replace of parameters, but the printf format is already supported,
so you don't have to do anything additional.

Also, if extreme writing speed is required, the C I/O streams tend to be
faster than the C++ ones.

Juha Nieminen

unread,
May 13, 2012, 3:28:54 AM5/13/12
to
Marcel Müller <news.5...@spamgourmet.org> wrote:
> The fumbling with the shift operators
> primarily makes the code unreadable, especially in conjunction with
> formatting.

There's a persistent claim that people keep repeating over and over
like parrots about the usage of the bit shift operators with streams
being confusing.

I have been programming in C++ both as a hobby and professionally for
the past 15 years, and I have yet to encounter and actual case where it
wasn't 100% clear which type of operator was being used.

(It's extremely difficult to even artifically create code that makes
sense and could equally well be interpreted as bit-shifting or using
C++ streams. "a << b" is a no-op if done with integrals so it alone
doesn't make sense. Conversely, streams are not assignable. Also, the
code would have to hide the actual type of those variables. This means
that the code would have to be eg. templated, and named in such a
deliberately confusing way as to not make clear what its functionality is.
In principle you could do something like "if(a << b)" and it perhaps could
be sensible as both types of operation, but then you would have to quite
artificially and carefully craft the surrounding code to also be sensible
for both situations. In short, it's actually very difficult to even
deliberately make code so obfuscated as to make it unclear which operator
is being used.)

Marcel Müller

unread,
May 13, 2012, 9:39:18 AM5/13/12
to
On 13.05.12 09.28, Juha Nieminen wrote:
> Marcel Müller<news.5...@spamgourmet.org> wrote:
>> The fumbling with the shift operators
>> primarily makes the code unreadable, especially in conjunction with
>> formatting.
>
> There's a persistent claim that people keep repeating over and over
> like parrots about the usage of the bit shift operators with streams
> being confusing.

The major drawback are not the shift operators. It is the lack of an
output template string. To intercept the output sting with often long
C++ expressions makes things unreadable. And additional format
specifiers do that even more. The usually very compact printf syntax is
simply more clear, once you know it.
This is the reason why I dislike iostreams and use them rarely.

boost::format tries to combine the advantages of template based
formatting and type safety. But I did not yet get familiar with it so
far. Simply because I did not need to do so.


Marcel

BGB

unread,
May 13, 2012, 10:55:44 AM5/13/12
to
On 5/13/2012 12:28 AM, Juha Nieminen wrote:
> Marcel Müller<news.5...@spamgourmet.org> wrote:
>> The fumbling with the shift operators
>> primarily makes the code unreadable, especially in conjunction with
>> formatting.
>
> There's a persistent claim that people keep repeating over and over
> like parrots about the usage of the bit shift operators with streams
> being confusing.
>
> I have been programming in C++ both as a hobby and professionally for
> the past 15 years, and I have yet to encounter and actual case where it
> wasn't 100% clear which type of operator was being used.
>

I personally don't think it is so much a matter of being confusing, so
much as "kind of ugly looking" and "doesn't particularly have convenient
fixed-formatting options".

for example, it is nice to be able to easily specify how many characters
a string or number will take up such that everything is lined up nicely
in the output, ...


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

maybe also a less brain-damaged "sscanf" variant would also be nice
(IOW, one capable of dealing with CSV and other data, with the kind of
character following the type indicating what is to be regarded as the
terminator, and maybe with some mechanism to specify regexes, ...).


> (It's extremely difficult to even artifically create code that makes
> sense and could equally well be interpreted as bit-shifting or using
> C++ streams. "a<< b" is a no-op if done with integrals so it alone
> doesn't make sense. Conversely, streams are not assignable. Also, the
> code would have to hide the actual type of those variables. This means
> that the code would have to be eg. templated, and named in such a
> deliberately confusing way as to not make clear what its functionality is.
> In principle you could do something like "if(a<< b)" and it perhaps could
> be sensible as both types of operation, but then you would have to quite
> artificially and carefully craft the surrounding code to also be sensible
> for both situations. In short, it's actually very difficult to even
> deliberately make code so obfuscated as to make it unclear which operator
> is being used.)

not much to say on this, not seeing "confusion as the main issue".

granted, there aren't all that many other good options, given C and C++
both have relatively weak varargs (which don't themselves preserve type).

yes, variadic templates may allow better options, assuming timely
adoption of C++11.


Java originally used '+' for everything, but later also added a
"printf()" style interface ("System.out.printf" and similar).

C# was more like "time for a pile of overloaded methods"
(Console.WriteLine, ...).


for my own language currently I mostly just adopted printf (along with
"println" and similar), but the language allows more options than C
would (such as a type for allowing any arguments to be automatically fed
through "toString", ...).

I am also "on a fence" regarding adopting the rest of stdio as the file
API (but likely using a different VFS internally, vaguely sort of like
in Cygwin). there are various possible options and tradeoffs.

Scott Lurndal

unread,
May 13, 2012, 1:25:10 PM5/13/12
to
if (p_num_breakpoints > 0) {
lp->log("Breakpoint Address Core Enabled?\n");
lp->log("---------- ---------------- ---- --------\n");

for(size_t b = 0; b < p_num_breakpoints; b++) {
const s_breakpoint *bpp = &p_breakpoints[b];
char core[16];

if (bpp->b_core == -1) {
snprintf(core, sizeof(core), "%s", "All");
} else {
snprintf(core, sizeof(core), "%2.2d", bpp->b_core);
}
lp->log(" %2.2zu %016llx %3.3s %s\n",
b, bpp->b_address, core, bpp->b_enabled?"Yes":"No");
}
}

Luca Risolia

unread,
May 13, 2012, 2:33:38 PM5/13/12
to
On 13/05/2012 19:25, Scott Lurndal wrote:
> Rui Maciel<rui.m...@gmail.com> writes:
>> Scott Lurndal wrote:
>>
>>> Use it and be happy. cout is useless in real applications.
>>
>> Can you provide an example where cout is useless and printf represents a
>> better option?
>>
>>
>
> if (p_num_breakpoints> 0) {
> lp->log("Breakpoint Address Core Enabled?\n");
> lp->log("---------- ---------------- ---- --------\n");
>
> for(size_t b = 0; b< p_num_breakpoints; b++) {
> const s_breakpoint *bpp =&p_breakpoints[b];
> char core[16];
>
> if (bpp->b_core == -1) {
> snprintf(core, sizeof(core), "%s", "All");
> } else {
> snprintf(core, sizeof(core), "%2.2d", bpp->b_core);
> }
> lp->log(" %2.2zu %016llx %3.3s %s\n",
> b, bpp->b_address, core, bpp->b_enabled?"Yes":"No");
> }
> }

It's really ugly C++ code. All that stuff can be written in a more
readable, type-safe code by using std::clog (or whatever stream you
need) and standard manipulators.

jacob navia

unread,
May 13, 2012, 5:39:17 PM5/13/12
to
Le 13/05/12 20:33, Luca Risolia a écrit :
> All that stuff can be written in a more readable, type-safe code by
> using std::clog (or whatever stream you need) and standard manipulators.

Great!

Go ahead, and show us how.


Martin Shobe

unread,
May 13, 2012, 7:21:27 PM5/13/12
to
std::ostream & operator<<(std::ostream & os, s_breakpoint const & bpp)
{
os << setw(16);

if (bpp.b_core == -1)
{
os << "All";
}
else
{
os << setprecision(2) << bpp.b_core;
}

os << " " << setfill('0') << hex << bpp.b_address;
os << " " << bpp.b_enabled ? "Yes" : " No";

return os;
}

if (p_num_breakpoints > 0)
{
lp->log("Breakpoint Address Core Enabled?\n");
lp->log("---------- ---------------- ---- --------\n");

for (size_t b = 0; b < p_num_breakpoints; b++)
{
std::stringstream core;
core << setw(5) << setprecision(2) << b;
core << " " << p_breakpoints[b] << "\n";

lp->log(core.str().c_str());
}
}

I don't particularly like having to use all those manipulators, but I
wouldn't call it useless. (and of course it would look a little better
if either lp or lp->log was a stream and we could get rid of the
std::stringstream).

Martin Shobe

Luca Risolia

unread,
May 13, 2012, 8:41:14 PM5/13/12
to
if (p_num_breakpoints > 0) {
clog << "Breakpoint Address Core Enabled?\n"
"---------- ---------------- ---- --------\n";

for (size_t b = 0; b < p_num_breakpoints; b++) {
const s_breakpoint *bpp = &p_breakpoints[b];
ostringstream core;
bpp->b_core == -1 ? core << "All" : core << setw(3) << bpp->b_core;
clog << setfill(' ') << setw(3) << ' ' // indent
<< setfill('0') << setw(2) << b
<< setfill(' ') << setw(8) << ' '
<< setfill('0') << setw(16)
<< hex << reinterpret_cast<unsigned long>(bpp->b_address)
<< setfill(' ') << setw(2) << ' '
<< core.str().substr(0, 3) << ' '
<< setfill(' ') << setw(3) << ' '
<< boolalpha << static_cast<bool>(bpp->b_enabled)
<< '\n';
}
}

..which produces the same output as the original version with printf():

Breakpoint Address Core Enabled?
---------- ---------------- ---- --------
00 00007fff7f886a0c All false
01 00007fff7f886a0c 12 false
02 00007fff7f886a0c 123 true


With manipulators it's evident how you want to format the output. You
can pack setfill() and setw() together in an inline, user-defined
manipulator and make code even more compact and readable.

Notes: yes, reinterpret_cast<> is allowed. you may want to use boolalpha
instead of those "Yes" "No", since it comes for free.

Martin Shobe

unread,
May 13, 2012, 8:49:52 PM5/13/12
to
I thought the output was supposed to look like this. (Obviously it
wouldn't take much to change it to this).

Breakpoint Address Core Enabled?
---------- ---------------- ---- --------
0 00007fff7f886a0c All No
1 00007fff7f886a0c 12 No
2 00007fff7f886a0c 123 Yes

[snip]

Martin Shobe

Luca Risolia

unread,
May 13, 2012, 9:07:15 PM5/13/12
to
On 14/05/2012 02:49, Martin Shobe wrote:
> I thought the output was supposed to look like this. (Obviously it
> wouldn't take much to change it to this).
>
> Breakpoint Address Core Enabled?
> ---------- ---------------- ---- --------
> 0 00007fff7f886a0c All No
> 1 00007fff7f886a0c 12 No
> 2 00007fff7f886a0c 123 Yes

as I said, with regard to the "Enabled" field I have deliberately used
the boolalpha manipulator to show how to print flags as a standard
alternative.

The change to print "Yes" and "No" is:

clog << [...]
<< (bpp->b_enabled ? "Yes" : "No") // instead of boolalpha

I also confirm that the "Breakpoint" field has to look with one initial
'0' with one-digit numbers.

Ian Collins

unread,
May 13, 2012, 9:31:01 PM5/13/12
to
On 05/14/12 05:25 AM, Scott Lurndal wrote:
> Rui Maciel<rui.m...@gmail.com> writes:
>> Scott Lurndal wrote:
>>
>>> Use it and be happy. cout is useless in real applications.
>>
>> Can you provide an example where cout is useless and printf represents a
>> better option?
>>
>>
>
> if (p_num_breakpoints> 0) {
> lp->log("Breakpoint Address Core Enabled?\n");
> lp->log("---------- ---------------- ---- --------\n");
>
> for(size_t b = 0; b< p_num_breakpoints; b++) {
> const s_breakpoint *bpp =&p_breakpoints[b];
> char core[16];
>
> if (bpp->b_core == -1) {
> snprintf(core, sizeof(core), "%s", "All");
> } else {
> snprintf(core, sizeof(core), "%2.2d", bpp->b_core);
> }
> lp->log(" %2.2zu %016llx %3.3s %s\n",
> b, bpp->b_address, core, bpp->b_enabled?"Yes":"No");
> }
> }

Is %016llx a valid format string?

--
Ian Collins

Martin Shobe

unread,
May 13, 2012, 9:50:29 PM5/13/12
to
Yes, you are correct.

Martin Shobe

BGB

unread,
May 13, 2012, 10:00:37 PM5/13/12
to
in C99 or newer, yes.

basically in means 0-padded, 16 digits, long-long, and hex (lower case).

so, if you print a constant like 12841684688557LL

the result is:
00000badf00ddead

Ian Collins

unread,
May 13, 2012, 10:16:10 PM5/13/12
to
Um, it doesn't appear to work as advertised:

int main()
{
printf( "%016llx %d\n", &main, 42 );
}

gives

0000002a08050a10 134511408

where

printf( "%016x %d\n", &main, 42 );

gives

0000000008050a10 42

for 32 bit builds on my platform (Solaris). 64 bit builds are OK.

--
Ian Collins

Ian Collins

unread,
May 13, 2012, 10:18:49 PM5/13/12
to
On 05/14/12 02:16 PM, Ian Collins wrote:
> On 05/14/12 02:00 PM, BGB wrote:
>> On 5/13/2012 6:31 PM, Ian Collins wrote:
>>>> }
>>>
>>> Is %016llx a valid format string?
>>>
>>
>> in C99 or newer, yes.
>>
>> basically in means 0-padded, 16 digits, long-long, and hex (lower case).
>>
>> so, if you print a constant like 12841684688557LL
>>
>> the result is:
>> 00000badf00ddead
>>
> Um, it doesn't appear to work as advertised:
>
> int main()
> {
> printf( "%016llx %d\n",&main, 42 );
> }
>
> gives
>
> 0000002a08050a10 134511408
>
> where
>
> printf( "%016x %d\n",&main, 42 );
>
> gives
>
> 0000000008050a10 42
>
> for 32 bit builds on my platform (Solaris). 64 bit builds are OK.

Which isn't surprising when an address is 32 bits and long long is 64.
Type safe output anyone?

:)

--
Ian Collins

Nobody

unread,
May 13, 2012, 11:20:55 PM5/13/12
to
On Sun, 13 May 2012 07:55:44 -0700, BGB wrote:

> for example, it is nice to be able to easily specify how many characters
> a string or number will take up such that everything is lined up nicely
> in the output, ...

If you want that capability, you need to use the *wprintf functions. The
non-wide versions measure the field width in "char"s (even for e.g.
"%ls"), which isn't of much use with a multi-byte encoding.

> maybe also a less brain-damaged "sscanf" variant would also be nice

If you get to decide the data format, *scanf is sufficient. If you don't
get to decide the data format, anything short of the capabilities provided
by e.g. lex+yacc is just tinkering with the boundary between "sufficient"
and "if only they had added just this one extra feature".


88888 Dihedral

unread,
May 14, 2012, 3:12:10 AM5/14/12
to
Nobody於 2012年5月14日星期一UTC+8上午11時20分55秒寫道:
Don't forget uni-code support in any descent high level languages.

BGB

unread,
May 14, 2012, 9:48:29 AM5/14/12
to
this is because a pointer is not a long-long.
on 32-bit targets, it is generally a 32 bit type.

otherwise, it would be necessary to cast to a 64 bit integer type,
probably as a 2-step cast as the compiler may complain about a
single-step cast from a pointer to an integer of a different size.

BGB

unread,
May 14, 2012, 12:04:28 PM5/14/12
to
On 5/13/2012 8:20 PM, Nobody wrote:
> On Sun, 13 May 2012 07:55:44 -0700, BGB wrote:
>
>> for example, it is nice to be able to easily specify how many characters
>> a string or number will take up such that everything is lined up nicely
>> in the output, ...
>
> If you want that capability, you need to use the *wprintf functions. The
> non-wide versions measure the field width in "char"s (even for e.g.
> "%ls"), which isn't of much use with a multi-byte encoding.
>

dunno.

probably the majority of cases where this sort of formatted output is
being done are ASCII-only anyways.

otherwise, there could be a use for, say, a version which uses UTF-8 (I
primarily use UTF-8 rather than UTF-16 or similar, because UTF-8 tends
to be more compact).


>> maybe also a less brain-damaged "sscanf" variant would also be nice
>
> If you get to decide the data format, *scanf is sufficient. If you don't
> get to decide the data format, anything short of the capabilities provided
> by e.g. lex+yacc is just tinkering with the boundary between "sufficient"
> and "if only they had added just this one extra feature".
>

it is not "one or the other" IMO, and personally I haven't used lex+yacc
either (as they offer little real obvious advantages over a hand-written
tokenizer and recursive descent parser, but come with a number of
drawbacks).


in the simple case, there are many forms of data which are seperated by
trivial, non-space deliminators.

CSV is a major example, where ',' is the main seperator.
some other formats use ':' or ';' for a similar role.

a sscanf-like function could easily deal with this case, if it did not
assume whitespace was the separator.


regexes or PEG parsers could be used in many more advanced cases.

Scott Lurndal

unread,
May 14, 2012, 12:54:39 PM5/14/12
to
Yes. It is posix compliant and C99 compliant. b_address is typed as 'unsigned long long'.

scott

Martin B.

unread,
May 14, 2012, 2:01:15 PM5/14/12
to
On 14.05.2012 18:51, Scott Lurndal wrote:
> Luca Risolia<luca.r...@studio.unibo.it> writes:
>> On 13/05/2012 23:39, jacob navia wrote:
>>> Le 13/05/12 20:33, Luca Risolia a écrit :
>>>> All that stuff (...)>
> All the C++ I write is in operating systems, hypervisors and other bare-metal
> code for which performance is priority #1, and where we don't have the standard
> C++ runtime (such as it is) available. No rtti, no exceptions, no templates
> and very few reference objects.
>
> I suppose you'd call it C with classes, rather than C++. I'm happy with that.
>

Just wondering ... I can understand not using RTTI and disabling
exceptions for performance reasons. (And with disabled exceptions, it
obviously follows that much stuff in the standard library doesn't work
properly.)

But no templates? Why? Templates as such certainly don't add anything to
runtime performance and are a tremendously powerful tool.

cheers,
Martin

Scott Lurndal

unread,
May 14, 2012, 2:30:10 PM5/14/12
to
Code footprint in the icache (and memory) is the primary reason for no
templates. This dates back to the first implementation of templates in Cfront 3.0
which, if templates were specialized for more than one or two types, would bloat
the codebase tremendously (where the OS and all apps were running in 4MB DRAM,
space is important). icache footprint is still pretty scarce, every bit that the
OS uses evicts something the applications need more. Modern templates still cause
code bloat, even if the programmer never sees it (and modern 4+GB DRAM setups
insulate this from most programmers).

In the OS, templates would be of very limited use, since the range of data structures
used in an OS is generally limited to tables and linked lists. If you're willing to use
casts, a good double linked list can be implemented as a base class using void* pointers
which can then be used stand-alone or derived from as necessary. Sure, you may give up
some type safety, but you gain pretty much everwhere else (footprint and performance). Likewise,
iterators can be implemented without templates too (as was done in the C++ 2.1 days).

for example:

c_dlist_iterator di(&n_send_queue);
while (di.next()) {
s_msg *mp = (s_msg *)di.curr(); // cast to list entry type.

mp->remove();
send_message(mp, false, NULL);
}

I've also done this by overloading 'operator ++' instead of the 'next()' method,
but for folks who live in both the C and C++ worlds, next() is more readable (IMO).

scott

Luca Risolia

unread,
May 14, 2012, 3:19:43 PM5/14/12
to
On 14/05/2012 18:51, Scott Lurndal wrote:

> More readable is certainly a matter of opinion, in this case.
>
> if (bpp->b_core == -1) {
> snprintf(core, sizeof(core), "%s", "All");
> } else {
> snprintf(core, sizeof(core), "%2.2d", bpp->b_core);
> }
> lp->log(" %2.2zu %016llx %3.3s %s\n",
> b, bpp->b_address, core, bpp->b_enabled?"Yes":"No");
>
> This is much more readable, and much more important to me, it will perform
> much better than the C++ stuff you've written above (two function calls
> versus many function calls, smaller cache footprint vs. larger cache footprint).

You should prove that your code will "perform much better". If I have to
bet, I'd say your code actually performs worse. Manipulators can be
easily optimized out by any modern compiler (see later for an example).
Also, clog is buffered, so it is faster than cout and printf() probably.

> All the C++ I write is in operating systems, hypervisors and other bare-metal
> code for which performance is priority #1, and where we don't have the standard
> C++ runtime (such as it is) available. No rtti, no exceptions, no templates
> and very few reference objects.

Templates themselves have no impact at run-time. Also, C++ streams were
designed with performance in mind and exceptions are deliberately
disabled by default. Note that I am not saying that printf() is actually
slower than clog.

Anyway, in your case it seems you just want to print out a simple table
to the user (from "%2.2zu" it seems the table is supposed have less than
100 entries), so for these simple cases I suggest you paying attention
to writing readable code rather than trying to optimize where the
benefits will not be noticeable.

To interpret your formatting I had to take a C manual (and a coffee..):

lp->log(" %2.2zu %016llx %3.3s %s\n"...

Not to mention the fact that I had to count the number of white spaces
by moving the cursor from right to left.

With pure C++ style you will not have an headache at least. In the
example below I have used a "custom" manipulator to make the code even
shorter than I wrote in my previous post. As an exercise, you may want
to try to implement a whitespace() manipulator to further improve it;

clog << pad(' ', 3) << ' '
<< pad('0', 2) << b
<< pad(' ', 8) << ' '
<< pad('0', 16)
<< hex << reinterpret_cast<unsigned long> (bpp->b_address)
<< pad(' ', 2) << ' '
<< core.str().substr(0, 3) << ' '
<< pad(' ', 3) << ' '
<< (bpp->b_enabled ? "Yes" : "No")
<< '\n';

The pad manipulator is so simple that can be easily expanded in-line by
any compiler, with no performance loss:

// Place this in a library for re-use
struct smanip {
ostream& (*f)(ostream&, char, int);
char c;
int i;
smanip(ostream& (*ff)(ostream&, char, int), char cc, int ii)
: f(ff), c(cc), i(ii) { }
};

ostream& operator<<(ostream& os, const smanip& m) {
return m.f(os, m.c, m.i);
}

ostream& set_pad(ostream& s, char c, int n) {
s.fill(c);
s.width(n);
return s;
}

inline smanip pad(char c, int n) {
return smanip(set_pad, c, n);
}

Ian Collins

unread,
May 14, 2012, 3:28:49 PM5/14/12
to
And you can see what happens if it isn't the same size as uint64_t!

Thank you for providing an example of why not to use printf.

--
Ian Collins

Ian Collins

unread,
May 14, 2012, 3:34:06 PM5/14/12
to
On 05/15/12 04:51 AM, Scott Lurndal wrote:
> Luca Risolia<luca.r...@studio.unibo.it> writes:
>> On 13/05/2012 23:39, jacob navia wrote:
>>> Le 13/05/12 20:33, Luca Risolia a écrit :
>>>> All that stuff can be written in a more readable, type-safe code by
>>>> using std::clog (or whatever stream you need) and standard manipulators.
>>>
>>> Great!
>>>
>>> Go ahead, and show us how.
>>
>> if (p_num_breakpoints> 0) {
>> clog<< "Breakpoint Address Core Enabled?\n"
>> "---------- ---------------- ---- --------\n";
>>
>> for (size_t b = 0; b< p_num_breakpoints; b++) {
>> const s_breakpoint *bpp =&p_breakpoints[b];
>> ostringstream core;
>> bpp->b_core == -1 ? core<< "All" : core<< setw(3)<< bpp->b_core;
>> clog<< setfill(' ')<< setw(3)<< ' ' // indent
>> << setfill('0')<< setw(2)<< b
>> << setfill(' ')<< setw(8)<< ''
>> << setfill('0')<< setw(16)
>> << hex<< reinterpret_cast<unsigned long>(bpp->b_address)
>> << setfill(' ')<< setw(2)<< ''
>> << core.str().substr(0, 3)<< ''
>> << setfill(' ')<< setw(3)<< ''
>> << boolalpha<< static_cast<bool>(bpp->b_enabled)
>> << '\n';
>> }
>> }
>
> More readable is certainly a matter of opinion, in this case.
>
> if (bpp->b_core == -1) {
> snprintf(core, sizeof(core), "%s", "All");
> } else {
> snprintf(core, sizeof(core), "%2.2d", bpp->b_core);
> }
> lp->log(" %2.2zu %016llx %3.3s %s\n",
> b, bpp->b_address, core, bpp->b_enabled?"Yes":"No");
>
> This is much more readable, and much more important to me, it will perform
> much better than the C++ stuff you've written above (two function calls
> versus many function calls, smaller cache footprint vs. larger cache footprint).

It will also fail in amusing ways in a 32 bit build.

> All the C++ I write is in operating systems, hypervisors and other bare-metal
> code for which performance is priority #1, and where we don't have the standard
> C++ runtime (such as it is) available. No rtti, no exceptions, no templates
> and very few reference objects.

Not that old FUD again. The only feature there that may impact
performance is RTTI. If anything, the reset will improve performance on
any decent modern compiler. Please, move on from the 90s.


--
Ian Collins

Jorgen Grahn

unread,
May 14, 2012, 4:44:54 PM5/14/12
to
On Mon, 2012-05-14, Ian Collins wrote:
> On 05/15/12 04:54 AM, Scott Lurndal wrote:
>> Ian Collins<ian-...@hotmail.com> writes:
>>> On 05/14/12 05:25 AM, Scott Lurndal wrote:
>>>> Rui Maciel<rui.m...@gmail.com> writes:
>>>>> Scott Lurndal wrote:
>>>>>
>>>>>> Use it and be happy. cout is useless in real applications.
>>>>>
>>>>> Can you provide an example where cout is useless and printf represents a
>>>>> better option?

...
>>>> lp->log(" %2.2zu %016llx %3.3s %s\n",
>>>> b, bpp->b_address, core, bpp->b_enabled?"Yes":"No");
>>>> }
>>>> }
>>>
>>> Is %016llx a valid format string?
>>
>> Yes. It is posix compliant and C99 compliant.
>> b_address is typed as 'unsigned long long'.
>
> And you can see what happens if it isn't the same size as uint64_t!
>
> Thank you for providing an example of why not to use printf.

To be fair, decent compilers warn about printf type mismatch problems,
and can be told to do the same to lp->log().

I use printf and friends in C and C++, but when that warning level is
(for some reason) unavailable, I am very, very careful.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Scott Lurndal

unread,
May 14, 2012, 4:53:44 PM5/14/12
to
No, it will not. The entire 60,000 line project builds and runs on both 64-bit
and 32-bit systems (hint: unsigned long long has the same size on both).

(for your edification, re: the &main example you posted, use the '%p' format specifier
for pointers, not %llx).

>
>> All the C++ I write is in operating systems, hypervisors and other bare-metal
>> code for which performance is priority #1, and where we don't have the standard
>> C++ runtime (such as it is) available. No rtti, no exceptions, no templates
>> and very few reference objects.
>
>Not that old FUD again. The only feature there that may impact
>performance is RTTI. If anything, the reset will improve performance on
>any decent modern compiler. Please, move on from the 90s.
>

It's not FUD, but rather 20+ years of hard-won experience using C++. I've benchmarked
exceptions vs. return values (or even siglongjmp). I've benchmarked the
effects of templates on the icache footprint. Maybe this weekend, I'll benchmark
the cout crap vs. snprintf and see who wins.

> Please, move on from the 90s

Why? Lamdas? give me a break. Not all new is good. You make the language both
harder to learn and more difficult to maintain, you end up with a fringe language like
Lisp or APL.

Now built-in thread support, that's useful, and I'll be happy to use it. Built-in atomics? Nice.

scott

Dombo

unread,
May 14, 2012, 5:09:48 PM5/14/12
to
Op 14-May-12 20:30, Scott Lurndal schreef:
> "Martin B."<0xCDC...@gmx.at> writes:

>> But no templates? Why? Templates as such certainly don't add anything to
>> runtime performance and are a tremendously powerful tool.
>
> Code footprint in the icache (and memory) is the primary reason for no
> templates. This dates back to the first implementation of templates in Cfront 3.0
> which, if templates were specialized for more than one or two types, would bloat
> the codebase tremendously (where the OS and all apps were running in 4MB DRAM,
> space is important). icache footprint is still pretty scarce, every bit that the
> OS uses evicts something the applications need more. Modern templates still cause
> code bloat, even if the programmer never sees it (and modern 4+GB DRAM setups
> insulate this from most programmers).

Naive use of templates may lead to unnecessary code bloat, but this can
be avoided by separating the parts that depend on the template
parameters from the parts that do not depend on the template parameters,
allowing the parts independent of the template arguments to be shared
between template instantiations.

> In the OS, templates would be of very limited use, since the range of data structures
> used in an OS is generally limited to tables and linked lists. If you're willing to use
> casts, a good double linked list can be implemented as a base class using void* pointers
> which can then be used stand-alone or derived from as necessary.

The same trick can be used with templates; implement a container for
void*, and use a template class to take care of the casting to pointers
of the type specified by the template argument. This way only code is
generated for a container of void*, instead of code for each template
instantiation. This technique is explained in the book "The C++
Programming Language" from Bjarne Stroustrup in the chapter about
template specialization.

> Sure, you may give up
> some type safety, but you gain pretty much everwhere else (footprint and performance).

With proper use of templates you don't have to give up type safety for
the sake of performance or a smaller memory footprint. In the example
you gave there is no reason why a properly implemented template version
would be slower or bigger than one which requires the programmer to
apply the casts at the right times and making sure that not the wrong
type is added to the container.

Ian Collins

unread,
May 14, 2012, 5:26:22 PM5/14/12
to
On 05/15/12 08:53 AM, Scott Lurndal wrote:
> Ian Collins<ian-...@hotmail.com> writes:
>> On 05/15/12 04:51 AM, Scott Lurndal wrote:

>>> All the C++ I write is in operating systems, hypervisors and other bare-metal
>>> code for which performance is priority #1, and where we don't have the standard
>>> C++ runtime (such as it is) available. No rtti, no exceptions, no templates
>>> and very few reference objects.
>>
>> Not that old FUD again. The only feature there that may impact
>> performance is RTTI. If anything, the reset will improve performance on
>> any decent modern compiler. Please, move on from the 90s.
>>
>
> It's not FUD, but rather 20+ years of hard-won experience using C++. I've benchmarked
> exceptions vs. return values (or even siglongjmp).

I'd like to see an example where exceptions slow things down. The first
compiler I did a comparative benchmark with was gcc 2.95 and I've yet to
see a case where an exception base solution was slower.

> I've benchmarked the effects of templates on the icache footprint.

Me to. Maybe you were using them inappropriately (the example with
lists indicates you might). A template solution often enables the
compiler to inline calls, improving the icache footprint.

> Maybe this weekend, I'll benchmark the cout crap vs. snprintf and see who wins.

You will find it's six of one and half a dozen of the other.

>> Please, move on from the 90s
>
> Why? Lamdas? give me a break. Not all new is good. You make the language both
> harder to learn and more difficult to maintain, you end up with a fringe language like
> Lisp or APL.
>
> Now built-in thread support, that's useful, and I'll be happy to use it. Built-in atomics? Nice.

Also understanding the how modern compilers optimise for specific targets.

--
Ian Collins

Scott Lurndal

unread,
May 14, 2012, 5:28:56 PM5/14/12
to
Jorgen Grahn <grahn...@snipabacken.se> writes:
>On Mon, 2012-05-14, Ian Collins wrote:
>> On 05/15/12 04:54 AM, Scott Lurndal wrote:
>>> Ian Collins<ian-...@hotmail.com> writes:
>>>> On 05/14/12 05:25 AM, Scott Lurndal wrote:
>>>>> Rui Maciel<rui.m...@gmail.com> writes:
>>>>>> Scott Lurndal wrote:
>>>>>>
>>>>>>> Use it and be happy. cout is useless in real applications.
>>>>>>
>>>>>> Can you provide an example where cout is useless and printf represents a
>>>>>> better option?
>
>...
>>>>> lp->log(" %2.2zu %016llx %3.3s %s\n",
>>>>> b, bpp->b_address, core, bpp->b_enabled?"Yes":"No");
>>>>> }
>>>>> }
>>>>
>>>> Is %016llx a valid format string?
>>>
>>> Yes. It is posix compliant and C99 compliant.
>>> b_address is typed as 'unsigned long long'.
>>
>> And you can see what happens if it isn't the same size as uint64_t!
>>
>> Thank you for providing an example of why not to use printf.
>
>To be fair, decent compilers warn about printf type mismatch problems,
>and can be told to do the same to lp->log().

Indeed:

void log(const char *, ...) __attribute__((format(printf, 2, 3)));

The only "tricky" part is that the attribute counts the hidden 'this' argument,
so you need "2, 3" instead of "1, 2".

(and internally, the code uses uintXX_t types exclusively to ensure that regardless
of the host architecture, the right things happen).

scott

Ian Collins

unread,
May 14, 2012, 5:45:28 PM5/14/12
to
On 05/15/12 04:54 AM, Scott Lurndal wrote:
Fair enough, when I was tinkering with the code I assumed b_address was
an address (void*).

But.... I still illustrates that using printf is more of a maintenance
burden than iostreams.

I should also put my hand up and say that I do use sprintf for small
fixed format, fixed size strings (such as date and times). For example

sprintf( tmp, "%04d%02d%02d%02d%02d%02dZ",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec );

is by far the most concise way to make an LDAP timestamp string.

But I avoid printf/scanf in C++.

--
Ian Collins

Ian Collins

unread,
May 14, 2012, 6:37:09 PM5/14/12
to
> 1) You should _never_ use sprintf. snprintf is preferred (but I understand microsoft doesn't have it).

Note what I wrote: "for small fixed format, fixed size strings". A case
where sprintf is acceptable.

> 2) 'strftime' is much better for formatting timestamps.

True.

> 3) I tried the cout stuff that was posted:

I wouldn't for your example, given the output is a small fixed format,
fixed size string!

--
Ian Collins

Martin B.

unread,
May 15, 2012, 12:48:31 AM5/15/12
to
On 14.05.2012 20:30, Scott Lurndal wrote:
> "Martin B."<0xCDC...@gmx.at> writes:
>> On 14.05.2012 18:51, Scott Lurndal wrote:
>>> Luca Risolia<luca.r...@studio.unibo.it> writes:
>>>> On 13/05/2012 23:39, jacob navia wrote:
>>>>> Le 13/05/12 20:33, Luca Risolia a écrit :
>>>>>> All that stuff (...)>
>>> All the C++ I write is in operating systems, hypervisors and other bare-metal
>>> code for which performance is priority #1, and where we don't have the standard
>>> C++ runtime (such as it is) available. No rtti, no exceptions, no templates
>>> and very few reference objects.
>>>
>>> I suppose you'd call it C with classes, rather than C++. I'm happy with that.
>>>
>>
>> Just wondering ... I can understand not using RTTI and disabling
>> exceptions for performance reasons. (And with disabled exceptions, it
>> obviously follows that much stuff in the standard library doesn't work
>> properly.)
>>
>> But no templates? Why? Templates as such certainly don't add anything to
>> runtime performance and are a tremendously powerful tool.
>
> Code footprint in the icache (and memory) is the primary reason for no

Sorry for the stupid q, but what is "icache"?

> templates. This dates back to the first implementation of templates in Cfront 3.0
> which, if templates were specialized for more than one or two types, would bloat
> the codebase tremendously (where the OS and all apps were running in 4MB DRAM,
> space is important). icache footprint is still pretty scarce, (...)
> Modern templates still cause code bloat, even if the programmer never sees it (...)
> (and modern 4+GB DRAM setups
>

Hmmm ... I have to say that I only work with MSVC, so my experience is
obviously very limited, but: While templates will "bloat" the object
code at first, it seems the linker will do a good job of folding
identical functions so that at the end it seems much of the duplicated
code of the templates will be removed.

> In the OS, templates would be of very limited use, since the range of data structures
> used in an OS is generally limited to tables and linked lists. (...)

What about algorithms? (Algorithm-like functions?)

Ah, obviously I have not much clues wrt OS level code, but still I'm
curious whether you not using templates is based on tradition for your
code base (not sayin' that's a bad thing) or whether it's based on
actual technical limitiations on the platforms you currently use.

cheers,
Martin

--
Good C++ code is better than good C code, but
bad C++ can be much, much worse than bad C code.

Ian Collins

unread,
May 15, 2012, 1:04:49 AM5/15/12
to
On 05/15/12 04:48 PM, Martin B. wrote:
> On 14.05.2012 20:30, Scott Lurndal wrote:
>> "Martin B."<0xCDC...@gmx.at> writes:
>>> On 14.05.2012 18:51, Scott Lurndal wrote:
>>>> Luca Risolia<luca.r...@studio.unibo.it> writes:
>>>>> On 13/05/2012 23:39, jacob navia wrote:
>>>>>> Le 13/05/12 20:33, Luca Risolia a écrit :
>>>>>>> All that stuff (...)>
>>>> All the C++ I write is in operating systems, hypervisors and other bare-metal
>>>> code for which performance is priority #1, and where we don't have the standard
>>>> C++ runtime (such as it is) available. No rtti, no exceptions, no templates
>>>> and very few reference objects.
>>>>
>>>> I suppose you'd call it C with classes, rather than C++. I'm happy with that.
>>>>
>>>
>>> Just wondering ... I can understand not using RTTI and disabling
>>> exceptions for performance reasons. (And with disabled exceptions, it
>>> obviously follows that much stuff in the standard library doesn't work
>>> properly.)
>>>
>>> But no templates? Why? Templates as such certainly don't add anything to
>>> runtime performance and are a tremendously powerful tool.
>>
>> Code footprint in the icache (and memory) is the primary reason for no
>
> Sorry for the stupid q, but what is "icache"?

The processor's instruction cache. These are typically small and fast,
for example see:

http://www.tomshardware.com/reviews/Intel-i7-nehalem-cpu,2041-10.html

--
Ian Collins

Juha Nieminen

unread,
May 15, 2012, 5:21:02 AM5/15/12
to
Scott Lurndal <sc...@slp53.sl.home> wrote:
> Code footprint in the icache (and memory) is the primary reason for no
> templates.

And the alternative is what, exactly?

Writing the functions for each separate type manually? Exactly how would this
be different from the template (other than the template avoiding code
repetition)?

Making the same code support different types? And how exactly would this be
achieved? I see only two possibilities:

1) OO polymorphism, which isn't possible without RTTI (which was also banned
in that list) and would actually increase memory consumption by a significant
lot (because now objects would need to be heap-allocated).

2) Bypass type safety mechanisms and make the code handle things like void
pointers and such. Yeah, great solution. (Not only is it completely horrible
and unsafe code, it also cannot handle everything that a template can, and
in a much safer way at that.)

Religiously avoiding templates is actually detrimental to the quality of
the code and may, in fact, in some cases produce *less* efficient code
(in terms of speed and/or memory usage).

Fearing that caches will fill up quicker if templates are instantiated with
lots of types is also quite moot in most cases. Just because the executable
binary may get larger (which isn't actually always the case) doesn't mean
that caches fill up faster. The CPU only loads into the cache code that is
being run.

Juha Nieminen

unread,
May 15, 2012, 5:23:00 AM5/15/12
to
Dombo <do...@disposable.invalid> wrote:
> Naive use of templates may lead to unnecessary code bloat

Could someone please post a practical example of this mythical "code
bloat" caused by templates, and a better alternative?

(And "practical" above means not artificially contrived to be as
pathological as possible by using completely unconventional code that
no sane programmer would ever write.)

Juha Nieminen

unread,
May 15, 2012, 5:28:33 AM5/15/12
to
Ian Collins <ian-...@hotmail.com> wrote:
> The only feature there that may impact performance is RTTI.

The impact of RTTI on performance is greatly exaggerated by people who
haven't actually tested it, but base their claims solely on assumptions
and impressions.

I have actually tested in practice the speed difference between calling
a regular function and a virtual function. The speed difference was not
measurable. In practice calling a virtual function is basically as fast
as calling a regular function. (Sure, there's an extra indirection step
involved in calling a virtual function, but this extra clock cycle or
so gets completely overwhelmed by everything else that's involved in
calling a function, such as putting values on the stack, brancing,
having the function execute its code, and branching back.)

There might be some contrived inheritance situations (especially if you
use multiple inheritance, and especially if you use virtual inheritance)
where calling a virtual function *might* be measurably slower. However,
these situations are quite rare in practice (and a savvy programmer
wouldn't use such a solution in a speed-critical situation anyways).

Ian Collins

unread,
May 15, 2012, 6:31:36 AM5/15/12
to
On 05/15/12 09:28 PM, Juha Nieminen wrote:
> Ian Collins<ian-...@hotmail.com> wrote:
>> The only feature there that may impact performance is RTTI.
>
> The impact of RTTI on performance is greatly exaggerated by people who
> haven't actually tested it, but base their claims solely on assumptions
> and impressions.

Hence "may". RTTI only really costs if you use it, for example a
dynamic_cast.

--
Ian Collins

Scott Lurndal

unread,
May 15, 2012, 10:35:01 AM5/15/12
to
Juha Nieminen <nos...@thanks.invalid> writes:
>Ian Collins <ian-...@hotmail.com> wrote:
>> The only feature there that may impact performance is RTTI.
>
>The impact of RTTI on performance is greatly exaggerated by people who
>haven't actually tested it, but base their claims solely on assumptions
>and impressions.
>
>I have actually tested in practice the speed difference between calling
>a regular function and a virtual function. The speed difference was not
>measurable. In practice calling a virtual function is basically as fast
>as calling a regular function.

What does vtables have to do with RTTI? They are completely orthogonal.

RTTI has a run-time cost to look up the types, and a space cost to store
the type information (the space cost applies to bare-metal applications such
as operating systems and hypervisors, for regular applications, the rtti
information is stored in the ELF or PE codefile and looked up at runtime
using disk I/O).

scott

Tobias Müller

unread,
May 15, 2012, 11:54:02 AM5/15/12
to
Juha Nieminen <nos...@thanks.invalid> wrote:
> The impact of RTTI on performance is greatly exaggerated by people who
> haven't actually tested it, but base their claims solely on assumptions
> and impressions.

As others already said, you don't need RTTI for virtual functions.

> I have actually tested in practice the speed difference between calling
> a regular function and a virtual function. The speed difference was not
> measurable. In practice calling a virtual function is basically as fast
> as calling a regular function. (Sure, there's an extra indirection step
> involved in calling a virtual function, but this extra clock cycle or
> so gets completely overwhelmed by everything else that's involved in
> calling a function, such as putting values on the stack, brancing,
> having the function execute its code, and branching back.)

And this is exactly where the biggest performance penalty of virtual
functions lies. Non-virtual functions can be inlined much easier and thus
omit that "calling a function" stuff entirely.

Tobi

Tobias Müller

unread,
May 15, 2012, 12:36:07 PM5/15/12
to
Juha Nieminen <nos...@thanks.invalid> wrote:
> 1) OO polymorphism, which isn't possible without RTTI (which was also banned
> in that list)

Virtual functions don't need RTTI, they are perfectly fine!
However you are possibly losing performance, due to the impossibility of
inlining with that solution.

> and would actually increase memory consumption by a significant
> lot (because now objects would need to be heap-allocated).

No they don't have to. Why do you think so?

class Base
{
virtual void doSomething() = 0;
};

class Derived1 : public Base
{
virtual void doSomething()
{
// do something
}
}

class Derived2 : public Base
{
virtual void doSomething()
{
// do something other
}
}

void callDoSomething(Base& obj)
{
obj.doSomething();
}

int main()
{
Derived1 obj1;
callDoSomething(obj1);

Derived2 obj2;
callDoSomething(obj2);

return 0;
}

Tobi

BGB

unread,
May 15, 2012, 1:19:49 PM5/15/12
to
On 5/15/2012 8:54 AM, Tobias Müller wrote:
> Juha Nieminen<nos...@thanks.invalid> wrote:
>> The impact of RTTI on performance is greatly exaggerated by people who
>> haven't actually tested it, but base their claims solely on assumptions
>> and impressions.
>
> As others already said, you don't need RTTI for virtual functions.
>

yep.


internally, it is generally done with vtables, which are "essentially"
arrays of function pointers. each method call is basically accessing the
table at a fixed index.


at least in the ABIs I am familiar with, RTTI is typically implemented
by having a pointer in the first entry of the vtable pointing to a
structure containing information about the class (which may in turn link
to parent classes, ...). (something like RTTI can then be implemented,
say, by walking this list and comparing the pointers).

a class using MI though will often have multiple vtables and multiple
bodies, usually with the parent classes being places as complete objects
next to each other in memory (following the data for the current class),
whereas with SI it is simply appending new data or methods onto the end
of the existing object or vtable.


(in my VM, it is the reverse, where the object points to the class which
points to the vtable, but there are reasons for doing it this way,
mostly related to classes being mutable at runtime...).


>> I have actually tested in practice the speed difference between calling
>> a regular function and a virtual function. The speed difference was not
>> measurable. In practice calling a virtual function is basically as fast
>> as calling a regular function. (Sure, there's an extra indirection step
>> involved in calling a virtual function, but this extra clock cycle or
>> so gets completely overwhelmed by everything else that's involved in
>> calling a function, such as putting values on the stack, brancing,
>> having the function execute its code, and branching back.)
>
> And this is exactly where the biggest performance penalty of virtual
> functions lies. Non-virtual functions can be inlined much easier and thus
> omit that "calling a function" stuff entirely.
>

it is a little better on newer HW, which is usually able to
branch-predict through the return instruction (vs older HW where nearly
any indirect call or return would stall the pipeline).

currently, an if-statement and an unpredictable jump are a bit more costly.

in cases where the call target or return are harder to predict, there
will often be a pipeline stall.

this is extra true with a "switch" which can be extra costly given the
CPU will most often get it wrong.


meanwhile, the extra indirection is "nearly free" AFAICT.

> Tobi

BGB

unread,
May 15, 2012, 1:26:14 PM5/15/12
to
well, except disk IO isn't generally involved in using RTTI.

as I understand it, it is generally contained within the raw image,
typically in ".data" or ".rdata" or similar, so it is loaded along with
everything else, and treated as raw data.


> scott

Dombo

unread,
May 15, 2012, 1:50:52 PM5/15/12
to
Op 15-May-12 11:23, Juha Nieminen schreef:
> Dombo<do...@disposable.invalid> wrote:
>> Naive use of templates may lead to unnecessary code bloat
>
> Could someone please post a practical example of this mythical "code
> bloat" caused by templates, and a better alternative?

Please notice the *may* in the above sentence. It depends on the code
and the compiler/linker. For example I know for a fact (having stepped
through the assembly code) that with recent versions of the Visual C++
compiler the void*/casting trick I mentioned in my previous post is not
necessary (at least not for trivial cases); the compiler takes care of
sharing the code between template instantiations if possible. I haven't
checked this with other compilers so your (or Scotts) mileage may vary.


nick_keigh...@hotmail.com

unread,
May 16, 2012, 7:21:28 AM5/16/12
to
On Tuesday, May 15, 2012 10:28:33 AM UTC+1, Juha Nieminen wrote:
> Ian Collins <ian-...@hotmail.com> wrote:

> > The only feature there that may impact performance is RTTI.
>
> The impact of RTTI on performance is greatly exaggerated by people who
> haven't actually tested it, but base their claims solely on assumptions
> and impressions.
>
> I have actually tested in practice the speed difference between calling
> a regular function and a virtual function.

RTTI != virtual function

was your virtual function being called polymorphically? Because if it wasn't any reasonable compiler would call it non-virtually

> The speed difference was not
> measurable. In practice calling a virtual function is basically as fast
> as calling a regular function. (Sure, there's an extra indirection step
> involved in calling a virtual function, but this extra clock cycle or
> so gets completely overwhelmed by everything else that's involved in
> calling a function, such as putting values on the stack, brancing,
> having the function execute its code, and branching back.)

yes

Richard Damon

unread,
May 16, 2012, 8:04:46 AM5/16/12
to
Note that as many "rules" based on performance, things change over time.
When C++ was young, optimizations like the linker folding two identical
functions was unheard of. In one sense, because of the level of this
problem, implementations have spent a lot of effort to relieve much of
the issue.

You can still have cases where a template function has a significant
piece that could be factored out as common over many types, but because
the programmer only sees it in the code once, they don't think about
factoring that code out, or that there are piece that COULD be factored
out with small implementation changes, but because the code is only
written once, the need doesn't present itself to the programmer.

Without knowing details of how good an implementation is, you don't know
how much output code a template is going to create when implemented many
times. Thus, especially in the hands of a junior programmer, there is a
danger that it is possible to generate this bloat without obvious signs
in the program.

Like most of these "rules", they are primarily aimed at the junior
programmer who is still learning what works well and what doesn't, and
were rarely intended to be "hard" rules that all programmers must obey.
Note that this rule begins with "Naive" and has "may", which basically
just says, think about before just throwing in a template, to make sure
you understand possible ramifications. The senior programmer likely has
developed the intuition to recognize these and has less need for them as
formal "rules"

Pavel

unread,
May 16, 2012, 9:55:01 PM5/16/12
to
Martin Shobe wrote:
> Luca Risolia wrote:
>
>> On 13/05/2012 23:39, jacob navia wrote:
>>> Le 13/05/12 20:33, Luca Risolia a écrit :
>>>> All that stuff can be written in a more readable, type-safe code by
>>>> using std::clog (or whatever stream you need) and standard manipulators.
>>>
>>> Great!
>>>
>>> Go ahead, and show us how.
>>
>> if (p_num_breakpoints> 0) {
>> clog<< "Breakpoint Address Core Enabled?\n"
>> "---------- ---------------- ---- --------\n";
>>
>> for (size_t b = 0; b< p_num_breakpoints; b++) {
>> const s_breakpoint *bpp =&p_breakpoints[b];
>> ostringstream core;
>> bpp->b_core == -1 ? core<< "All" : core<< setw(3)<< bpp->b_core;
So, what's the advantage of this code, which is longer, more complex (counted,
for example, as the number of tokens even when adjusted with the number of
format specifiers from printf code) and unnecessary dynamic memory
[de]allocations dynamic inside the loop (which may start having quite
interesting performance implications for a process compiled with thread-safety
options somewhere in the middle of production) over the original code does not
do any of it?

Also, the output statement does not relate intuitively to the header so counting
all these setw arguments would make me feel dumb (for doing a stupid job that I
could have avoided if I used the right tools).

-Pavel

>> clog<< setfill(' ')<< setw(3)<< ' ' // indent
>> << setfill('0')<< setw(2)<< b
>> << setfill(' ')<< setw(8)<< ''
>> << setfill('0')<< setw(16)
>> << hex<< reinterpret_cast<unsigned long>(bpp->b_address)
>> << setfill(' ')<< setw(2)<< ''
>> << core.str().substr(0, 3)<< ''
>> << setfill(' ')<< setw(3)<< ''
>> << boolalpha<< static_cast<bool>(bpp->b_enabled)
>> << '\n';
>> }
>> }
>>
>> ..which produces the same output as the original version with printf():
>>
>> Breakpoint Address Core Enabled?
>> ---------- ---------------- ---- --------
>> 00 00007fff7f886a0c All false
>> 01 00007fff7f886a0c 12 false
>> 02 00007fff7f886a0c 123 true
>
> I thought the output was supposed to look like this. (Obviously it
> wouldn't take much to change it to this).
>
> Breakpoint Address Core Enabled?
> ---------- ---------------- ---- --------
> 0 00007fff7f886a0c All No
> 1 00007fff7f886a0c 12 No
> 2 00007fff7f886a0c 123 Yes
>
> [snip]
>
> Martin Shobe
>

Pavel

unread,
May 16, 2012, 10:46:32 PM5/16/12
to
Not to blame templates in general for code bloating (bloating compilation time
is another story; for whatever reason no one here complains about this one), but
the above ostream code (and any iostream code, in my experience) certainly
bloats the code at the call site as compared to printf code as it emits many
extra function calls and object constructions on top of 2-3 function calls taken
by printf.

Just for my own interest, I composed two roughly equivalent printing functions
from the above example into a little program (see below), compiled it with g++
4.6.3 on 64-bit Linux, optimizing for size:

g++ -Os -o pvocs pvocs.cpp

and measured the functions from 'objdump -d' dump.

the size of printfOutput() is 0x400f22 - 0x400e74 + 1 = 175 bytes
the size of ostreamOutput() is x401146 - 0x400f23 + 5 = 552 bytes


The last addition of 1 or 5 is for the length of the last command whose address
I have in the dump. It is instructive that printOutput() ends with a simple
1-byte retq instruction:

400f22: c3 retq

whereas ostreamOutput() ends with 5-byte call of (obviously long-jumping) C++
stack-unwinding stub

401146: e8 c5 fb ff ff callq 400d10 <_Unwind_Resume@plt>

where _Unwind_Resume strains CPU pipeline a couple of extra times and loads a
few extra cache lines:

0000000000400d10 <_Unwind_Resume@plt>:
400d10: ff 25 82 13 20 00 jmpq *0x201382(%rip) # 602098
<_GLOBAL_OFFSET_TABLE_+0xb0>
400d16: 68 13 00 00 00 pushq $0x13
400d1b: e9 b0 fe ff ff jmpq 400bd0 <_init+0x20>


(so much for C++ function calls' not having extra run-time cost as compared to C
ones -- probably true only if you use nothrow functions throughout?)

As for the original purpose of the exercise, ostreamOutput() takes more than
thrice the code size of the printfOutput(). Judge for yourself.

STANDARD DISCLAIMER : your mileage can vary (not too much though :-) ).


-Pavel

// -------------- pvocs.cpp -- cut here -------------------
#include <iomanip>
#include <iostream>
#include <sstream>
#include <stdio.h>

using namespace std;

void
printfOutput(size_t b, void *bAddress, int bEnabled, int bCore) {
char core[16];
if (bCore == -1) {
snprintf(core, sizeof(core), "%s", "All");
} else {
snprintf(core, sizeof(core), "%2.2d", bCore);
}
printf(" %2.2zu %016llx %3.3s %s\n", b, (long long)bAddress, core,
bEnabled ? "Yes" : "No");
}

void
ostreamOutput(size_t b, void *bAddress, int bEnabled, int bCore) {
ostringstream core;
bCore == -1 ? core << "All" : core << setw(3) << bCore;
clog << setfill(' ') << setw(3) << ' ' // indent
<< setfill('0') << setw(2) << b
<< setfill(' ') << setw(8) << ' '
<< setfill('0') << setw(16)
<< hex << reinterpret_cast<unsigned long>(bAddress)
<< setfill(' ') << setw(2) << ' '
<< core.str().substr(0, 3) << ' '
<< setfill(' ') << setw(3) << ' '
<< (static_cast<bool>(bEnabled) ? "Yes" : "No")
<< '\n';
}

int
main(int, char*[]) {
size_t b = 2;
void *bAddress = &b;
int bEnabled = 0;
int bCore = -1;
printfOutput(b, bAddress, bEnabled, bCore);
ostreamOutput(b, bAddress, bEnabled, bCore);
return 0;
}
// -------------- pvocs.cpp -- cut here -------------------

Pavel

unread,
May 16, 2012, 11:38:10 PM5/16/12
to
Juha Nieminen wrote:
> Scott Lurndal<sc...@slp53.sl.home> wrote:
>> Code footprint in the icache (and memory) is the primary reason for no
>> templates.
>
> And the alternative is what, exactly?
>
> Writing the functions for each separate type manually? Exactly how would this
> be different from the template (other than the template avoiding code
> repetition)?
>
> Making the same code support different types? And how exactly would this be
> achieved? I see only two possibilities:
>
> 1) OO polymorphism, which isn't possible without RTTI (which was also banned
> in that list) and would actually increase memory consumption by a significant
> lot (because now objects would need to be heap-allocated).
>
> 2) Bypass type safety mechanisms and make the code handle things like void
> pointers and such. Yeah, great solution. (Not only is it completely horrible
> and unsafe code,
Just a friendly poke, please don't take too seriously: I hope you are writing
this post using an operating system unknown to me that is at least a decade old
but is not fully composed of such completely horrible and unsafe code...

More seriously, in my experience template code carries more bugs and uglier than
plain-old C-like code. I found it to be related to its inherent inflexibility
that manifests itself after a while, e.g. as follows:

1. At one moment in time we decide to use compile-time parametrization for a new
system. We assume the arguments will always be known at compile time. We build
the system and boast how flexible it is (e.g. how fast it is for us to plug in a
new algorithm or type) and higher management grows to rely on our ability to
deliver new functionality in a matter of few days, sometimes hours.

2. Sooner or later at least one "disruptive requirement" comes that translates
to a necessity to select an algo or type based on information only available at
run time. The "correct" approach would be to re-design a system and try to
provide "best of two worlds" but delivery would take months (with the resulting
system being notably more complex than the current one, but maintainable).
Higher management does not see any principal difference between the new
requirement and previous ones we aced so brilliantly; thus we are not getting
budget for redesign. Instead, we either instantiate code with all possible
combinations of template parameters (code bloating in its best) and select the
right one using some ugly variety of Factory or add some really dirty hack or two.

3. If the system is good for anything, that one disruptive requirement is
followed by a number of others, not necessarily disruptive in themselves, but
now they need to be implemented on top of over-bloated and/or ugly hacked
system. The bug frequency and code bloating starts increasing quadratically or
worse with the system size (whereas before the "disruptive requirement" we
managed to keep it almost linear). Everyone gets annoyed. If an original
designer did not leave the organization at stage #2, s/he leaves it now.

4. A 1-year-ago "beautiful little system" is now known as "an ugly
unmaintainable monster that devours thousands of top-consultant-hours per year
which we have to tolerate to stay in business". At this point, no one even dares
to request a performance improvement (the best answer of a swamped development
manager who "did not write this system" would be "buy better hardware; if you
are lucky we will be able to migrate to it in a couple of months"). Technically
speaking, running those glue-code-hacks (e.g. Factories) may now take more
run-time than using the resulting "products". Oh, yes, and compiling our system
now takes many hours on the best available hardware (maybe as long as it used to
take to add a new feature at stage #1).

I hate to say it but the initial decision to rely on compile-time
parametrization played a significant role in this outcome.

To summarize, I do not mind templates for "programming-in-small" but it would be
very unusual for me to base a design of a business system on them. Wherever
possible I am trying to give a non-trivial component a non-template interface
regardless of whether it uses templates in its implementation.

HTH
-Pavel

Pavel

unread,
May 17, 2012, 12:13:49 AM5/17/12
to
Ian, with all due respect exceptions do matter -- please see my example above
for the difference between the epilogue code of printf- and ostream- using
output functions.

My post has all necessary data for you to experiment on your own and it is with
your modern enough g++ 4.6.3, optimized for code size.

-Pavel
>
>

Pavel

unread,
May 17, 2012, 12:16:26 AM5/17/12
to
Ian Collins wrote:
> On 05/15/12 08:53 AM, Scott Lurndal wrote:
>> Ian Collins<ian-...@hotmail.com> writes:
>>> On 05/15/12 04:51 AM, Scott Lurndal wrote:
>
>>>> All the C++ I write is in operating systems, hypervisors and other bare-metal
>>>> code for which performance is priority #1, and where we don't have the standard
>>>> C++ runtime (such as it is) available. No rtti, no exceptions, no templates
>>>> and very few reference objects.
>>>
>>> Not that old FUD again. The only feature there that may impact
>>> performance is RTTI. If anything, the reset will improve performance on
>>> any decent modern compiler. Please, move on from the 90s.
>>>
>>
>> It's not FUD, but rather 20+ years of hard-won experience using C++. I've
>> benchmarked
>> exceptions vs. return values (or even siglongjmp).
>
> I'd like to see an example where exceptions slow things down. The first compiler
> I did a comparative benchmark with was gcc 2.95 and I've yet to see a case where
> an exception base solution was slower.
>
>> I've benchmarked the effects of templates on the icache footprint.
>
> Me to. Maybe you were using them inappropriately (the example with lists
> indicates you might). A template solution often enables the compiler to inline
> calls, improving the icache footprint.
In general, it may -- but not for C++ streams that is the original subject of
this thread. C++ streams require virtual calls for conversions by the Standard.

-Pavel

Ian Collins

unread,
May 17, 2012, 12:28:19 AM5/17/12
to
iostreams aren't an example of comparing an exception based solution
with a call and test solution. Try a more apples to apples comparison
and exceptions will win the day.

--
Ian Collins

Ian Collins

unread,
May 17, 2012, 12:33:51 AM5/17/12
to
On 05/17/12 04:16 PM, Pavel wrote:
> Ian Collins wrote:
>> On 05/15/12 08:53 AM, Scott Lurndal wrote:
>>> Ian Collins<ian-...@hotmail.com> writes:
>>>> On 05/15/12 04:51 AM, Scott Lurndal wrote:
>>
>>>>> All the C++ I write is in operating systems, hypervisors and other bare-metal
>>>>> code for which performance is priority #1, and where we don't have the standard
>>>>> C++ runtime (such as it is) available. No rtti, no exceptions, no templates
>>>>> and very few reference objects.
>>>>
>>>> Not that old FUD again. The only feature there that may impact
>>>> performance is RTTI. If anything, the reset will improve performance on
>>>> any decent modern compiler. Please, move on from the 90s.
>>>>
>>>
>>> It's not FUD, but rather 20+ years of hard-won experience using C++. I've
>>> benchmarked
>>> exceptions vs. return values (or even siglongjmp).
>>
>> I'd like to see an example where exceptions slow things down. The first compiler
>> I did a comparative benchmark with was gcc 2.95 and I've yet to see a case where
>> an exception base solution was slower.
>>
>>> I've benchmarked the effects of templates on the icache footprint.
>>
>> Me to. Maybe you were using them inappropriately (the example with lists
>> indicates you might). A template solution often enables the compiler to inline
>> calls, improving the icache footprint.

> In general, it may -- but not for C++ streams that is the original subject of
> this thread. C++ streams require virtual calls for conversions by the Standard.

My comment was in response to the claim templates negatively impact
performance, which is nonsense. Templates were introduced into the
thread as one place where iostreams really have to be used in preference
to C stream functions.

--
Ian Collins

Juha Nieminen

unread,
May 17, 2012, 5:16:13 AM5/17/12
to
Pavel <pauldont...@removeyourself.dontspam.yahoo> wrote:
> Instead, we either instantiate code with all possible
> combinations of template parameters (code bloating in its best)

Ok, I have decided to not take seriously anybody who uses the term
"code bloat" to describe templates. It's just a myth that people keep
repeating like parrots, with little actual evidence.

(And by that I don't mean that using templates wouldn't increase the
size of the executable in any way. What I do mean is the implication
that templates cause the executable size to explode (which is what I
understand by "code bloat") and that there would be a better (or at
least equally good) alternative that does not cause an equivalent
increase in executable size, while still keeping the design of the
program equally clean, efficient and safe.)

Ironically, in some cases using templates can actually *reduce* the
size of the executable (because it allows the compiler to inline and
optimize the code on a type-by-type basis which, especially when dealing
with basic integral types and such, can help it eliminiate tons of code
that it would not be able to eliminate otherwise, had a more "C-like"
solution been used).

Martin B.

unread,
May 17, 2012, 8:54:36 AM5/17/12
to
On 17.05.2012 04:46, Pavel wrote:
> Juha Nieminen wrote:
>> Dombo<do...@disposable.invalid> wrote:
>>> Naive use of templates may lead to unnecessary code bloat
>>
>> Could someone please post a practical example of this mythical "code
>> bloat" caused by templates, and a better alternative?
>>
>> (And "practical" above means not artificially contrived to be as
>> pathological as possible by using completely unconventional code that
>> no sane programmer would ever write.)
> Not to blame templates in general for code bloating (bloating
> compilation time is another story; for whatever reason no one here
> complains about this one), but the above ostream code (and any iostream
> code, in my experience) certainly bloats the code at the call site as
> compared to printf code (...)
>
> I composed two roughly equivalent printing
> functions from the above example into a little program (see below),
> compiled it with g++ 4.6.3 on 64-bit Linux, optimizing for size:
>
> g++ -Os -o pvocs pvocs.cpp
>
> and measured the functions from 'objdump -d' dump.
>
> the size of printfOutput() is 0x400f22 - 0x400e74 + 1 = 175 bytes
> the size of ostreamOutput() is x401146 - 0x400f23 + 5 = 552 bytes
> (...)
>
> (so much for C++ function calls' not having extra run-time cost as
> compared to C ones -- probably true only if you use nothrow functions
> throughout?)
>
> As for the original purpose of the exercise, ostreamOutput() takes more
> than thrice the code size of the printfOutput(). Judge for yourself.
>
> STANDARD DISCLAIMER : your mileage can vary (not too much though :-) ).
>

Are you trying to make a point wrt. templates, or wrt. the original
topic else-thread?

cheers,
Martin

Martin B.

unread,
May 17, 2012, 9:11:43 AM5/17/12
to
On 17.05.2012 05:38, Pavel wrote:
> Juha Nieminen wrote:
>> Scott Lurndal<sc...@slp53.sl.home> wrote:
>>> Code footprint in the icache (and memory) is the primary reason for no
>>> templates.
>>
>> And the alternative is what, exactly?
>>
>> Writing the functions for each separate type manually? Exactly how
>> would this
>> be different from the template (other than the template avoiding code
>> repetition)?
>>
>> Making the same code support different types? And how exactly would
>> this be
>> achieved? I see only two possibilities:
>>
>> (...)
>>
>> 2) Bypass type safety mechanisms and make the code handle things like
>> void
>> pointers and such. Yeah, great solution. (Not only is it completely
>> horrible
>> and unsafe code,
> Just a friendly poke, please don't take too seriously: I hope you are
> writing this post using an operating system unknown to me that is at
> least a decade old but is not fully composed of such completely horrible
> and unsafe code...
>

Makes me wonder ... according to [The Programming Languages
Beacon](http://www.lextrait.com/vincent/implementations.html) MS Windows
uses C++ (whatever that means, it's not like we have too many details
here). That means we cannot rule out that some OS components are
actually using templates.

> More seriously, in my experience template code carries more bugs and
> uglier than plain-old C-like code. I found it to be related to its
> inherent inflexibility that manifests itself after a while, e.g. as
> follows:
>

Well, my (limited) experience is the opposite.

> 1. At one moment in time we decide to use compile-time parametrization
> for a new system. We assume the arguments will always be known at
> compile time. We build the system and boast how flexible (...)
>
> 2. Sooner or later at least one "disruptive requirement" comes that
> translates to a necessity to select an algo or type based on information
> only available at run time. (...)
>
> 3. If the system is good for anything, that one disruptive requirement
> is followed by a number of others, not necessarily disruptive in
> themselves, but now they need to be implemented on top of over-bloated
> and/or ugly hacked system. (...)
>
> 4. A 1-year-ago "beautiful little system" is now known as "an ugly
> unmaintainable monster (...) Oh, yes, and compiling our system now takes
> many hours on the best available hardware (maybe as long as it used to
> take to add a new feature at stage #1).
>
> I hate to say it but the initial decision to rely on compile-time
> parametrization played a significant role in this outcome.
>

Is this FUD based on any actual experience or did you make it up on the
spot? (And do you really believe that templates played a *significant*
role?)

Just sayin' ... you *don't have to* base the whole design of something
on templates. You don't have to understand
[Boost.Spirit](http://boost-spirit.com/home/) to be using templates.

std::vector and friends is just fine, stick to the basics.

> To summarize, I do not mind templates for "programming-in-small" but it
> would be very unusual for me to base a design of a business system on
> them. Wherever possible I am trying to give a non-trivial component a
> non-template interface regardless of whether it uses templates in its
> implementation.
>

To repeat, this does seem like a prudent approach, but this doesn't
imply to not use *any* templates in a big project, and your quote from
above:

> in my experience template code carries more bugs and
> uglier than plain-old C-like code.

now more sounds like "huh, templates aren't the solution for everything,
you know", which I think noone really claims.

cheers,
Martin

Martin Shobe

unread,
May 17, 2012, 11:53:57 AM5/17/12
to
Pavel wrote:

> Martin Shobe wrote:
>> Luca Risolia wrote:
[snip]
>>>
>>> if (p_num_breakpoints> 0) {
>>> clog<< "Breakpoint Address Core Enabled?\n"
>>> "---------- ---------------- ---- --------\n";
>>>
>>> for (size_t b = 0; b< p_num_breakpoints; b++) {
>>> const s_breakpoint *bpp =&p_breakpoints[b];
>>> ostringstream core;
>>> bpp->b_core == -1 ? core<< "All" : core<< setw(3)<< bpp->b_core;
> So, what's the advantage of this code, which is longer, more complex (counted,
> for example, as the number of tokens even when adjusted with the number of
> format specifiers from printf code) and unnecessary dynamic memory
> [de]allocations dynamic inside the loop (which may start having quite
> interesting performance implications for a process compiled with thread-safety
> options somewhere in the middle of production) over the original code does not
> do any of it?
>
> Also, the output statement does not relate intuitively to the header so counting
> all these setw arguments would make me feel dumb (for doing a stupid job that I
> could have avoided if I used the right tools).

[snip]

Pavel, if the only thing you are responding to is the quoted part of the
post, you might want to consider replying to the quoted post instead.
This makes it look like you are asking me, and I didn't write this
code, and wouldn't write it that way. (I did post code showing how I
would do it, so if you have any question about that, feel free).

Martin Shobe

Scott Lurndal

unread,
May 17, 2012, 12:04:32 PM5/17/12
to
As of NT 5.0 (Win2000), none of NT was written in C++. I haven't seen
the source code for successive releases, but I seriously doubt that they've
changed. Even if there was C++ code in the kernel, they would be very
unlikely to use Templates.

It is entirely likely that much of the user-level code that makes up the
user interface for Windows is in C++ and may use templates. That just
explains why windows is so blame slow. :-)

scott

Dombo

unread,
May 17, 2012, 1:52:11 PM5/17/12
to
Op 17-May-12 18:01, Scott Lurndal schreef:
> Juha Nieminen<nos...@thanks.invalid> writes:
>> Pavel<pauldont...@removeyourself.dontspam.yahoo> wrote:
>>> Instead, we either instantiate code with all possible
>>> combinations of template parameters (code bloating in its best)
>>
>> Ok, I have decided to not take seriously anybody who uses the term
>> "code bloat" to describe templates. It's just a myth that people keep
>> repeating like parrots, with little actual evidence.
>
> Our definition of bloat obviously differs. For the code that I
> work with, every single byte matters. The fact that templatization of
> basic data structures will repeat the same code multiple types (once for
> each parameterized type), is bloat.

That is not necessarily true. Instantiation of a template with another
parameterized type does not always result in code being generated
specifically for that type. The compiler/linker may reuse code generated
for another template instantiation. In case of Visual C++ I have seen
exactly that (by stepping through the generated assembly code).

Earlier you mentioned a double linked list with void* which requires the
programmer to make sure to use the correct casts and that the right type
is inserted. With a recent version of Visual C++ (and I expect also
other modern C++ compilers) you might as well have used templates
because the code generated for dl_list<A*> is also called for
dl_list<B*>, dl_list<C*>...etc. I.e. you get type safety a no cost
(well...compilation times may increase a bit).

> I'm not talking about doubling
> the executable text region size (although I've seen significant percentage
> increases), but every cache line matters for certain classes of programs.

True, but if template instantiations share the same (not repeated) code
there is no increase in executable size or cache misses.

>> (And by that I don't mean that using templates wouldn't increase the
>> size of the executable in any way. What I do mean is the implication
>> that templates cause the executable size to explode (which is what I
>> understand by "code bloat") and that there would be a better (or at
>> least equally good) alternative that does not cause an equivalent
>> increase in executable size, while still keeping the design of the
>> program equally clean, efficient and safe.)
>
> As Pavel pointed out, safe is relative. Pointers and casting void *
> are perfectly valid C++ and can be used safely.

There is a difference between perfectly valid code and safe code.

I'm interested in how you get the compiler to complain if you
accidentally pass a pointer of the wrong type to a function or variable
taking a void* and when a wrong cast from a void* is made.

Not related to safety but to productivity, I'm also interested in how
you get intellisense/code completion feature (or whatever it is called
in your IDE) to know which members are on the object the void* is
referencing.

> They also can be
> misused. C'est la vie. They are way more efficient in many cases.

I'm not saying the use templates never leads to an increase of the
executable size, but I get the impression you believe that each template
instantiation always increases the size of the executable. With modern
compilers/linkers this is not necessarily the case.

Scott Lurndal

unread,
May 17, 2012, 2:34:15 PM5/17/12
to
Dombo <do...@disposable.invalid> writes:

>That is not necessarily true. Instantiation of a template with another
>parameterized type does not always result in code being generated
>specifically for that type. The compiler/linker may reuse code generated
>for another template instantiation. In case of Visual C++ I have seen
>exactly that (by stepping through the generated assembly code).
>
>Earlier you mentioned a double linked list with void* which requires the
>programmer to make sure to use the correct casts and that the right type
>is inserted. With a recent version of Visual C++ (and I expect also
>other modern C++ compilers) you might as well have used templates
>because the code generated for dl_list<A*> is also called for
>dl_list<B*>, dl_list<C*>...etc. I.e. you get type safety a no cost
>(well...compilation times may increase a bit).

I've never used microsoft compilers (well there were those two device
drivers in the late 90's for 3.51, but that doesn't really count), so
I'd have to see if g++ on unix/linux supports this.

It is a bit of a trivial example of template usage, tho.

>
>>> (And by that I don't mean that using templates wouldn't increase the
>>> size of the executable in any way. What I do mean is the implication
>>> that templates cause the executable size to explode (which is what I
>>> understand by "code bloat") and that there would be a better (or at
>>> least equally good) alternative that does not cause an equivalent
>>> increase in executable size, while still keeping the design of the
>>> program equally clean, efficient and safe.)
>>
>> As Pavel pointed out, safe is relative. Pointers and casting void *
>> are perfectly valid C++ and can be used safely.
>
>There is a difference between perfectly valid code and safe code.
>
>I'm interested in how you get the compiler to complain if you
>accidentally pass a pointer of the wrong type to a function or variable
>taking a void* and when a wrong cast from a void* is made.

I don't. The programmers are expected to test every line of code before
it gets checked in, and at least one peer must review the code prior to
acceptance into the tree.

Yes type safety is helpful, if it can be done without extra footprint.

>
>Not related to safety but to productivity, I'm also interested in how
>you get intellisense/code completion feature (or whatever it is called
>in your IDE) to know which members are on the object the void* is
>referencing.

IDE, what's that?

gvim, cscope, gdb and make have been sufficient so far. I played with eclipse, but
productivity suffered (my fingers have been 'vi'ing since 1989, and ed'ing before
that from 1979). I also often program on remote hosts via ssh and on headless
systems on which a GUI IDE is unavailable.

Even when writing Java, netbeans wasn't as productive for me as
gvim, javac, cscope and ant (or even make).

YMWV.

scott

Luca Risolia

unread,
May 17, 2012, 3:20:15 PM5/17/12
to
On 17/05/2012 20:34, Scott Lurndal wrote:
> Dombo<do...@disposable.invalid> writes:

> I don't. The programmers are expected to test every line of code before
> it gets checked in, and at least one peer must review the code prior to
> acceptance into the tree.
>
> Yes type safety is helpful, if it can be done without extra footprint.

I think what Dombo was trying to say is something like this:

template <class T> class Vector {
T*v;
int sz;
public:
Vector();
Vector(int);
T& elem(int i) {
return v[i];
}
T& operator[](int i);
void swap(Vector&);
//...
};

template<> class Vector<void*> { // complete specialization
void** p;
//...
void*& operator[](int i);
};

// ALL the partial specializations can now share the same code:
template<class T> class Vector<T*> : private Vector<void*> {
public:
typedef Vector<void*> Base;
Vector : Base() {}
explicit Vector(int i) : Base(i) {}
T& elem(int i) { return static_cast<T*&>(Base::elem(i)); }
T*& operator[](int i) { return static_cast<T*&>(Base::operator[](i)); }
}

which is a rather simple idea, if your compiler does not do that for
you. Everything will be expanded in-line and type safety will be preserved.

Where is the "extra footprint" there?

(The example has been taken from the C++ bible)

Jorgen Grahn

unread,
May 17, 2012, 3:29:16 PM5/17/12
to
On Thu, 2012-05-17, Juha Nieminen wrote:
> Pavel <pauldont...@removeyourself.dontspam.yahoo> wrote:
>> Instead, we either instantiate code with all possible
>> combinations of template parameters (code bloating in its best)
>
> Ok, I have decided to not take seriously anybody who uses the term
> "code bloat" to describe templates. It's just a myth that people keep
> repeating like parrots, with little actual evidence.

Yes. I'm very tempted to start trying to refute claims from Pavel and
S.L. in this thread ... but then I do a reality check. Templates
(writing new ones, and especially using the standard library's
containers and algorithms) are one of the cornerstones of modern C++.

Put differently, if I want C, I know where to find it.

(I also note that this is a re-run of a discussion back in January.
See e.g. Message-ID: <jgmUq.16032$5g....@news.usenetserver.com>.
Same people involved, and I bet the same outcome: no consensus.)

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Marc

unread,
May 17, 2012, 4:17:31 PM5/17/12
to
Scott Lurndal wrote:

> Dombo <do...@disposable.invalid> writes:
>
>>That is not necessarily true. Instantiation of a template with another
>>parameterized type does not always result in code being generated
>>specifically for that type. The compiler/linker may reuse code generated
>>for another template instantiation. In case of Visual C++ I have seen
>>exactly that (by stepping through the generated assembly code).
[...]
> I've never used microsoft compilers (well there were those two device
> drivers in the late 90's for 3.51, but that doesn't really count), so
> I'd have to see if g++ on unix/linux supports this.

If you use the gold linker, look at the --icf option.

Juha Nieminen

unread,
May 18, 2012, 2:46:58 AM5/18/12
to
Scott Lurndal <sc...@slp53.sl.home> wrote:
> As Pavel pointed out, safe is relative. Pointers and casting void *
> are perfectly valid C++ and can be used safely.

"Can be used safely" != "safe".

>>Ironically, in some cases using templates can actually *reduce* the
>>size of the executable (because it allows the compiler to inline and
>>optimize the code on a type-by-type basis which, especially when dealing
>>with basic integral types and such, can help it eliminiate tons of code
>>that it would not be able to eliminate otherwise, had a more "C-like"
>>solution been used).
>
> Use the inline keyword with the appropriate functions when you write
> them.

Did you miss the "optimize the code on a type-by-type basis" part?

Martin B.

unread,
May 18, 2012, 7:48:08 AM5/18/12
to
Well. That's more than 10 years!

Anyways, given how the MS compiler handles C and C++, my personaly guess
would be that they compile their kernel code with the C++ compiler mode
but write mostly "C" code. I may be totally wrong of course.

> Even if there was C++ code in the kernel, they would be very
> unlikely to use Templates.
>

What information is this statement based on?

cheers,
Martin

Paul

unread,
May 18, 2012, 5:59:49 PM5/18/12
to

"Seneika" <not.s...@today.com> wrote in message
news:4fad54e1$0$291$1472...@news.sunsite.dk...
> 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.
>
> Thanks,
> Seneika.
>
printf is a function, cout is an object. They are different mechanisms for
directing output. If printf works for you then why not use it, I guess it
depends on your project and how it all fits together. C++ is built on a C
foundation so there is no problem using C within C++.



--- Posted via news://freenews.netfront.net/ - Complaints to ne...@netfront.net ---

Scott Lurndal

unread,
May 14, 2012, 6:28:29 PM5/14/12
to
Ian Collins <ian-...@hotmail.com> writes:
>On 05/15/12 04:54 AM, Scott Lurndal wrote:
>> Ian Collins<ian-...@hotmail.com> writes:
>>> On 05/14/12 05:25 AM, Scott Lurndal wrote:
>>>> Rui Maciel<rui.m...@gmail.com> writes:
>>>>> Scott Lurndal wrote:
>>>>>
>>>>>> Use it and be happy. cout is useless in real applications.
>>>>>
>>>>> Can you provide an example where cout is useless and printf represents a
>>>>> better option?
>>>>>
>>>>>
>>>>
>>>> if (p_num_breakpoints> 0) {
>>>> lp->log("Breakpoint Address Core Enabled?\n");
>>>> lp->log("---------- ---------------- ---- --------\n");
>>>>
>>>> for(size_t b = 0; b< p_num_breakpoints; b++) {
>>>> const s_breakpoint *bpp =&p_breakpoints[b];
>>>> char core[16];
>>>>
>>>> if (bpp->b_core == -1) {
>>>> snprintf(core, sizeof(core), "%s", "All");
>>>> } else {
>>>> snprintf(core, sizeof(core), "%2.2d", bpp->b_core);
>>>> }
>>>> lp->log(" %2.2zu %016llx %3.3s %s\n",
>>>> b, bpp->b_address, core, bpp->b_enabled?"Yes":"No");
>>>> }
>>>> }
>>>
>>> Is %016llx a valid format string?
>>>
>>
>> Yes. It is posix compliant and C99 compliant. b_address is typed as 'unsigned long long'.
>
>Fair enough, when I was tinkering with the code I assumed b_address was
>an address (void*).
>
>But.... I still illustrates that using printf is more of a maintenance
>burden than iostreams.
>
>I should also put my hand up and say that I do use sprintf for small
>fixed format, fixed size strings (such as date and times). For example
>
>sprintf( tmp, "%04d%02d%02d%02d%02d%02dZ",
> tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
> tm.tm_hour, tm.tm_min, tm.tm_sec );
>
>is by far the most concise way to make an LDAP timestamp string.

1) You should _never_ use sprintf. snprintf is preferred (but I understand microsoft doesn't have it).
2) 'strftime' is much better for formatting timestamps.
3) I tried the cout stuff that was posted:

if (p_num_breakpoints > 0) {
std::cout << "Breakpoint Address Core Enabled?\n"
"---------- ---------------- ---- --------\n";

for (size_t b = 0; b < p_num_breakpoints; b++) {
const s_breakpoint *bpp = &p_breakpoints[b];
ostringstream core;
bpp->b_core == -1 ? core << "All" : core << setw(3) << bpp->b_core;
cout << setfill(' ') << setw(3) << ' ' // indent
<< setfill('0') << setw(2) << b
<< setfill(' ') << setw(8) << ' '
<< setfill('0') << setw(16)
<< hex << (bpp->b_address)
<< setfill(' ') << setw(2) << ' '
<< core.str().substr(0, 3) << ' '
<< setfill(' ') << setw(3) << ' '
<< boolalpha << static_cast<bool>(bpp->b_enabled)
<< '\n';
}

}

The results? My snprintf and calls to lp->log was a total of 0xe0 bytes of code and four callq's
while the cout version was

$ printf "0x%x\n" $(( 0x40b756 - 0x40ae04 ))
0x952 (more than 2k bytes - or 10 times more code footprint).

It also took 61 function calls versus the 4 function calls in my snprintf version,
including constructors and destructors for dynamic objects. Given that the snprintf
implementation in GLIBC is quite efficient, and that my log function simply writes the
char array to a file using the write(2) system call, the cout stuff is pretty pathetic
at a 10 to 1 code size factor.

There were also a handful of bus-locked (atomic) instructions generated inline, which
can be a significant problem in certain hardware environments and operating system kernels.

Thanks, i'll pass on the C++ I/O facilities in performance critical code. In fact, I'll probably
pass on them in all code, more maintainable or not :-)

As for exceptions, all the calls to

40a1b2: e8 e1 cb ff ff callq 406d98 <_Unwind_Resume@plt>

Are enough for me to turn off exceptions completely with -fno-exceptions, not to mention the
lack of unwind data when you don't have a runtime (e.g. in a hypevisor or hardware simulator,
where _every_ cycle matters).

scott
(tested on RHEL 6, gcc 4.4.6, -O2).

Adam Skutt

unread,
May 19, 2012, 12:15:37 AM5/19/12
to
On May 14, 6:28 pm, sc...@slp53.sl.home (Scott Lurndal) wrote:
> Ian Collins <ian-n...@hotmail.com> writes:
> >On 05/15/12 04:54 AM, Scott Lurndal wrote:
> >> Ian Collins<ian-n...@hotmail.com>  writes:
> >>> On 05/14/12 05:25 AM, Scott Lurndal wrote:
> >>>> Rui Maciel<rui.mac...@gmail.com>   writes:
> >>>>> Scott Lurndal wrote:
>  3) I tried the cout stuff that was posted:
> * snip*

> It also took 61 function calls versus the 4 function calls in my snprintf version,
> including constructors and destructors for dynamic objects.  Given that the snprintf
> implementation in GLIBC is quite efficient, and that my log function simply writes the
> char array to a file using the write(2) system call, the cout stuff is pretty pathetic
> at a 10 to 1 code size factor.
>
> There were also a handful of bus-locked (atomic) instructions generated inline, which
> can be a significant problem in certain hardware environments and operating system kernels.

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, so it's not like the
cost is without benefit. 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.

Adam

Adam Skutt

unread,
May 19, 2012, 12:29:01 AM5/19/12
to
On May 13, 10:55 am, BGB <cr88...@hotmail.com> wrote:
> 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

Adam Skutt

unread,
May 19, 2012, 12:46:55 AM5/19/12
to
On May 11, 5:03 pm, BGB <cr88...@hotmail.com> wrote:
> On 5/11/2012 11:55 AM, Marcel Müller wrote:
>
> > 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, especially in conjunction with
> > formatting.
>
> 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

Martin B.

unread,
May 19, 2012, 6:38:56 AM5/19/12
to
> 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

--
Good C++ code is better than good C code, but
bad C++ can be much, much worse than bad C code.

Adam Skutt

unread,
May 19, 2012, 8:21:23 AM5/19/12
to
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?

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

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
It is loading more messages.
0 new messages