Why C++ is vastly superior to C

I

Ian Collins

Op 21-May-11 21:48, Balog Pal schreef:

I never looked seriously at C99 but the last time I looked at C it
didn't have destructors, exceptions, templates or other constructs that
can generate surprising amounts of code if one is not careful.

No its doesn't. You just have to generate that very same code by hand!
Whether the compiler or the programmer generates the code is
irrelevant. If a job has to be done, code has to be generated to do it.
In the strict sense that can be said for just about any language, but
for some programming languages it is easier to predict what code will
generated than others. There are many shades of gray between black and
white.

The point "Nobody" was making is that C being such a low level language
the translation from source code to assembly is relatively
straightforward, so it is easier for a programmer to predict what kind
of code and how much code the compiler will produce by just looking at
the source code. In many cases this is a matter of no concern, but in
small embedded systems where every cycle counts and the amount memory is
expressed in Kilobytes or less rather than Mega- or Gigabytes you don't
want to have too much between you and the bare metal. In some cases C
might be the right compromise between assembly and C++.

That may have been true in the 90s, but it is no longer the case with
contemporary compilers. Have you ever looked at the generated assembler
for a highly optimised code module?
 
Ö

Öö Tiib

Op 21-May-11 21:48, Balog Pal schreef:



I never looked seriously at C99 but the last time I looked at C it
didn't have destructors, exceptions, templates or other constructs that
can generate surprising amounts of code if one is not careful.

C++ used wrongly is indeed a great tool for code obfuscation. For
example, like you seem to complain, some of C++ features allow to
generate implicit easter egg code that is hard to spot.

These features are indeed there but that does not mean you absolutely
have to use the features. On the contrary, most C++ coding standards
suggest not to use conversion operators, suggest to avoid implicit
conversion constructors, put lot of limitations to usage of exceptions
and so on.

For example in our team obfuscated C++ usually does not pass the code
review and author has to correct it.

I am not sure what is wrong with destructors and templates you claim.
Usage of templates has to be explicit and all classes do have
destructors so these can not be surprise.
 
B

Balog Pal

Kai-Uwe Bux said:
In C++, destructors and exceptions are the two things that carry a load of
implicitness with them. That it makes a real difference is illustrated by
the need of people getting used to it.

Hm, I'm reading C++ forums for nearly 2 decades, and know a shelfload of
stuff that needs "getting used to". i can't recall a single case someone
didn;t get the "destructor will be called on any kind of exiting scope"
part. And there is confusion around exceptions, but it is definitely not
the implicit code generated by the exception.
If it was "just the same", old habits
should carry over and work just fine.

IME people with good programming habits fare well with whatever
language/system. And those with bad habits mes up anything. Putting with C
they mess up miserably and need no help from C++. (buffer overruns? leaks?
race conditions? dongling pointers?)
he difficulty of learning to aquire
the habit of writing code that fares well in the presence of possible
exceptions goes to show that the difference is real.

Again, IME people who can could not grok exception safety did not write
usable code in C either -- it maybe the "straight" path worked to some
level, but errors were not checked, and/or the error handling code was
buggy.

And those who mastered the discipline to write correct C code (with no leaks
or UB on any condition) all could deal with exceptions -- and actually
preferred to use them whenever possible. Just to get rid of the extreme
source bloat handling the error conditions bring even to single functions --
let alone when you want to propagate upwards. What is a too common case.
 
B

Balog Pal

Dombo said:
I never looked seriously at C99 but the last time I looked at C it didn't
have destructors, exceptions, templates or other constructs that can
generate surprising amounts of code if one is not careful.

For some reason claims about amount of code in forums generally come from
people who never bothered to look at assy. Just like the famous priest back
in middle age who knew better that Jupiter can not have moons. Please do
some experiments then come back with results, and we may have a fruitful
discussion.
In the strict sense that can be said for just about any language, but for
some programming languages it is easier to predict what code will
generated than others. There are many shades of gray between black and
white.

Probably so, but I keep the claim that destructor code hardly surprises any
competent C programmer.
With implicit converting ctors and operators you indeed can create a mess,
but the base guidelines say not to use them. And when used they are
hopefully well designed. (And deliberate bad design is not worth
discussing, as C has too many elements that are can be broken.)
The point "Nobody" was making is that C being such a low level language
the translation from source code to assembly is relatively
straightforward, so it is easier for a programmer to predict what kind of
code and how much code the compiler will produce by just looking at the
source code.

I never had problem predicting the code in either language. My experience
really show no practical difference. I mean if said prediction happens in
the correct context, as comparing it to the *necessary* code to have a
desired behavior.
In many cases this is a matter of no concern, but in small embedded
systems where every cycle counts and the amount memory is expressed in
Kilobytes or less rather than Mega- or Gigabytes you don't want to have
too much between you and the bare metal.

Another interesting observation from forums, that claims about embedded
systems too often come from people never working on any. I for example did
embedded stuff in both languages. Exceptions are normally turned off, while
dtors and templates are welcome. And they create the same code you would by
hand. And when you're hit by a case you need to do assembly, it is the
same in C.
In some cases C might be the right compromise between assembly and C++.

The only cases I experienced were "no C++ compiler" and "C++ compiler way
too expensive".

When Bjarne created C++ the design foundation had both "C compatibility" and
"no pay for what is not used". And those fairly succeeded.
 
M

MikeP

Balog said:
Type in bullshit at the same place to get an even longer list... Care
to explain your own assertion, especially what bloat is created
compared to what alternative executing the same task?

Or you're just creating flood to have more posts i this thread than
the rest together?

I can only lead you to the water. I can't make you drink it. Suit
yourself.
 
M

MikeP

Keith said:
Keith said:
[...] I would say that
the C++ automatic constructor/destructor paradigm (which was a C++
innovation as far as I know?) is also extremely valuable.

Innovation? Looks like a quite nice and convenient side-effect.

Side-effect of what?

Of the quest to make user-defined types behave like built-in types.
 
M

MikeP

Stefan said:
Why is it »clouding«?


If terminology is missing for something that already exists,
the terminology can be introduced. Would you mind to suggest
some of this terminology?

Actually, I was hoping someone else would after thinking about it so I
could fit that info in with my own thoughts on the matter. Seems like a
good question for "the man" himself! How about it Bjarne, too busy? It's
not a major thing, but maybe something more than just trivia.
 
M

MikeP

Dombo said:
Op 21-May-11 16:08, MikeP schreef:

Several C++ compilers I have used in the early nineties didn't support
exceptions at all, yet they did support constructors and destructors.
In those days classes often had an initialize method or a method to
check if the object was successfully constructed; not particularly
elegant but at the end of the day it did get the job done.

There certainly is a strong synergy between exceptions and RAII in
C++, the C++ language would have been a lot worse without one or the
other. But without exceptions RAII would be still useful, whereas
exceptions would be useless in C++ without RAII (unless something
like a 'finally' clause were added to the language).

Can there be "synergy" where there is a dependency? Doesn't the
dependency make it (exceptions + RAII) a singularity rather than
plurality?
 
J

Juha Nieminen

MikeP said:
I can only lead you to the water. I can't make you drink it. Suit
yourself.

I still don't understand what this "template code bloat" is.

Does templated code require more code than non-templated code?
Well, if we compare it to *one* non-templated function or class, there
indeed is at least one additional line of code (the "template" part).
Also, every member function of a template class, if not implemented in
the class declaration, will require the additional "template" line as
well. Also, there will be additional "<type>" specifications. This indeed
increases the amount of code somewhat, but not very radically.

However, if we compare the *generated* code with the equivalent
non-templated versions, the amount of code is immediately smaller in
the templated version, rather obviously, because there will be no
code repetition. A non-templated function or class will have to be
duplicated for each type. This would indeed be code bloat. In fact,
templates are helping *reduce* code bloat, not increase it. So I don't
really understand your point.

Or are you talking about the executable binary? Do you mean that the
end result ends up significantly larger than when not using templates?
My question would be: Compared to what?

If you use a templated function or class with one type, the generated
code will be pretty much the same as if a non-templated version of that
function or class had been used with that specific type. I don't see how
the executable binary would get any larger. (If the binary includes
debugging information then it may be larger because mangled template
names are usually larger than non-templated ones. However, that would be
a pretty silly argument because the debug information is there for a
debug build of the project, but not intended for the final distribution
build.)

If you use the same templated function or class with two types, the
code will be duplicated (although optimized for each type individually,
possibly resulting in a smaller result for one type than the other).
What would be a non-templated alternative? Most obviously, explicitly
duplicating the source code for each of the two types. The resulting
binary would still be pretty much the same as with the templated version,
so exactly what would be the difference?

In some situations you could try to make a (somewhat) generic but
non-templated implementation, as is typical in C. Almost invariably
this will introduce inefficiency and lack of type-safety compared to
the templated version (see qsort() for a practical example). Rather
ironically, in some situations it might even produce a *larger* binary
than the templated version (because the templated version can usually
be inlined by the compiler, possibly optimizing entire function calls
away and tightly optimizing and packing their code at the calling site,
embedded and merged with the surrounding code).

Nevertheless, even in situations where heavily templated code truly
generates a larger executable binary by a significant margin compared to
some non-templated alternative, is it really that catastrophic? Maybe in
some embedded systems with very little RAM it could be an issue, but the
vast majority of C++ code out there is not made for such systems. In a
typical desktop machine it makes little difference whether the executable
binary is 1 MB or 1.5 MB in size (assuming the templated code makes that
much of a difference).

It is still unclear to me what exactly is it that you mean by "template
code bloat".
 
J

Joshua Maurice

  I still don't understand what this "template code bloat" is.

Imagine you have a container, such as std::vector. There are two basic
ways to implement it, as C++ std::vector, or as you have to in C with
void pointers, or as you would in Java with their equivalent of
pointers eg: ArrayList.

It's a tradeoff. With the C approach (equivalently
java.util.ArrayList), there is one piece of assembly corresponding to
that function for all possible instantiations of the container class
and for all possible contained types. With the std::vector approach,
you can have a different piece of assembly in the object file for each
different type used to instantiate the template. That is,
std::vector<int> and std::vector<char> can (and usually will) be
compiled down to different functions in the object file, distinguished
by name mangling. Thus, the template way results in less instructions
executed compared to the void pointer approach, but it results in
larger executables.

The tradeoff is do you want smaller executables, or less instructions
implemented. The decision is particulars specific, and I'm not talking
about hard memory constraints. I'm talking about execution time.
Larger executables generally mean that your cache hit rate will be
smaller, possibly dwarfing the benefit of less instructions executed,
resulting in a slower running executable.
 
K

Kai-Uwe Bux

Balog said:
Hm, I'm reading C++ forums for nearly 2 decades, and know a shelfload of
stuff that needs "getting used to". i can't recall a single case someone
didn;t get the "destructor will be called on any kind of exiting scope"
part.
[...]

You snipped too much context. Also, it seems as though you are shifting the
question under discussion. The question was whether the difference of
explicitness and implicitness "is real" (a vague term). I don't think the
difference, although real, has to manifest itself in items that some people
just don't get. Of course, both languages can be learned and understood
(like German and Japaneese). Still there can be real differences (like with
German and Japaneese).


Upthread, you asked the question in the first place:
Is there any real difference?

Since you chose the term, maybe you could _define_ "real difference"?

The way, I understand it, the difference between explicit and implicit is
real. But it seems, your understanding is different.


Best,

Kai-Uwe Bux
 
B

Balog Pal

"Kai-Uwe Bux" <[email protected]>

[quotes rearranged!]
Upthread, you asked the question in the first place:

Seem like poor choice of words on my part. I meant something like real
*practical* difference. A difference that leads to actual better/worse
software, more/less planted bugs etc.
Hm, I'm reading C++ forums for nearly 2 decades, and know a shelfload of
stuff that needs "getting used to". i can't recall a single case someone
didn;t get the "destructor will be called on any kind of exiting scope"
part.
[...]

You snipped too much context. Also, it seems as though you are shifting
the
question under discussion. The question was whether the difference of
explicitness and implicitness "is real" (a vague term).

I was following my own thoughts ;-) In the abstract terms, certainly
explicit is "really" different from implicit. I don't see too productive to
discuss sophistry. I'm interested in implications on the real world.
I don't think the
difference, although real, has to manifest itself in items that some
people
just don't get. Of course, both languages can be learned and understood
(like German and Japaneese). Still there can be real differences (like
with
German and Japaneese).

After clearing up the misunderstanding, could we follow the practical chain?
 
K

Kai-Uwe Bux

Balog said:
"Kai-Uwe Bux" <[email protected]>

[quotes rearranged!]
Upthread, you asked the question in the first place:

Seem like poor choice of words on my part. I meant something like real
*practical* difference. A difference that leads to actual better/worse
software, more/less planted bugs etc.
[...]

After clearing up the misunderstanding, could we follow the practical
chain?

Certainly.

In terms of likelyhood of bugs, I think it's not so much a difference in
total number of bugs per line but a difference in distribution. The error
patterns will be different. The implicit coding style can help to avoid many
bugs. If you put clean up code in the destructor, it will be very hard to
_not_ execute it at the appropriate point. Exceptions in templated code, on
the other hand, can lurk at places where you don't espect them diverting
control flow to a point determined by the type of the exception thrown (and
within a template, that can be an unknown type, whence the target of the
jump is unknown, too). On the other hand, not seeing those jumps written
explicitly, can improve readability of the code; and having client code
determine the target of the jump even allows for greater flexibility than
goto -- whether that is good or bad, I guess, depends on the perspective.

In terms of better/worse software, I do not see any evidence either way. It
seems that other factors are much more decisive. In the end, it's not the
language alone but a tupel

(group of developers, languages used, toolchain, ...)

that influences overall quality of the result. This is not to say that
changing a single element in the tupel does not have an effect. But in one
setting going from C to C++ can boost the quality and in another setting it
can make things worse.


Upshot:
=======
It think, for any particular project, there is a practical difference in
choosing C++ or C or (insert other language here). Making the right choice
will improve work flow and quality of the developed program. However, what
the "right choice" is depends on many factors and the "right choice" is not
uniform.

I am not sure whether that qualifies as a "practical difference" in the
sense of your question. But surely, choosing C++ or C is a consideration of
practical importance. I would, however, venture the following conjecture:
whether the explicit/implicit distinction will have any seizable weight in
choosing the language probably depends more on the developers involved than
the task at hand.


Best,

Kai-Uwe Bux
 
R

Rui Maciel

Balog said:
Is there any real difference? C was created as a "portable assembler".
You lost the "what you see" attribute right with that step.

By design certainly, that was the point -- you write code using abstract
types like int, short, pointers, "arrays", and the approppriate assembly
code is generated for the platform.

Can you tell what machine instructions you will get from a C source? For
some level, yes: if you know the semantics.

But the very same is true for C++.

No one may be 100% sure on how a C source will translate to opcode. The
same also applies to C++. Yet, while the case with C is limited to how a
set of C instructions is translated to opcode, the case with C++ also
involves how the C++ code is used to generate other language constructs
and subsequently how those language constructs are translated to opcode.
Therefore, with C++ you have to deal with an added, opaque abstraction
layer which no one actually has any control over. This is a real
difference.


Rui Maciel
 
R

Rui Maciel

Öö Tiib said:
C++ used wrongly is indeed a great tool for code obfuscation. For
example, like you seem to complain, some of C++ features allow to
generate implicit easter egg code that is hard to spot.

These features are indeed there but that does not mean you absolutely
have to use the features. On the contrary, most C++ coding standards
suggest not to use conversion operators, suggest to avoid implicit
conversion constructors, put lot of limitations to usage of exceptions
and so on.

For example in our team obfuscated C++ usually does not pass the code
review and author has to correct it.

I am not sure what is wrong with destructors and templates you claim.
Usage of templates has to be explicit and all classes do have
destructors so these can not be surprise.

As I see it, the point is that, in this context, while with C you only
need to invest your time researching how to generate code from your source
code, with C++ you are forced to research how to generate code from your
source code and, more importantly, how to avoid code being generated for
you from your source code. Although this isn't necessarily a big concern
in many applications, when it is then it's quite possible that C++
generates more work than it saves, or at least requires a greater level of
expertise than that which would otherwise be required.


Rui Maciel
 
S

Stefan Ram

Juha Nieminen said:
Nevertheless, even in situations where heavily templated code truly
generates a larger executable binary by a significant margin compared to
some non-templated alternative, is it really that catastrophic? Maybe in
some embedded systems with very little RAM it could be an issue, but the
vast majority of C++ code out there is not made for such systems. In a
typical desktop machine it makes little difference whether the executable
binary is 1 MB or 1.5 MB in size (assuming the templated code makes that
much of a difference).

When code is smaller, the chance is greater that it might fit in some
cache, which will make it execute much faster.
 
D

Dombo

Op 22-May-11 6:55, MikeP schreef:
Can there be "synergy" where there is a dependency? Doesn't the
dependency make it (exceptions + RAII) a singularity rather than
plurality?

I have used C++ compilers which supported RAII but not exceptions (and
even today you can disable exception support without disabling RAII on
several C++ compilers), there are also quite a few programming languages
which support exceptions but no RAII. So as far as 'm concerned RAII and
exceptions are not mutually dependent in the sense that you cannot have
one without the other.
 
M

MikeP

Dombo said:
Op 22-May-11 6:55, MikeP schreef:

I have used C++ compilers which supported RAII but not exceptions (and
even today you can disable exception support without disabling RAII on
several C++ compilers), there are also quite a few programming
languages which support exceptions but no RAII. So as far as 'm
concerned RAII and exceptions are not mutually dependent in the sense
that you cannot have one without the other.

I was asking [the group] in general about "synergy". But on the C++
topic, I was suggesting that C++ exceptions (not exceptions in general)
depend on RAII. A one-way dependency rather than a mutual one.
 
I

Ian Collins

I was asking [the group] in general about "synergy". But on the C++
topic, I was suggesting that C++ exceptions (not exceptions in general)
depend on RAII. A one-way dependency rather than a mutual one.

C++ exceptions depends on destructors. Without destructors, some form
of finally block is required.

RAII also depends on destructors.
 
M

MikeP

Ian said:
I was asking [the group] in general about "synergy". But on the C++
topic, I was suggesting that C++ exceptions (not exceptions in
general) depend on RAII. A one-way dependency rather than a mutual
one.

C++ exceptions depends on destructors.

Destructors that DO SOMETHING (RAII and other cleanup). Yes, the
mechanics are related also, but the language (semantics) was really the
point (I think!).
Without destructors, some form
of finally block is required.

RAII also depends on destructors.

So what's your take on the synergy thing? Is there any in this case? Can
there be synergy where there is dependency?
 

Ask a Question

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

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

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,175
Latest member
Vinay Kumar_ Nevatia
Top