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

C++ Frequently Questioned Answers

783 views
Skip to first unread message

Yossi Kreinin

unread,
Oct 26, 2007, 4:27:19 AM10/26/07
to
The C++ FQA Lite is available here: http://yosefk.com/c++fqa
It's based on the comp.lang.c++ FAQ - http://parashift.com/c++-faq-lite

While certainly not off-topic for comp.lang.c++[.moderated], the FQA
can make a flame bait. Apparently, a good way to prevent flame wars is
to have the discussion in a moderated newsgroup (I have a recent
experience with a weakly moderated forum, and it was suboptimal). You
can also e-mail me privately (if you're human, you'll find the address
at the site). One thing I'm definitely interested in is whether I got
any facts wrong, and if I did, which ones.

The FQA is fairly large (about half the size of the FAQ). The
"summary" pages are:

http://yosefk.com/c++fqa/defective.html
http://yosefk.com/c++fqa/picture.html

Reading the disclaimers can prevent some of the non-technical replies:

http://yosefk.com/c++fqa/disclaimers.html

-- Yossi


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Alex Shulgin

unread,
Oct 28, 2007, 2:01:52 PM10/28/07
to
Yossi Kreinin wrote:
> The C++ FQA Lite is available here: http://yosefk.com/c++fqa
> It's based on the comp.lang.c++ FAQ - http://parashift.com/c++-faq-lite
>
> While certainly not off-topic for comp.lang.c++[.moderated], the FQA
> can make a flame bait. Apparently, a good way to prevent flame wars is
> to have the discussion in a moderated newsgroup (I have a recent
> experience with a weakly moderated forum, and it was suboptimal). You
> can also e-mail me privately (if you're human, you'll find the address
> at the site). One thing I'm definitely interested in is whether I got
> any facts wrong, and if I did, which ones.

DISCLAIMER: I haven't read all of your FQA--there is just too many of
them.

Although it seems you did a fair amount of research, to me your post
is not much more than a very sophisticated way of saying "C++ SUCKS"
on a moderated C++ newsgroup. :-)

>From your words I can guess that you have had pretty weird experience
with C++ on a large project... and somehow you have expected much of
the "features" the language have never declared to have: garbage
collection, built-in run-time checks, reflection, etc; Features one
would normally expect from a _very_ high level language like Python,
Ruby, Lisp, etc; but not C++.

So basically, instead of blaming yourself (or whoever is responsible)
for poor language/task choice you try to blame C++... Duh!

To be more specific, from http://yosefk.com/c++fqa/defective.html:

"No compile time encapsulation"

This item implies that you have to ship the implementation class
declaration to clients, which is not necessarily true. Have you ever
heard about abstract classes ("interfaces")? (Obviously, this won't
work with templates, but you have to provide the definitions in that
case too.)

"Outstandingly complicated grammar"

Maybe, you have your point here.

"No run time encapsulation"

See above: no built-in run-time checks.

"No binary implementation rules"

Frankly, I don't see your point here. You have tried to link g++-
compiled libraries with msvc-compiled code, or what exactly?

"Very complicated type system"

I'll quote that a bit to bring in some context:

> For example, if your function accepts const std::vector<const
> char*>& (which is supposed to mean "a reference to an immutable
> vector of pointers to immutable built-in strings"), and I have a
> std::vector<char*> object ("a mutable vector of mutable built-in
> strings"), then I can't pass it to your function because the types
> aren't convertible. You have to admit that it doesn't make any
> sense, because your function guarantees that it won't change
> anything, and I guarantee that I don't even mind having anything
> changed, and still the C++ type system gets in the way and the only
> sane workaround is to copy the vector.

Not really. You can rewrite your function into a function template
which will accept a range instead of `std::vector< whatever >&':

template< typename Iter > void func2(Iter begin, Iter end);

Of course, you loose that prominent `const*' in declaration, but as
long as implementation complies with the given pact (not to modify
whatever iterators "point to"), I think it's pretty OK.

Anyway, I don't recall myself applying cv-qualifiers to the type of
contained elements in a sensible and useful way. Anyone?

"Defective exceptions"

> ... Exception safe C++ code is almost infeasible to achieve in a non-trivial program.

Maybe, if you prefer to write everything from scratch instead of
trying to stick to standard library. Personally, I have never had
that kind of problem.


Now... I'm kinda tired writing all that stuff. Do you want me to
continue?


--
Kind regards,
Alex

Walter Bright

unread,
Oct 28, 2007, 9:26:22 PM10/28/07
to
Alex Shulgin wrote:
> From your words I can guess that you have had pretty weird experience
> with C++ on a large project... and somehow you have expected much of
> the "features" the language have never declared to have: garbage
> collection, built-in run-time checks, reflection, etc; Features one
> would normally expect from a _very_ high level language like Python,
> Ruby, Lisp, etc; but not C++.

It is not necessary to have a very high level language to have these
features (after all, Pascal had runtime checks decades ago). C++ does
have some compile time reflection (sizeof, offsetof, and the various
traits templates), and some runtime reflection (dynamic_cast, rtti).

> So basically, instead of blaming yourself (or whoever is responsible)
> for poor language/task choice you try to blame C++... Duh!

While it's pretty obvious that Standard C++ doesn't have gc, many of the
issues with C++ don't become apparent until you're fairly skilled with
it. The lack of transitive const comes to mind.

> "No binary implementation rules"
>
> Frankly, I don't see your point here. You have tried to link g++-
> compiled libraries with msvc-compiled code, or what exactly?

I think he means ABI, Application Binary Interface. While a lack of this
allows C++ compiler vendors to innovate with it (and they have done so),
the downside is that objects compiled with one compiler cannot be linked
with objects compiled with another compiler.

> "Defective exceptions"
>
>> ... Exception safe C++ code is almost infeasible to achieve in a non-trivial program.
>
> Maybe, if you prefer to write everything from scratch instead of
> trying to stick to standard library. Personally, I have never had
> that kind of problem.

Exception safety isn't hard to achieve with RAII techniques if the
things that must be unwound are independent, or nested. Achieving
exception safety where A, B, C, and D must either all happen or none
happen is much more difficult.

For more information:

http://www.digitalmars.com/d/exception-safe.html

http://www.ddj.com/cpp/184403758

"Item 29: Strive for exception-safe code" in Effective C++ Third
Edition, pg. 127 by Scott Meyers

-----
Walter Bright
http://www.digitalmars.com
Digital Mars C, C++, D programming language compilers

Alf P. Steinbach

unread,
Oct 28, 2007, 9:22:37 PM10/28/07
to
* Yossi Kreinin:

> The C++ FQA Lite is available here: http://yosefk.com/c++fqa
> It's based on the comp.lang.c++ FAQ - http://parashift.com/c++-faq-lite

A Frequently Questioned Answers is a very good idea, and I commend the
effort you've put into this.


> While certainly not off-topic for comp.lang.c++[.moderated], the FQA
> can make a flame bait. Apparently, a good way to prevent flame wars is
> to have the discussion in a moderated newsgroup (I have a recent
> experience with a weakly moderated forum, and it was suboptimal). You
> can also e-mail me privately (if you're human, you'll find the address
> at the site). One thing I'm definitely interested in is whether I got
> any facts wrong, and if I did, which ones.

Well, most of it. ;-)

One main high level issue that the FAQ gets wrong is the purpose of a
programming language. In general it's a good idea to use the most
appropriate language for a task, but your FQA seems to assume that
somehow everything has to be done in C++. OK, it can be done, and more
to the point it is often done, but it's just stupid to use C++ for
everything (as your FQA implicitly points out a thousand times or more),
and that's not a fault of the language: it's a fault of the user.

One main fact the FQA got wrong is that C++ doesn't have garbage
collection, in the sense that it doesn't support garbage collection.
Garbage collectors such as the Boehm collector work well. However, for
many C++ programmers garbage collection is not desirable, because while
it can ensure that memory is always reclaimed, it leads to other problems.


>From the FQA:
> And what if you make a mistake, one single mistake with all these deletions?
> Either you'll run out of memory, or your program will crash, or it will
> corrupt its own data. Which is why many experienced C++ programmers do
> everything to avoid explicit calls to new. Instead, they use RAII - have some
> constructor do the new and the destructor do the delete. Which eventually
> leads to lots of unnecessary copying - you can't point to some data inside
> a data structure since the data structure may be about to die, and it will
> kill all its data whether or not someone points to it. So you have to make a
> copy of that data, and keep believing that manual memory management is what
> makes your C++ programs so fast.

Note that the FAQ argument here is that in some other language you are
encouraged to access invalid data, "zombie" objects that have been
logically destroyed but live on, and that somehow that is a good thing.

Good C++ programmers generally think it's not a good thing, because
correctness is most important.

They think it's bad, and use RAII to avoid it. The point being that
with proper RAII, if an object is still alive, then its class invariant
still holds. And that that class invariant is a strong one giving very
practically useful guarantees, not a watered-down one "this object
either provides the following guarantees, or perhaps not, depending on
zombiness level" suitable for gc zombie objects.


Cheers, & hth.,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Eugene Gershnik

unread,
Oct 29, 2007, 5:00:22 PM10/29/07
to
Alex Shulgin wrote:
>
> "Very complicated type system"
>
> I'll quote that a bit to bring in some context:
>
>> For example, if your function accepts const std::vector<const
>> char*>& (which is supposed to mean "a reference to an immutable
>> vector of pointers to immutable built-in strings"), and I have a
>> std::vector<char*> object ("a mutable vector of mutable built-in
>> strings"), then I can't pass it to your function because the types
>> aren't convertible. You have to admit that it doesn't make any
>> sense, because your function guarantees that it won't change
>> anything, and I guarantee that I don't even mind having anything
>> changed, and still the C++ type system gets in the way and the only
>> sane workaround is to copy the vector.

The interesting thing is the OP chooses to criticise something that is much
harder or impossible to do in languages he seems to prefer. How do you say
'immutable Foo' in Java or C#? You can't and are left with coding guidelines
like 'make your classes [always] immutable' (see for example Effective Java
book) which are there for a good reason. The same reason C++ has const in
the language.

When you do try to make things immutable but still need to modify them from
time to time you hit exactly the same problem as in his C++ example. There
are three known ways of having 'immutable Foo' in popular languages.

One is to have two distinct classes, like Java's String and StringBuffer.
One of them is immutable, another is not and they can be freely converted to
each other. This is the 'recommended' approach but if you do this you will
quickly discover that you can't pass List<StringBuffer> to a function
expecting List<String>. Just like in C++ only worse becasue there is no
solution to it.

Another approach is to have something like (in C++ notation)

struct ImmutableFoo
{
virtual int GetValue() = 0;
};

struct MutableFoo : ImmutableFoo
{
virtual void SetValue(int val) = 0;
};

struct Foo : MutableFoo
{
int GetValue();
void SetValue(int val);
};

Unfortunately, you still cannot pass List<Foo> to a function that expectes
List<ImmutableFoo> (for the same reason arrays are not polymorfic in C++).
However, now it is possible to do something about this and the solution is
along the lines suggested for C++. You make the function a generic that
accepts List<anything derived from ImmutableFoo>.
This approache to constness sort of works but badly. It is intrusive,
doesn't scale well (what if Foo concept itself is polymorphic?) and so is
rarely used.

Finally you can do what Java collections do, namely provide a wrapper that
fails mutating methods at runtime. Something like (in C++ notation again)

struct Foo
{
virtual int GetValue() = 0;
virtual void SetValue(int val) = 0;
};

struct FooImpl : Foo
{
int GetValue();
void SetValue(int val);
};

struct ImmutableFoo : Foo
{

int GetValue()
{ return impl.GetValue(); }

void SetValue(int val)
{ throw some_exception(); }

Foo impl;
};


This "works" since you will only have List<Foo> as the parameter to any
function. The price you pay is detecting violations of const correctness
only at runtime.

Now which language's type system makes things more complicated?

--
Eugene

Alex Shulgin

unread,
Oct 29, 2007, 5:02:10 PM10/29/07
to
On Oct 29, 4:26 am, Walter Bright <wal...@digitalmars-nospamm.com>
wrote:
> Alex Shulgin wrote:
[snip]

> >> ... Exception safe C++ code is almost infeasible to achieve in a non-trivial program.
>
> > Maybe, if you prefer to write everything from scratch instead of
> > trying to stick to standard library. Personally, I have never had
> > that kind of problem.
>
> Exception safety isn't hard to achieve with RAII techniques if the
> things that must be unwound are independent, or nested. Achieving
> exception safety where A, B, C, and D must either all happen or none
> happen is much more difficult.
>
> For more information:
>
> http://www.digitalmars.com/d/exception-safe.html
>
> http://www.ddj.com/cpp/184403758
>
> "Item 29: Strive for exception-safe code" in Effective C++ Third
> Edition, pg. 127 by Scott Meyers

So we are talking about transactions... Anyway I would expect
transactional programming to be hard to get right in any language
which does not provide standard means for this.

Nice links. :-)


--
Regards,
Alex

David Abrahams

unread,
Oct 29, 2007, 5:41:22 PM10/29/07
to

on Sun Oct 28 2007, "Alf P. Steinbach" <alfps-AT-start.no> wrote:

> * Yossi Kreinin:
>> The C++ FQA Lite is available here: http://yosefk.com/c++fqa
>> It's based on the comp.lang.c++ FAQ - http://parashift.com/c++-faq-lite
>
> A Frequently Questioned Answers is a very good idea, and I commend the
> effort you've put into this.
>
>
>> While certainly not off-topic for comp.lang.c++[.moderated], the FQA
>> can make a flame bait. Apparently, a good way to prevent flame wars is
>> to have the discussion in a moderated newsgroup (I have a recent
>> experience with a weakly moderated forum, and it was suboptimal). You
>> can also e-mail me privately (if you're human, you'll find the address
>> at the site). One thing I'm definitely interested in is whether I got
>> any facts wrong, and if I did, which ones.
>
> Well, most of it. ;-)

Frankly, I was surprised at how many things the "FAQ Lite" gets
wrong, making it an extremely easy target. For example,


[35.6] What is "genericity"?

Yet another way to say, "class templates."

http://parashift.com/c++-faq-lite/templates.html#faq-35.6

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

ap...@student.open.ac.uk

unread,
Oct 30, 2007, 8:41:41 AM10/30/07
to
On 26 Oct, 08:27, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
> The C++ FQA Lite is available here:http://yosefk.com/c++fqa
> It's based on the comp.lang.c++ FAQ -http://parashift.com/c++-faq-lite

>
> While certainly not off-topic for comp.lang.c++[.moderated], the FQA
> can make a flame bait. Apparently, a good way to prevent flame wars is
> to have the discussion in a moderated newsgroup (I have a recent
> experience with a weakly moderated forum, and it was suboptimal). You
> can also e-mail me privately (if you're human, you'll find the address
> at the site). One thing I'm definitely interested in is whether I got
> any facts wrong, and if I did, which ones.
>
> The FQA is fairly large (about half the size of the FAQ).

Yes it is. In order to make it easier to navigate could you put fwd
and backward arrows on each page please? A top button would be useful
as well.

I realise that alot of work has gone into this so please don't take
this the wrong way: after reading for a bit it seems to me like a long
rant against C++. I think many of the criticisms are valid but are
phrased in an aggressive way. It makes it appear the people
responsible for C++ are stupid or irresponsible, which IMO is not the
case.

Would you be ammenable to suggestions from this NG that would tone
some of the entries down a bit?

-Andrew Marlow

Yossi Kreinin

unread,
Oct 30, 2007, 3:51:17 PM10/30/07
to
On Oct 30, 2:41 pm, ap...@student.open.ac.uk wrote:
> I think many of the criticisms are valid but are
> phrased in an aggressive way. It makes it appear the people
> responsible for C++ are stupid or irresponsible, which IMO is not the
> case.

About "aggression": http://yosefk.com/c++fqa/faq.html#faq-6
As the disclaimers section states, I didn't want to personally offend
anybody.

>
> Would you be ammenable to suggestions from this NG that would tone
> some of the entries down a bit?
>

Definitely, if you can point out the places which make actual people
appear stupid or make any derogatory statements (maybe e-mail is a
better way to talk about the details). I will probably not remove the
claims about how marketing forces shaped C++ (which I think are the
fiercely phrased ones), because of quotes like this (http://
www.research.att.com/~bs/dne.html):

"Within C++, there is a much smaller and cleaner language struggling
to get out," which he says "would ... have been an unimportant cult
language."

.../and/ of course because of quotes like this (http://
www.research.att.com/~bs/bs_faq.html#C++success):

"the chance of success was affected by marketing clout, which I did
not have."

P.S. I'll try to answer the rest of the meaningful criticism in the
evening (I've quit all online discussions in non-moderated newsgroups
though). I'm publishing factual corrections at this page (which also
has my definition of "factual" in this context):

http://yosefk.com/c++fqa/web-vs-fqa.html

If you want to get a reply from me, e-mail is the most effective way:
http://yosefk.com

-- Yossi

LR

unread,
Oct 30, 2007, 9:58:23 PM10/30/07
to
Yossi Kreinin wrote:
> The C++ FQA Lite is available here: http://yosefk.com/c++fqa

I haven't read the whole thing, but what I have read seems very interesting.

But I'm not quite sure that I understand the point you're trying to make.

The FQA contains at least one FAQ (in the literal sense, rather than the
sense that FAQ is used for Answers to Frequently Asked Questions), in
FQA 23.6 http://yosefk.com/c++fqa/inheritance-mother.html#fqa-23.6
"How about trying another programming language?"

Could this be what you're driving at? If so, what do you have in mind?

LR

Yossi Kreinin

unread,
Oct 31, 2007, 12:39:37 PM10/31/07
to
On Oct 29, 11:00 pm, "Eugene Gershnik" <gersh...@hotmail.com> wrote:
>
> When you do try to make things immutable but still need to modify them from
> time to time you hit exactly the same problem as in his C++ example.

Some time after I wrote about the problems with const, which tended to
get in my way when I had classes with member pointers, I learned that
apparently D improved const. I didn't know about a language that did
it before that.

However, I don't think it's very important to get const right. The
ultimately important thing is usability. From my experience, a
language without const is sure usable. Possibly, having a working
const in the language can be even better. But having the C++ kind of
const, which breaks when you have levels of indirection, is suboptimal
in terms of usability - you never get the typing quite right, and the
compiler keeps complaining. And avoiding const will not work well,
either (you won't be able to do things like func(ClassName(args)), for
example, and if you need libraries that use const, it will get worse):

http://yosefk.com/c++fqa/const.html#fqa-18.1

One counter argument is that the C++ approach still allows for more
compile time checking compared to having no const at all. I think that
compile time checking is overrated in this case compared to the
usability problems, including the clarity of error messages (consider
the ones you get if you say "map::iterator p=m.begin()" when m is a
const map). There are things you'd obviously not try to represent in a
static type system (such as subsets of natural numbers - "this
argument must be an even number, this one must be a prime number, and
this one must be a number representing a Turing machine that halts").
I think that "mutability" gets close to the edge here (I'm sincerely
not sure at which side of the edge it is); again, I didn't try to work
with "better const", only with the C++ kind.

As to the idea to convert void func(const std::vector<const T*>&) to
template<class It> void func(It b, It e): this forces me to make func
a template, which moves the source code to header files slowing down
the build and further complicates the error messages, etc., opening a
can of warms unrelated to this sub-thread. I'm aware of this option
and find it completely unacceptable. As to the idea to avoid the const
qualification of vector the element type - this may work, or it may
end with a const_cast somewhere else, since you might fill the vector
with elements which you get from functions returning const objects.


--

Maciej Sobczak

unread,
Oct 31, 2007, 7:36:20 PM10/31/07
to
On 31 Pa , 17:39, Yossi Kreinin <yossi.krei...@gmail.com> wrote:

> However, I don't think it's very important to get const right. The
> ultimately important thing is usability. From my experience, a
> language without const is sure usable.

>From some other exprience, a language without any static type system
is also surely usable. Should we criticize those that have a static
type system?

> One counter argument is that the C++ approach still allows for more
> compile time checking compared to having no const at all.

I have found recently (or better: I have came to appreciate) that
constness is essential for code clarity - to the point that I would
even endorse having const as a default (!) with some 'variable'
modifier (or just 'mutable' as an existing keyword) or such to mark
those objects that are actually allowed to change.

Difference in expectations.

> I think that
> compile time checking is overrated in this case compared to the
> usability problems, including the clarity of error messages

The clarity of error message has nothing to do with whether constness
is a good idea or not. It's a quality of implementation issue. You can
say that 100% of compilers are crappy when it comes to error messages
(and you might be surprised how many of C++ programmers would actually
agree), but that is still far from saying that the language itself
sucks.

> There are things you'd obviously not try to represent in a
> static type system

> I think that "mutability" gets close to the edge here

Mutability is one of the simplest things we can assert about any given
object. Much simpler than, say, type substitution. Why not using the
type system for such a simple thing?

> again, I didn't try to work
> with "better const", only with the C++ kind.

Try the Ada one.
No, it's not "better".


> As to the idea to convert void func(const std::vector<const T*>&) to
> template<class It> void func(It b, It e): this forces me to make func
> a template

It might only contain a loop that calls void funcElem(const T *) for
each iterator value in the range, and funcElem(const T *) itself need
not be templated.


void funcElem(const T * p) { ... } // not a template!

// only this is a template
template <class Container>
void func(const Container & c)
{
for_each(c.begin(), c.end(), funcElem);
}

And then:

vector<T *> vp;
vector<const T *> vcp;
// ...
func(vp); // OK
func(vcp); // OK


I appreciate the amount of work that you have put into gathering all C+
+ criticism in a single place, but I'm afraid you got into the trap of
being focused on problems instead of solutions.

--
Maciej Sobczak * www.msobczak.com * www.inspirel.com

Eugene Gershnik

unread,
Oct 31, 2007, 7:35:59 PM10/31/07
to
Yossi Kreinin wrote:

> I learned that
> apparently D improved const.

How? If you know please post it here. If not this is a meaningless statement
in a technical forum.

> However, I don't think it's very important to get const right. The
> ultimately important thing is usability.

I don't know what "usability" is for you and how you define it. If you can
provide a good measurable definition please provide it.
Otherwise I can and do claim that const imporves 'usablity' as I understand
it.

What I do know is that a language should allow one to express design
concepts as clearly as possible. There is an exteremely important concept
like 'this thing is supposed to be logically unchanged' in any design. One
ought to be able to express it at the language level.

> From my experience, a
> language without const is sure usable.

For *your* definition of usable and from *your* experience.

> But having the C++ kind of
> const, which breaks when you have levels of indirection, is suboptimal
> in terms of usability - you never get the typing quite right, and the
> compiler keeps complaining.

Meaningless words again.
As a name for 'logcally immutable value' const always works right, including
when you have levels of indirection. Your expectations of what const ought
to do are based on confusion. When your expectations are not met you react
with blaming const rather than adjusting them.

To address your confusion about levels of indirection, in C++

struct Foo
{
Bar * pBar;
};

doesn't mean 'Bar is a part of Foo' or 'Bar is owned by Foo' as people
coming from other languages often believe. What it really means is 'Foo
knows about Bar'. As such, immutability of Foo doesn't mean immutability of
Bar. If you want to express a different relationship between the two, which
will imply immutability propagation, raw pointer is a bad choice. It is
pretty trivial to create a smart pointer, let's call it owning_ptr that
enforces ownership and propagates immutability. If you do that you will map
your design concepts cleanly into the language rather than fight it.
If you insist to use pointers for a design concept that they don't model
well you will have to deal with maintaning ownership and const-correctness
yourself. And yes, as any manual work this might be hard. There might be
technical reasons to do this but in vast majority of cases this is
unnecessary. The fact that you complain about const pointers so much only
tells me that you use them inappropriately most of the time.

> And avoiding const will not work well,
> either (you won't be able to do things like func(ClassName(args)), for
> example,

Wrong. You can avoid const pretty easily but you must do exactly what Java
or C# do. Namely:
a) Use garbage collector
b) Use func(new ClassName(args))

Now you are free not to use const.

> and if you need libraries that use const, it will get worse):

Why worse? Do what you do every two lines in other languages: cast.

> One counter argument is that the C++ approach still allows for more
> compile time checking compared to having no const at all. I think that
> compile time checking is overrated in this case compared to the
> usability problems

The famous "usability" again.

>, including the clarity of error messages (consider
> the ones you get if you say "map::iterator p=m.begin()" when m is a
> const map).

Are you talking about the language or particular compilers? Incidentally
this confusion is all over your site.
Then why is that a problem of const? You should have complained: "error
messages produced by C++ compilers are often hard to understand". This would
be a true and valid criticism. AFAIK and FYI there are proposals before C++
committee to address this.

> There are things you'd obviously not try to represent in a
> static type system (such as subsets of natural numbers - "this
> argument must be an even number, this one must be a prime number, and
> this one must be a number representing a Turing machine that halts").
> I think that "mutability" gets close to the edge here (I'm sincerely
> not sure at which side of the edge it is);

This argument tells a lot about your skills as a designer. You might want to
ponder the reason why a widely respected book like "Effective Java" had
chosen to include advice about immutability instead of dealing with prime
numbers.

> As to the idea to convert void func(const std::vector<const T*>&) to
> template<class It> void func(It b, It e): this forces me to make func
> a template, which moves the source code to header files slowing down
> the build and further complicates the error messages, etc., opening a
> can of warms unrelated to this sub-thread. I'm aware of this option
> and find it completely unacceptable.

Sorry but this is rubbish. You have been given a good solution that allows
you to express your design clearly in C++ language. This solution also
parallels the best (AFAIK) solutions available in competing languages. Case
closed as far as const is concerned.

If you are dissatisfied with build speeds, quality of error messages etc.
feel free to complain about them. You will find much more sympathetic ears
for that.

--
Eugene

Yossi Kreinin

unread,
Nov 1, 2007, 2:36:59 PM11/1/07
to
On Oct 31, 3:58 am, LR <lr...@superlink.net> wrote:
> "How about trying another programming language?"
>
> Could this be what you're driving at? If so, what do you have in mind?
>

This is exactly what I'm driving at. The language choice depends on
your application domain, organizational constraints and personal
taste, and discussing this seems off-topic here. A partial list of
languages I refer to when I speak about useful things C++ doesn't have
(or problematic things other languages haven't) is here:

http://yosefk.com/c++fqa/disclaimers.html

One particular thing that AFAIK applies almost always: low-level work
is better done in C than C++. I speak from lots of experience (but it
doesn't matter - what matters is whether my points in the FQA convince
you or not). By "low-level", I mean "when your resources are really
scarce" (compared to the "size" of the problem).

Yossi Kreinin

unread,
Nov 1, 2007, 2:36:41 PM11/1/07
to
On Oct 29, 3:22 am, "Alf P. Steinbach" <al...@start.no> wrote:

> A Frequently Questioned Answers is a very good idea, and I commend the
> effort you've put into this.

Thanks!


> > One thing I'm definitely interested in is whether I got
> > any facts wrong, and if I did, which ones.
>
> Well, most of it. ;-)
>

Well, I meant "facts" :) My definition of "facts" in this context is
here (together with the first factual correction submitted by a
reader):

http://yosefk.com/c++fqa/web-vs-fqa.html

> One main high level issue that the FAQ gets wrong is the purpose of a
> programming language. In general it's a good idea to use the most
> appropriate language for a task, but your FQA seems to assume that
> somehow everything has to be done in C++.

The FQA doesn't say that everything has to be done in one language
anywhere, and I use several languages myself. The FQA says that /
nothing/ should be done in C++. This does not imply that "if C++ were
good, you'd have to do anything in it" or anything like that.

> Garbage collectors such as the Boehm collector work well.

One problem is that several libraries may be built to use different
garbage collectors. Garbage collectors are not entirely non-intrusive;
they may ask the user to provide some kind of layout information or
finalization functions, for example. Having many of them in one
program is thus problematic.

Non-intrusive garbage collectors have false positives (some things
look like pointers, but aren't), and fail when people use formally
illegal, but normally working techniques like int* one_based_vector =
(int*)malloc(sz)-1;

One thing C++ garbage collectors can't do is heap compaction. AFAIK
this increases the upper bound on the memory consumed by a program
with the peak sum of allocated object sizes equal to M from M to
M*log(max_block_size), which is a huge difference for realistic values
of max_block_size.

However, for
> many C++ programmers garbage collection is not desirable, because while
> it can ensure that memory is always reclaimed, it leads to other problems.

If you refer to /hard/ real time applications (most large ones are
really /soft/ real time), than AFAIK operator new has the same
problems in terms of worst case performance. If you refer to resource
leaks when resources!=memory, then I have to point out that the vast
majority of resources /is/ memory, and very basic language constructs
(such as new) depend on being able to acquire memory, and not having a
simple solution for memory management is likely the single biggest
problem with "low-level" languages when you try to do "high-level"
work in them (but C++ is promoted as a pretty high-level language).
IMO not acknowledging the difference between "memory" and "resource"
is a manifestation of perfectionism ("solutions that only solve 95% of
the problem are worthless"), and there's plenty of such things in C++.

>
> >From the FQA:
> > And what if you make a mistake, one single mistake with all these deletions?

> with proper RAII, if an object is still alive, then its class invariant
> still holds.

RAII does not at all prevent problems with dangling references. If you
keep a pointer into an object in an STL container, which acquires
memory in the constructor and deallocates its memory in the destructor
(which is what RAII means), and the container dies, you have a
dangling reference. Which is why C++ programs will frequently make
themselves private copies of objects - pointing to something you don't
"own" is dangerous.

You can implement reference counting on top of RAII, making all your
pointers "smart" (and having the usual trouble when integrating with
other libraries of equally smart though different, or, most often,
dumb bare pointers). Then you'll have the problem with cyclic
references (which, by the way, is AFAIK the single case when a GC
could leave "zombie objects" around - and note that it took us way
less work to get to this stage).

-- Yossi


--

Walter Bright

unread,
Nov 1, 2007, 2:47:37 PM11/1/07
to
Maciej Sobczak wrote:
> The clarity of error message has nothing to do with whether constness
> is a good idea or not. It's a quality of implementation issue. You can
> say that 100% of compilers are crappy when it comes to error messages
> (and you might be surprised how many of C++ programmers would actually
> agree), but that is still far from saying that the language itself
> sucks.

Pedantically, you're right. On the other hand, a lot of very smart
people have poured enormous resources into developing and refining C++
compilers. If 100% of the C++ compilers generate crappy error messages
(relative to error messages for compilers of other languages), then it
is a reasonable thing to ask what is it about C++ that makes it very
hard to do good error messages?

I.e. it *is* a problem with the language. The C++ committee recognizes
this, as concepts are an attempt to make it easier to generate better
error messages. How well this will turn out is too soon to know.

----
Walter Bright


Digital Mars C, C++, D programming language compilers

http://www.digitalmars.com

Walter Bright

unread,
Nov 1, 2007, 2:45:51 PM11/1/07
to
Eugene Gershnik wrote:
> Yossi Kreinin wrote:
>
>> I learned that
>> apparently D improved const.
>
> How? If you know please post it here. If not this is a meaningless statement
> in a technical forum.

It turns out that designing a const system in a language is a
surprisingly difficult problem. We worked for about 9 months to design
one for the D programming language, but it has turned out to be too
complicated, and is undergoing a redesign.

Setting aside the details of syntax, the two fundamental differences
between the D const system and the C++ one are:

1) D has two kinds of const: const and invariant. Const is, like C++, an
immutable view of data. Other views of that same data may be mutable.
Invariant represents data that is immutable. C++ does have invariant
when const is used as a storage class, but there's no way to declare a
pointer to immutable data in C++.

2) const (and invariant) in D is transitive. That means if you have a
pointer to const T, anything reachable through that pointer cannot be
mutated. This is different from C++, where although T is const, nothing
T refers to is necessarily const.


One controversial thing that D's const doesn't have is the 'mutable'
keyword which is there to support the notion of 'logical constness'. It
is a deliberate design choice to not support logical constness with the
const system.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Francis Glassborow

unread,
Nov 1, 2007, 5:24:49 PM11/1/07
to
Eugene Gershnik wrote:
>
> To address your confusion about levels of indirection, in C++
>
> struct Foo
> {
> Bar * pBar;
> };
>
> doesn't mean 'Bar is a part of Foo' or 'Bar is owned by Foo' as people
> coming from other languages often believe. What it really means is 'Foo
> knows about Bar'. As such, immutability of Foo doesn't mean immutability of
> Bar.

Let me elaborate on the above:

struct Foo {
Bar * bar_ptr;
Foo(Bar * b_ptr): bar_ptr(b_ptr){}
};

Objects of type Foo are allowed to know about an object of type Bar (the
passed pointer might be a null pointer so they do not necessarily know
about a specific Bar. It is a little like having the key to a house, it
allows you access but does not guarantee ownership or responsibility for
the object.

struct Foo {
Bar * bar_ptr;
Foo(Bar * b_ptr): bar_ptr(new Bar){}
~Foo(){ delete bar_ptr;}
};

This is a strong 'ownership' relationship (better provided by a
smart-pointer) and each object of type Foo is responsible for the life
of an object of type Bar.

Note that the first of these is fragile because nothing has been done to
deal with the problem of a Bar object going away prematurely.

--

Eugene Gershnik

unread,
Nov 2, 2007, 2:31:23 AM11/2/07
to
Yossi Kreinin wrote:
> If you refer to resource
> leaks when resources!=memory, then I have to point out that the vast
> majority of resources /is/ memory,

[...]

> ("solutions that only solve 95% of
> the problem are worthless")

Yet another often repeated and unsubstantiated claim. How did you arrive at
the 95% figure? Hopefully not by counting 1MB memory allocation as
equivalent to 1 million mutexes.
In *my* experience the vast majority of resource types are not memory. The
raw non memory types include sockets, synchronization primitives, threads,
files etc. But this is only the beginning of the story. What "memory is the
only resource" people don't realize is that any time you have

struct Foo
{
NonMemoryResource bar;
};

Foo immediately becomes non memory resource too and has to be disposed of
correctly. And so on up to the top level owner. This is why 'database
connection' is a non memory resource even though it is a high level
abstraction. In C++ this comes for free but in your favorite GC-but-no-RAII
languages it is a manual task. Counting these "indirect resource" classes
the count of non-memory vs. memory resource becomes much bigger.
Incidentaly this is one of the largest sources of resource leaks in Java
code I have found. Even though a raw resource there may have a close()
function higher level classes that use it often forget to supply their own
close()-ing mechanism. Even when such close() is present, the user of the
higher level class often ignores to call it hoping for GC to do the job and
not realizing that there is e.g. socket inside.

There are some domains/problems where GC is really the best choice memory
management. An example I have personally met is implementing tree-like
object relationships. There were other examples pointed out by much smarter
people in lengthy discussions on this forum which you have obviously never
read.

There probably also exist domains where memory is, indeed, the main
resource. However, I doubt that many C++ programmers ever met them.

> and very basic language constructs
> (such as new) depend on being able to acquire memory, and not having a
> simple solution for memory management is likely the single biggest
> problem with "low-level" languages when you try to do "high-level"
> work in them (but C++ is promoted as a pretty high-level language).

FUD and demagogery.

This is IMO also a good description of your entire approach. You criticise
things without defining what you are criticising (const or compiler
messages?), constantly shift the targets of criticism (language, specific
compiler or a debugger?), make unsubstantiated claims ('often', 'biggest',
95%) and use meaningless terminology ('usability') in order to evoke
emotional reaction from the reader. In short you use debate 'techniques'
commonly used in political and religious arguments.
Sometimes, you come close to identifying some real problems in the language
but from what I have seen this is rather accidental.

--
Eugene

Nevin :-] Liber

unread,
Nov 2, 2007, 2:32:48 AM11/2/07
to
In article <1193873515.2...@57g2000hsv.googlegroups.com>,
Yossi Kreinin <yossi....@gmail.com> wrote:

> Well, I meant "facts" :) My definition of "facts" in this context is
> here (together with the first factual correction submitted by a
> reader):
>
> http://yosefk.com/c++fqa/web-vs-fqa.html

It is basically just a rant, not facts.

For instance, you claim that people don't measure performance of
templated code <http://yosefk.com/c++fqa/templates.html#fqa-35.10> and
then go on to claim that templated code in most cases is slower than non
templated code due to i-cache effects. Yet nowhere can I find any of
*your measurements* which show this to be true. Where are your
measurements?

Take a simple templated function which calls a templated algorithm:

template<typename T, ::size_t N>
void SortAnArray(T (&array)[N])
{
std::sort(array, array + N);
}

Now, pick any other language, and show us that this C++ version (with
optimizations turned on, of course, since inlining is an important part
of templated code performance) is slower for most invocations of this
with fundamental types (this way we leave out variations due to poorly
written operator<) and various sizes and initial randomness of the
array. Other than handcoded assembly by an expert, I seriously doubt
you will.

You make other wild claims, such as vector<int> and vector<void*> not
sharing code, yet I know of a least one C++ implementation (Metrowerks)
that has done so for years.

--
Nevin ":-)" Liber <mailto:ne...@eviloverlord.com> 773 961-1620

Yossi Kreinin

unread,
Nov 2, 2007, 2:59:11 AM11/2/07
to
On Nov 1, 1:35 am, "Eugene Gershnik" <gersh...@gmail.com> wrote:
> Yossi Kreinin wrote:
> > I learned that
> > apparently D improved const.
>
> How? If you know please post it here. If not this is a meaningless statement
> in a technical forum.

I primarily referred to making const transitive (that is, if an object
is const, you can't modify anything via that object - all pointers
you'll get via it will be const, too); Walter Bright's message has the
full details.

I mentioned it because you talked about other languages, and didn't
elaborate on it since for me, personally, the ways of getting const
right are not the main theme of this discussion; the main theme is
what happens when you get const wrong in the language.

> I don't know what "usability" is for you and how you define it. If you can
> provide a good measurable definition please provide it.
> Otherwise I can and do claim that const imporves 'usablity' as I understand
> it.

In a complicated context like programming frequently all we can do is
point out that something exists ("const helps me since I don't have to
check whether that object is modified in the code"/"const gets in my
way because I can't get the typing right when there are levels of
indirection"). Reasonably quantifying the importance of such things,
however, is impossible since approximating the distribution of the
"relevant" programs and programmers is impossible.

What I claim is this. Most languages don't have const and most
programmers consider this tolerable. C++ does have const, which
according to most programmers helps in many simple cases. However,
when you have levels of indirection, you simply can't do "the right
thing", which can be objectively demonstrated, and programmers end up
copying objects or casting pointers or making everything template,
each which has it's own problems and is a way to work around the
problem, not solve it. I claim that the damage done by the complicated
cases exceeds the benefits gained in the simple cases.

Evaluating this claim against its exact opposite can't be done
objectively, so there's not much to argue about - different people
have different taste and experience. What can be discussed objectively
is the /existence/ of the problem in the cases when there's
indirection.

>
> What I do know is that a language should allow one to express design
> concepts as clearly as possible. There is an exteremely important concept
> like 'this thing is supposed to be logically unchanged' in any design. One
> ought to be able to express it at the language level.

...But you wouldn't quantify /that/ knowledge (by measuring the
benefits of "clear expression", for example), would you? :)

The problem with clearly expressing immutability in C++ is that it
isn't clear where "this thing" which shouldn't be changed ends (see
also below). I agree that in general, there's lots of potential value
in specifying constraints on values of variables. However, these
specifications can also cause problems, by not exactly matching the
constraints you'd really like to specify, and/or by simply cluttering
the code and/or producing compile time error messages where no run
time error would occur. So each special case of such things has to be
evaluated separately.

>
> > From my experience, a
> > language without const is sure usable.
>
> For *your* definition of usable and from *your* experience.
>

Well, quantifying is hard, but in this particular case, you have to
agree that lots of people using Java, C#, VB, Perl, Python, Ruby and
JavaScript (among lots of other languages) don't care much about not
having const. It's not just my experience in this case. Of course this
doesn't "prove" my claim, just shows that it isn't as exotic as you
make it sound.

>
> To address your confusion about levels of indirection, in C++
>
> struct Foo
> {
> Bar * pBar;
>
> };
>
> doesn't mean 'Bar is a part of Foo' or 'Bar is owned by Foo' as people
> coming from other languages often believe. What it really means is 'Foo
> knows about Bar'. As such, immutability of Foo doesn't mean immutability of
> Bar.

I don't have any "confusion" about this, I know exactly what const
means for years, I don't "come from other languages", and please let's
not discuss each other and concentrate on C++, a subject we both know
something about.

Here's an example which I brought up earlier in the thread - a real
example of a simple attempt to use const and the standard library
given the C++ definition of const you've just correctly described:

http://yosefk.com/c++fqa/const.html#fqa-18.1

Why isn't this a problem? Why wouldn't having no const in the language
be better /in terms of clarity/? Why is /not knowing/ from the
function prototype that something isn't going to change so bad,
especially considering the fact that you /yourself/ give an advice to
cast const away in the body of the function?

Furthermore, why is it "good" that to implement an STL-like container,
you need to implement an iterator and a const_iterator? What happens
if you have a std::vector-like container that can get the storage
pointer from outside (as opposed to allocating it in the constructor),
and we want to attach a const storage pointer to it in some contexts
and non-const storage pointer in other contexts? Should we have a
vector and a const_vector? "const vector" wouldn't do just like "const
iterator" won't: with a const storage pointer, the vector /itself/ can
be modified (you can resize it, for example).

I would appreciate if you clarified your position on these particular
issues.

> If you want to express a different relationship between the two, which
> will imply immutability propagation, raw pointer is a bad choice. It is
> pretty trivial to create a smart pointer, let's call it owning_ptr that
> enforces ownership and propagates immutability. If you do that you will map
> your design concepts cleanly into the language rather than fight it.

Writing smart pointer classes to avoid the problems with const is what
I call fighting the language. You write lots of code ultimately doing
nothing. IMO, the readability gained from understanding which objects
can be modified by a piece of code is lost many times over by having
much more code.

> If you insist to use pointers for a design concept that they don't model
> well you will have to deal with maintaning ownership and const-correctness
> yourself. And yes, as any manual work this might be hard.

Writing smart pointer classes to deal with const correctness is also
manual work.

>
> > And avoiding const will not work well,
> > either (you won't be able to do things like func(ClassName(args)), for
> > example,
>
> Wrong. You can avoid const pretty easily but you must do exactly what Java
> or C# do. Namely:
> a) Use garbage collector
> b) Use func(new ClassName(args))
>
> Now you are free not to use const.

a) Garbage collectors don't work with C++ in practice, see another sub-
thread.
b) This has nothing to do with garbage collection. If C++ didn't have
const like most languages, and you had a void func(ClassName&), then
func(ClassName(args)) would work just like func(new ClassName(args)).

>
> > and if you need libraries that use const, it will get worse):
>
> Why worse? Do what you do every two lines in other languages: cast.

I don't do it in other languages, and that would be lots of casts.

>
> Are you talking about the language or particular compilers? Incidentally
> this confusion is all over your site.

This isn't "confusion", either. For someone actually writing software,
a language is only as good as the best implementations you can use in
practice. And C++ is old enough and popular enough to give people
enough time and motivation to create high quality implementations.
Whatever doesn't work smoothly by now has low chances to improve
significantly in the future.

>
> This argument tells a lot about your skills as a designer.

I could discuss the different notions of "design" different people
have, but I'd like to stick to the topic, which is const and neither
you nor me.

>
> Sorry but this is rubbish. You have been given a good solution that allows
> you to express your design clearly in C++ language. This solution also
> parallels the best (AFAIK) solutions available in competing languages. Case
> closed as far as const is concerned.
>

But it's open as far as template is concerned.

> If you are dissatisfied with build speeds, quality of error messages etc.
> feel free to complain about them. You will find much more sympathetic ears
> for that.
>

The problems with the build speeds of templates are inherent to the
design of this language feature:

http://yosefk.com/c++fqa/templates.html#fqa-35.12

I've spent lots and lots of time working with C++, and I understand
your position on this issue quite well. You value precise static
typing (which you call "clear expression") very high and the actual
problems of "implementations" very low, nor do you acknowledge that
these problems really come from the language definition. You're also
very tolerant to the need to write wrapper code, and prefer the
benefits of precise modeling to the drawback of having more code to
read and maintain.

There are different perspectives on these issues, and I think you
could agree that they are "internally consistent". But the main point
is the concrete examples, like the "const vector"/"const_vector"
issue: why do you think that the behavior of the language makes sense
with them?


--

Eugene Gershnik

unread,
Nov 2, 2007, 3:07:07 AM11/2/07
to
On Nov 1, 11:45 am, Walter Bright <wal...@digitalmars-nospamm.com>
wrote:

> 1) D has two kinds of const: const and invariant. Const is, like C++, an


> immutable view of data. Other views of that same data may be mutable.
> Invariant represents data that is immutable. C++ does have invariant
> when const is used as a storage class, but there's no way to declare a
> pointer to immutable data in C++.

Interesting. Is this distinction primarily to help optimizer or does
it have some high level design uses?

> 2) const (and invariant) in D is transitive. That means if you have a
> pointer to const T, anything reachable through that pointer cannot be
> mutated. This is different from C++, where although T is const, nothing
> T refers to is necessarily const.

Without knowledge of D it is hard to tell whether this would work. How
do you model
'immutable X that knows about mutable Y'?

> One controversial thing that D's const doesn't have is the 'mutable'
> keyword which is there to support the notion of 'logical constness'. It
> is a deliberate design choice to not support logical constness with the
> const system.

Hmmm. Rather than give the obligatory 'string that caches the length'
example let me give you something closer to what I usually see

class Mutex
{
void Lock(); //non const, changes the mutex
void Unlock(); //non const, changes the mutex
};

class SynchronizedInt
{
int value;
Mutex mutex;

int ReadValue() const //doesn't change value
{
Lock lock(mutex); //for this to work mutex must be mutable
return value;
}

};

How do you do it in D?

--
Eugene

Eugene Gershnik

unread,
Nov 2, 2007, 3:04:09 AM11/2/07
to
On Nov 1, 2:24 pm, Francis Glassborow
<francis.glassbo...@btinternet.com> wrote:

>
> struct Foo {
> Bar * bar_ptr;
> Foo(Bar * b_ptr): bar_ptr(new Bar){}
> ~Foo(){ delete bar_ptr;}
>
> };
>
> This is a strong 'ownership' relationship (better provided by a
> smart-pointer) and each object of type Foo is responsible for the life
> of an object of type Bar.

The important thing here is that 'ownership' concept is implicit in
such code. Nothing in the line

Bar * bar_ptr;

means ownership. It is just the same old 'knows about'. Only by
looking at what the implementation actually does in ctor, dtor etc.
you can discover that it uses 'knows about' to model 'owns'. This is
what I meant by manual (hard, error prone etc.) method of implementing
ownership.
An imaginary ideal language would provide 'ownership' as a language
level primitive. Something like

//pseudo language
class Foo
{
Bar * [owned, propagate immutability] bar_ptr;
};

In absence of such feature the best one can do is to write manual
ownership implementation once in a form of some 'smart' pointer and
never again have to deal with it. Thus you probably can get something
like

//C++
class Foo
{
smart_ptr<Bar, owning, propagate_immutability> bar_ptr;
};

using policy based designs. In practice a dedicated smart pointer
class with required semantics is usually enough.

--
Eugene

Yossi Kreinin

unread,
Nov 2, 2007, 3:12:28 AM11/2/07
to
On Nov 1, 1:36 am, Maciej Sobczak <see.my.homep...@gmail.com> wrote:

> From some other exprience, a language without any static type system
>
> is also surely usable. Should we criticize those that have a static
> type system?

Well, if we should, that certainly isn't because the dynamically typed
ones are usable, at least not because of that by itself. I criticized C
++ const because it doesn't deal with the cases when a const object
itself points to other objects very well (I understand how the
behavior is defined, I just don't think the definition is useful in
practice). Based on /that/, I claim that having no const at all is
better than having C++ const.

>
> I have found recently (or better: I have came to appreciate) that
> constness is essential for code clarity - to the point that I would
> even endorse having const as a default (!) with some 'variable'
> modifier (or just 'mutable' as an existing keyword) or such to mark
> those objects that are actually allowed to change.
>
> Difference in expectations.

Not neccessarily :) I appreciate ways to specify constraints on the
program data (and control) flow. And I think const is useful in the
simpler cases, when the const object doesn't itself contain pointers
(especially to objects that themselves contain pointers, etc.). All I
am saying is that the confusion it causes with multiple levels of
indirection exceeds the benefits you get in the simple cases. This
can't be proven (it can only be demonstrated that the problem exists,
but its importance can't be quantified), nor does it mean that a
definition of const not entirely different from the one used in C++
can't be more useful than not having any kind of const according to my
own (informal) criteria.

>
> Mutability is one of the simplest things we can assert about any given
> object. Much simpler than, say, type substitution. Why not using the
> type system for such a simple thing?

I don't know how simple it is if we consider the issue theoretically,
from the perspective of the designer of a new language. I know that
the const definition in C++ has problems with indirection, and they
come from a basic question: where does an object end?

Consider std::vector. There are two kinds of modifications: changes to
the elements (like v[i]=x) and changes to the vector itself (like
v[i].reserve(n)); some changes fall into both categories (like
v.clear()). Now, suppose we want to have a vector-like class that
works with storage buffers passed as constructor arguments, sometimes
the buffers are const and sometimes they aren't. Clearly a vector that
was passed a const storage buffer can still be changed (for example,
we could attach a different storage buffer to it), without modifying
the buffer. So const vector is not the right way to represent a vector
working with a const buffer. What should we do - implement a
const_vector (the way we have const_iterator, for these same reasons)?
See also the adjacent sub-thread.

> It might only contain a loop that calls void funcElem(const T *) for
> each iterator value in the range, and funcElem(const T *) itself need
> not be templated.
>
> void funcElem(const T * p) { ... } // not a template!
>
> // only this is a template
> template <class Container>
> void func(const Container & c)
> {
> for_each(c.begin(), c.end(), funcElem);
>
> }
>
> And then:
>
> vector<T *> vp;
> vector<const T *> vcp;
> // ...
> func(vp); // OK
> func(vcp); // OK

I think that having to write all that code in order to solve the
problems with const correctness illustrates the fact that we have a
problem. The ultimate point of const is clarity; the extra layers of
code seem to defeat the purpose (you do get the extra benefit of
better compile time checking; I've never seen any ROI from this in the
case of const though - people rarely end up modifying random things,
the benefit of const is primarily to tell a reader of the code what
people most certainly didn't want to modify in the first place).

>
> I appreciate the amount of work that you have put into gathering all C+
> + criticism in a single place, but I'm afraid you got into the trap of
> being focused on problems instead of solutions.
>

Thank you, and I don't think it's a trap - some problems are best
solved by being avoided, and you have to "focus" on the problems to
see if you want them solved locally or by avoiding the whole bunch of
them (in this case, by programming language choice).

By the way, I have lots of C++ programming experience, in particular
experience with finding solutions like these. In particular, whenever
you have a typing problem (you don't want to spell out a type name or
you have many types the language considers "unrelated", like
vector<const T*> and vector<T*>), one thing you can do is add a
template layer and solve the problem. That complicates the control
flow though, and creates lots of code to read and write.

Hyman Rosen

unread,
Nov 2, 2007, 12:34:23 PM11/2/07
to
Yossi Kreinin wrote:
> The problems with the build speeds of templates are inherent to the
> design of this language feature

I work with a project that contains many hundreds of C++ files,
pretty much all of which use class and function templates from
the standard library and our own. I can do a clean recompile of
the whole system in ten or fifteen minutes or so on my Solaris
x86 machine using the Sun CC 11 compiler. For normal work, the
source compilation time is inconsequential - it's linking that
takes most of the time, and even that isn't too long.

Hyman Rosen

unread,
Nov 2, 2007, 12:35:14 PM11/2/07
to
Yossi Kreinin wrote:
>> vector<T *> vp;
>> vector<const T *> vcp;

You know, you keep carping about how these two types are
incompatible, as if it were a given that this should not
be so. But in fact, the two classes can be very different,
because C++ allows specialization. It's not the case for
std::vector, but it can be the case for other classes.

As a simple example, I can create

template<typename T> class p;
template<typename T> class p<T const *> { T const &deref_const(); };
template<typename T> class p<T *> { T &deref(); };

How in the world should the compiler allow passing a
p<char *> to a function expecting a p<char const *>?
They don't even have the same methods. Templates with
different parameters are entirely different types, and
there should be no expectation of substituting one when
another is called for. You could argue with as much
futility that it should be permitted to pass an X<char>
to a function expecting an X<long>, since after all a
long can hold every value in a char's range.

khall....@gmail.com

unread,
Nov 2, 2007, 12:36:27 PM11/2/07
to
On Oct 26, 1:27 am, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
> One thing I'm definitely interested in is whether I got
> any facts wrong, and if I did, which ones.

Secion "No compile time encapsulation"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Recompilation can be avoided by using the PIMPL pattern:
http://en.wikipedia.org/wiki/Opaque_pointer .
This problem is not unique to C++; C has similar problems.


Section "Defective operator overloading"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

First off, there is no restriction that says operators must return by
value. The << operator overloaded for std::ostream is a perfect
example. The normal and recommended way of overloading << is by
returning a reference, not a value:

std::ostream& operator<< (std::ostream&, {some type here});

Also, smart pointers can be used when needing to return "new'd"
pointers. There's one in the current standard (std::auto_ptr) and a
few more that will be in the next standard.


Section "Defective exceptions"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The RAII idiom works very well. I've was the project lead on a C++
project that involved 200,000 lines of code and there were only 2
instances of the "new" keyword. (And yes, we did use exceptions, OOP
conecpts, the STL, as well as other paradigms and features of C++.)
By using standard containers, smart resources (for non-pointer items
like mutex lockers), etc..., large software projects can be built that
properly release memory and resources when exceptions occur.

And by the way, the RAII idiom isn't what is wrong with C++
exceptions. Similar problems exist in other languages that have
exceptions too. How does one release a resource in C# when an
exception occurs? Via a 'finally' statement or a 'using' statement.
The problem is that one must be careful when writing code that uses
exceptions. (I for one prefer RAII to 'finally' and 'using'
statments, but that's a personal preference, not a rule.)


Section "Duplicate facilities"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can find "devotees" to rattle off all sorts of nonsense for any
argument under the sun. But that doesn't mean that the experts share
that view -- or even that the common user shares that view. No
language feature is 'evil' (the intent with which it is used may be --
see obfuscated C contest ;] ).

Note also that the reason std::string and std::vector exist is because
the C facilities have their shortcomings: namely the inability to
resize themselves. You're right, the std::string and std::vector are
in some ways inferior to char* and arrays, but in some ways they are
superior too. There's a tradeoff that must be considered.


Section "No high-level built-in types"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You said "C++ doesn't add any built-in types to C."
What about 'bool'?
Also, depending on your definition of "built in", you could include
all the standard library classes, which actually is quite a large
number (std::string, std::ifstream, std::ostream, etc...)


Section "Defective metaprogramming facilities"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Macros are not lame at all. They are useful when created domain
specific languages (http://en.wikipedia.org/wiki/
Domain_Specific_Language) as well as helping out sometimes with other
tasks. Sure, they have been mis-used but that's not their fault.
They are incredibly helpful when used properly.

By the way, macros and templates make C and C++ two of the few
languages to be able to create DSLs that are parsed by the compilers
themselves rather than having to build a program or library to parse
DSL. That's nifty (and useful)!


I could continue on, but I need to go to bed now as I'm falling asleep
typing.

Jerry Coffin

unread,
Nov 2, 2007, 12:33:52 PM11/2/07
to
In article <mr-dndWKz9q5sbTa...@comcast.com>,
wal...@digitalmars-nospamm.com says...

[ ... ]

> I.e. it *is* a problem with the language. The C++ committee recognizes
> this, as concepts are an attempt to make it easier to generate better
> error messages. How well this will turn out is too soon to know.

Yes and no. It's true that it's technically too soon to know about
concepts as part of the official C++ language, because (of course) they
won't be for a while. OTOH, concepts have been implemented in conceptgcc
so it's entirely reasonable for just about anybody who cares to download
a copy and start testing anytime they want to.

My own experience is pretty simple: using conceptgcc, virtually all of
the hard to read error messages simply disappear, at least for code that
actually uses concepts. Of course, nothing forces you to do that, so if
you want to write code that generates arbitrarily lousy error messages,
you can still do so.

--
Later,
Jerry.

The universe is a figment of its own imagination.

Yossi Kreinin

unread,
Nov 2, 2007, 12:42:09 PM11/2/07
to
On Oct 28, 8:01 pm, Alex Shulgin <alex.shul...@gmail.com> wrote:
>
> Although it seems you did a fair amount of research, to me your post
> is not much more than a very sophisticated way of saying "C++ SUCKS"
> on a moderated C++ newsgroup. :-)

Most of the FQA is not based on "research" done for the purpose of
writing it, but on my experience using C++ and watching others do it.

My purpose is to convince people, not to irritate the ones I can't
convince; I see no practical way to attempt to do the former while
completely avoiding the latter though.

>
> "No compile time encapsulation"
>
> This item implies that you have to ship the implementation class
> declaration to clients, which is not necessarily true. Have you ever
> heard about abstract classes ("interfaces")?

I know the ways to work around the problems with private:

http://yosefk.com/c++fqa/inheritance-abstract.html#fqa-22.1 (abstract
classes)
http://yosefk.com/c++fqa/heap.html#fqa-16.21 (pimpl)
http://yosefk.com/c++fqa/class.html#fqa-7.5 (incomplete types)

The C++ solutions (the first two) require you to write wrapper code,
the C solution (the last one) does not, which IMO makes it the best
(I'm referring to the context where you'd use private but you want to
avoid recompilation upon changes to private members and this is the /
only/ problem you're solving; abstract classes have other merits, and
the FQA acknowledges them).

However, workarounds aside, C++ does have private, and there's a
separate question of whether it's useful or not. I claim that the
advantages (getting a compile time error when someone accesses a
member you didn't want them to) are dwarfed by the drawbacks. Most C++
classes are neither pimpls nor implementations of base classes (that
takes manually written wrapper/bridge code) and of course not C-like
incomplete types (naturally, people most often use C++ classes when
they program in C++). Changes done to the private members of those
classes are responsible for a significant amount of time spent for
rebuilding C++ code (of course I don't have numeric data proving this,
and you don't have numeric data refuting this; all we can do is to
point out the things that happen, not to measure their importance).

>
> "Outstandingly complicated grammar"
>
> Maybe, you have your point here.

Note that frequent rebuilds would be less of a problem if C++ compiled
fast, and the fact that it follows from the language definition, not
the "low quality" of all implementations available today:

http://yosefk.com/c++fqa/defective.html#defect-2
http://yosefk.com/c++fqa/defective.html#defect-3
http://yosefk.com/c++fqa/defective.html#defect-12
http://yosefk.com/c++fqa/defective.html#defect-14
http://yosefk.com/c++fqa/defective.html#defect-16

>
> "No run time encapsulation"
>

I'm not claiming that every language should have run time
encapsulation. Frequently, language features can not be judged by
themselves, but only if you consider how they interact.

The problem with having no run time encapsulation /in C++/ is that the
language has no reflection and several different and non-trivial
commercially significant ABIs. This means, for example, that when a
program crashes, figuring out what's going on in the core dump is hard
compared to, say, a program compiled from C source (C doesn't have
reflection, either, but maps more straight-forwardly to executable
code):

http://yosefk.com/c++fqa/defective.html#defect-4

>
> "No binary implementation rules"
>
> Frankly, I don't see your point here. You have tried to link g++-
> compiled libraries with msvc-compiled code, or what exactly?
>

I didn't, because I know it doesn't work. I wish it did though. Are
you saying that this is a non-problem, and if so, why?

> "Very complicated type system"
>
>
> You can rewrite your function into a function template
> which will accept a range instead of `std::vector< whatever >&':
>
> template< typename Iter > void func2(Iter begin, Iter end);
>
> Of course, you loose that prominent `const*' in declaration, but as
> long as implementation complies with the given pact (not to modify
> whatever iterators "point to"), I think it's pretty OK.

I disagree, because of the many problems with templates:

http://yosefk.com/c++fqa/templates.html#fqa-35.1

I'm not shifting the focus of the discussion, just saying that solving
the problem with const by introducing much more complicated problems
related to templates has drawbacks.

> Anyway, I don't recall myself applying cv-qualifiers to the type of
> contained elements in a sensible and useful way. Anyone?

What about when you fill your vector with objects obtained by calling
a method returning a const reference in a loop?

> > ... Exception safe C++ code is almost infeasible to achieve in a non-trivial program.
>
> Maybe, if you prefer to write everything from scratch instead of
> trying to stick to standard library.

What about when you need a subset of objects kept in std::vector or
std::list, and the owning collection is about to be deallocated,
because you only need some of the objects, but not all of them
anymore?

http://yosefk.com/c++fqa/dtor.html#fqa-11.1

Do you copy the objects, which has a performance penalty, or keep
collections of pointers, so you have no RAII coming from the standard
library to help you with exception safety anymore, or do you use smart
pointer classes which do reference counting, which breaks when you
have cyclic references?


--

James Hopkin

unread,
Nov 2, 2007, 6:53:21 PM11/2/07
to
On Nov 2, 4:42 pm, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
>
> What about when you need a subset of objects kept in std::vector or
> std::list, and the owning collection is about to be deallocated,
> because you only need some of the objects, but not all of them
> anymore?
>
> http://yosefk.com/c++fqa/dtor.html#fqa-11.1
>
> Do you copy the objects, which has a performance penalty, or keep
> collections of pointers, so you have no RAII coming from the standard
> library to help you with exception safety anymore, or do you use smart
> pointer classes which do reference counting, which breaks when you
> have cyclic references?
>

With std::list you can use splice. As for contiguous containers
(vector, deque) - that's the tradeoff you make for the advantages of
contiguous allocation. None of the other popular languages give you
this ability (or only for a very restricted set of types like C# value
types).

Note that C++0x will give you the option of, for example,
std::vector<unique_ptr<T>>


James

khall....@gmail.com

unread,
Nov 2, 2007, 6:58:40 PM11/2/07
to
On Oct 26, 1:27 am, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
> The C++ FQA Lite is available here:http://yosefk.com/c++fqa
> It's based on the comp.lang.c++ FAQ -http://parashift.com/c++-faq-lite
>
> While certainly not off-topic for comp.lang.c++[.moderated], the FQA
> can make a flame bait. Apparently, a good way to prevent flame wars is
> to have the discussion in a moderated newsgroup (I have a recent
> experience with a weakly moderated forum, and it was suboptimal). You
> can also e-mail me privately (if you're human, you'll find the address
> at the site). One thing I'm definitely interested in is whether I got

> any facts wrong, and if I did, which ones.

[6.1] "Is C++ a practical language?"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You say "C++ is not 'well-supported' in the sense that development
tools for C++ lack features and are unreliable compared to other
languages."

I strongly disagree with that. All you need to do is go to
SourceForge.Net and go to "software development"->"Debugggers" and see
how many items are related to C++. Visual Studio 6.0 to Visual Studio
2008 offer incredible capabilities in debugging, with VS2008 showing
many very cool new features. There are many non-MS and *nix tools
available too: CodeBlocks, KDevelop, gcc, BoundsChecker, Electric
Fence, and the list goes on and on and on....

You also say "C++ is not "mature" in the sense that different
compilers will interpret it differently". While the very advanced
parts of the language may act different on some compilers, most behave
similarly for parts of the language that produce defined behavior.
Most programs that do not include undefined behavior will produce the
same results on the popular compilers.


[6.2] "Is C++ a perfect language?"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

All I can say is that you are ranting here. There are very few facts
here.

No language is "perfect" -- each has its pros and cons. C++ is a
practical language; it may not be practical for a specific
application, but it certainly is practical for many. Why? It
availability on almost every OS (Python is a very popular scripting
language, but it isn't available on VxWorks or uItron), C++ is a multi-
paradigm language (it's OK to mix OOP, procedural programming, meta-
programming, and templates), C++ is powerful (boost::spirit and
boost::mpl are amazing examples), and there are many tools out there
designed for C++.


[6.3] What's the big deal with OO?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You go far beyond the question to rant on C++. There is nothing wrong
with the FAQ's answer. It's brief, to the point, and answers the
question. You go off making assertions that are false (as I mentioned
earlier, use opaque pointers).


[6.5] Is C++ better than Ada? (or Visual Basic, C, FORTRAN, Pascal,
Smalltalk, or any other language?)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Your first paragraph is a perfect complement to the FAQ's answer. The
rest is a rant on C++. That other information, toned-down, could be
useful advice about how to interface C++ with other languages.


[6.6] Who uses C++?
~~~~~~~~~~~~~~~~~~~

Another rant. Please, explain what is factually incorrect with the
FAQ's answer?


[6.7] How long does it take to learn OO/C++?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you removed your anti-C++ bias, you would have a good complement to
the FAQ's answer. Yes, it takes a long time to master the language.
You mention some specifics, fine. But the FAQ's answer is correct
too.


[6.8] What are some features of C++ from a business perspective?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You state "No practical implementation of C++ runs in managed
environments, increasing both the defect rate and the potential damage
of an undetected defect".

And what does a managed environment buy, do tell? If a program is ill
formed in a managed environment, the environment may tell you more
information when the programs behaves incorrectly, but the program
still behaves incorrectly. The managed environment may have garbage
collection, but there are garbage collectors available for C++ too. C+
+ is a complicated language, and *that* can increase the defect rate.

And, by the way, C++/CLI does run in managed environments. While
techinically a different language than C++, it combines the most of
the powerful features of C++ with those of .NET.

You also state "Providing C++ interfaces to a software component is
impossible in practice due to lack of compile time and run time
interoperability". That's just simply wrong! My proof is the vast
number of sucessful C++ projects. What's your proof?

You state "C++ is extremely inconsistent and complicated, increasing
learning curves and the defect rate". Leave out the 'inconsistent'
part.

You state "C++ compilers typically fail to comply to its intricate
standard, reducing portability". While technically true, this is
practically false. Visual Studio, gcc, Digital Mars, Comeau, and
other popular compilers are all at least 98% conformant, with some
even reaching 100% conformance. The main feature missing from most
compilers is 'export'. Other than that, there are a few advanced
features that may not be perfectly portable. But most programs that
don't use compiler-specific extensions are very easy to port from
compiler to compiler.

You state "C++ compilation is both very slow and very frequent,
increasing development time and defect rate". This is not true. Good
design practices (one being opaque pointers), pre-compiled headers,
multi-processor and distributed compilation all help reduce long
compile times. It's also possible to edit a file while something else
is compiling. Practically speaking, a developer's job included many
other things than just editing code; if there is a particularly long
compilation occurring, it's an opportunity to write documentation,
write tests, go to a meeting, .... There's always something that can
be done.

You state "C++ lacks standard types representing basic data structures
like strings, arrays and lists (or has more than one standard and many
non-standard ones, which is the same)." That's just false. There are
standard types for each structure you mention. One may *think* that
there are more than one, but disregarding template parameters, that's
not technically true. There's only one doubly-linked list type, only
one resizable array with O(1) random access, only one double-ended
queue, etc.... All of these could be considered "lists" from a high-
level view, but each has very different performance characteristics
and is why people choose a particular type for a given job.

You state "C++ doesn't reduce safety-vs-anything trade-off since it's
extremely unsafe". This isn't true. The 'const' keyword (which has
been discussed may times already in this thread) has been a lifesaver
in my company. Our customers were confused why data was getting
modified when we stated that it shouldn't. Well, we didn't have
'const' in our interface. So we inserted it in, compiled and found
all the places we needed to carry it further. Following this process
quickly lead us to the bug. Now we use 'const' as a means of
enforcing our contract, and consequently we find these problems during
compilation time long before the customer even sees the product. My
biggest complaint about most recent languages is their lack of
'const'. I'm glad D didn't sacrafice this!

[6.9] Are virtual functions (dynamic binding) central to OO/C++?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

C++ is in some ways more object-orientated than more recent languages
like Java and C#. Neither Java nor C# supports multiple inheritance,
but that is one major feature of a true object-orientated language.
(And for the record, both Java and C# have many procedural methods
using "objects" more as a namespace than really an object. One cannot
create a System.Math object, nor is a System.Math object needed to
call System.Math.Cos).

[6.10]-[6.12]
~~~~~~~~~~~~~

These all look to be nothing more than rants. If you toned down your
language and presented a more objective view, you might have something
useful. As it stands, only people who already share an anti-C++ view
will support. As such, it's not going to help anyone.


[6.14]
~~~~~~

You state "Asking about things specific to C++ is not very useful."
That's not true at all. You acknowledge earlier in the FAQ that it
takes a long time to become proficient in C++. If a company already
has a C++ project that must be maintained, then simply grabbing a
talented person who isn't familliar with C++ is a poor buisness
decision. Sure, that person should be able to pick up C++ (since he/
she is talented), however comapny may not be able to afford the time
needed for the developer to gain that proficiency. If a C++ person is
needed, then asking C++ questions is legitimate!

[6.15] What does the FAQ mean by "such and such is evil"?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

First, you can't really tell us what someone else means. That's like
me telling you that what you really mean by your FQA is that "C++
sucks", despite your disclaimers to the contrary. This sounds just
like another rant.

While I personally hate that the FAQ uses the term 'evil', I
understand that the FAQ's author wants to bring attention to a topic.
The FAQ afterall is not some hallowed document -- it's just one man's
opinion. Other people will agree with the FAQ to varying degrees. I
agree with most, but certainly not all of it.

khall....@gmail.com

unread,
Nov 2, 2007, 6:52:07 PM11/2/07
to
On Nov 2, 9:42 am, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
> On Oct 28, 8:01 pm, Alex Shulgin <alex.shul...@gmail.com> wrote:
> > Although it seems you did a fair amount of research, to me your post
> > is not much more than a very sophisticated way of saying "C++ SUCKS"
> > on a moderated C++ newsgroup. :-)
>
> Most of the FQA is not based on "research" done for the purpose of
> writing it, but on my experience using C++ and watching others do it.
>
> My purpose is to convince people, not to irritate the ones I can't
> convince; I see no practical way to attempt to do the former while
> completely avoiding the latter though.

I think you'll do a poor job of convincing people. The reason I
belive this is because you very frequently use an inflamatory tone in
your answers while not offering good alternatives. And the implied
alternatives frequently contradict one another. You argue that C++
exceptions are bad, but C++ exceptions are not really that different
from other language's exceptions (especially when compared against
your criteria for exceptions being bad). On the other hand, you argue
that C++ being an unmanaged and non-garbage collected language is
bad. Well, most mangaged, garbage collected languages out there use
exceptions.

It would be better to have a more objective tone. For example in the
"No compile time encapsulation" section, you could write: The nature
of C++ can make recompilation longer than it otherwise needs to be.
<insert your explanation of changing private members here and its
effect on compilation times>. Languages such as C, Ada, and D
(Walter correct me if I'm wrong) suffer similar problems and share the
same well-developed workaround: the "opaque pointer" (also commonly
referred to as the RAII idiom). The downside of using opaque pointers
is that is requires an extra level of abstraction and requires a bit
more time designing. Languages that have helped solve these problems
include: .... <list languages here> ...

If you answered each question in this manner, most C++ developers
would probably wouldn't have much of a problem with it. Sure, there
will always be die-hards, but you would not quite have the backlash
you currently have.


And here's something to consider: in my company (which uses primarily
C, but also C++, Python, C#, VB.NET, and a variety of other lesser
known langugages), I'm one of the few C++ advocates (though many
project are developed using C++). People tell me C++ is a complicated
language; the syntax is difficult; it's so prone to memory and
resource leaks. However, they are amazed when they see my C++
programs. They compliment me on how simple the program is (frequently
1/3 the size of a similar C program -- sometimes even much smaller;
often smaller and more concise than .NET programs too), how well it
runs, how bug free it is. I'm not trying to toot my own horn, rather
I'm trying to point out that C++ has the tools to make understandable,
robust programs. It's a challenging language to master, but when used
properly is a beautiful tool!

Walter Bright

unread,
Nov 2, 2007, 7:03:17 PM11/2/07
to
Hyman Rosen wrote:
> Yossi Kreinin wrote:
>> The problems with the build speeds of templates are inherent to the
>> design of this language feature
>
> I work with a project that contains many hundreds of C++ files,
> pretty much all of which use class and function templates from
> the standard library and our own. I can do a clean recompile of
> the whole system in ten or fifteen minutes or so on my Solaris
> x86 machine using the Sun CC 11 compiler. For normal work, the
> source compilation time is inconsequential - it's linking that
> takes most of the time, and even that isn't too long.

I've heard many reports of heavily templated C++ code having build times
measured in hours - such as overnight builds. Even so, a build time of
ten minutes is very consequential. Having a build time of a few seconds
can completely transform how one develops code.

I know very well how and why templates slow down compilation, and this
is inherent to how C++ templates work. It is not fixable.

There are other ways to do it, however. Templates are routinely abused
for things they were never meant for, because fundamental language
facilities like function literals are missing from the core language.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Walter Bright

unread,
Nov 2, 2007, 7:02:51 PM11/2/07
to
Jerry Coffin wrote:
> In article <mr-dndWKz9q5sbTa...@comcast.com>,
> wal...@digitalmars-nospamm.com says...
>
> [ ... ]
>
>> I.e. it *is* a problem with the language. The C++ committee recognizes
>> this, as concepts are an attempt to make it easier to generate better
>> error messages. How well this will turn out is too soon to know.
>
> Yes and no. It's true that it's technically too soon to know about
> concepts as part of the official C++ language, because (of course) they
> won't be for a while. OTOH, concepts have been implemented in conceptgcc
> so it's entirely reasonable for just about anybody who cares to download
> a copy and start testing anytime they want to.
>
> My own experience is pretty simple: using conceptgcc, virtually all of
> the hard to read error messages simply disappear, at least for code that
> actually uses concepts. Of course, nothing forces you to do that, so if
> you want to write code that generates arbitrarily lousy error messages,
> you can still do so.

I think it's very good that some of these new features are being tried
out in real implementations before finalizing them in the standard.
Frankly, I think *all* of the proposed new features should be tried out
in a real compiler before voting them in the standard. This would help
avoid disasters like the C++98 export. Even simple things should be
tried out, as it was discovered that the 'auto' feature was
unimplementable as specified.

Although conceptgcc does exist and that is great, realistically, a
complex feature like that needs to be in real use for years by a lot of
people before it becomes clear whether or not it solves the problem. For
example, namespaces were implemented and in wide use for years before
people realized that they didn't really work.

See the infamous "so sue me":
http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/d9eb5db7019673e0/1aaae5a80f53bd4f?hl=en&lnk=st&q=namespaces+scott+meyers+%22so+sue+me%22#1aaae5a80f53bd4f

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Walter Bright

unread,
Nov 2, 2007, 7:03:01 PM11/2/07
to
Eugene Gershnik wrote:
> On Nov 1, 11:45 am, Walter Bright <wal...@digitalmars-nospamm.com>
> wrote:
>
>> 1) D has two kinds of const: const and invariant. Const is, like C++, an
>> immutable view of data. Other views of that same data may be mutable.
>> Invariant represents data that is immutable. C++ does have invariant
>> when const is used as a storage class, but there's no way to declare a
>> pointer to immutable data in C++.
>
> Interesting. Is this distinction primarily to help optimizer or does
> it have some high level design uses?

Invariant references, unlike const, can be used by the optimizer. The
invariant values can be cached, they are thread safe, they won't be
compromised by exceptions, and there's no need to worry about aliases to
them. Invariant references behave a lot like value types. This works out
particularly well for strings, as people intuitively think about strings
as if they were value types. Language after language (Java, Perl, etc.)
successfully treat strings as invariant without user problems. Strings
as mutable types don't work so well (I often run into code where people
defensively dup strings "just to be sure" someone else doesn't change them).

There's no way to have invariant strings with C++ const. This is often
dealt with by adopting a convention that strings are invariant, but
conventions are not reliable, and widely adopted conventions are ripe
for consideration for adopting into the language.

The sole reason to have const is so that the same function can deal with
both invariant and mutable types, as invariant and immutable can both be
implicitly converted to const.

The other advantages of invariant is it opens the door to being able to
prove things about functions, something that has been very elusive for
C++. It also opens the door to functional programming, a paradigm that
is not available for C++.


>> 2) const (and invariant) in D is transitive. That means if you have a
>> pointer to const T, anything reachable through that pointer cannot be
>> mutated. This is different from C++, where although T is const, nothing
>> T refers to is necessarily const.
>
> Without knowledge of D it is hard to tell whether this would work. How
> do you model
> 'immutable X that knows about mutable Y'?

The const X would have read only access to mutable Y. There is no way to
modify Y through const X. If you want to mutate Y through X, then X must
not be const.

This seems at first blush like "no way, this is a crippling
shortcoming." But is it? All I can say is look at real code, and look
for legitimate use cases of modifying a referenced object through a
const reference.


>> One controversial thing that D's const doesn't have is the 'mutable'
>> keyword which is there to support the notion of 'logical constness'. It
>> is a deliberate design choice to not support logical constness with the
>> const system.
>
> Hmmm. Rather than give the obligatory 'string that caches the length'
> example let me give you something closer to what I usually see
>
> class Mutex
> {
> void Lock(); //non const, changes the mutex
> void Unlock(); //non const, changes the mutex
> };
>
> class SynchronizedInt
> {
> int value;
> Mutex mutex;
>
> int ReadValue() const //doesn't change value
> {
> Lock lock(mutex); //for this to work mutex must be mutable
> return value;
> }
>
> };
>
> How do you do it in D?

You drop the 'const' from ReadValue(), because it is mutating the
instance of SynchronizedInt. Essentially, D doesn't support the notion
of an object pretending to be const, only objects that actually are. The
idea is that if you see a function signature that says its taking a
const argument, it really truly cross-my-heart promises that it *will
not* be mutating it.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Eugene Gershnik

unread,
Nov 2, 2007, 7:01:43 PM11/2/07
to
Yossi Kreinin wrote:
> On Nov 1, 1:35 am, "Eugene Gershnik" <gersh...@gmail.com> wrote:
>> Yossi Kreinin wrote:

> [...] for me, personally, the ways of getting const


> right are not the main theme of this discussion; the main theme is
> what happens when you get const wrong in the language.

Fair enough but you still didn't demonstrate that the const is done
wrong in
C++.

>> I don't know what "usability" is for you and how you define it. If
>> you can provide a good measurable definition please provide it.
>> Otherwise I can and do claim that const imporves 'usablity' as I
>> understand it.
>

[...]

> Reasonably quantifying the importance of such things,
> however, is impossible since approximating the distribution of the
> "relevant" programs and programmers is impossible.

So you agree that usability is in the eyes of the beholder. Good. In this
case can we drop this term from the discussion?

> What I claim is this. Most languages don't have const and most
> programmers consider this tolerable.

And what is the technical value of this argument? How is what most
people do
(they write in-house unmaintainable, code in VB actually) relevant to
anything. This is a common fallacy called, I believe, "Appeal To Widespread
Belief".

> However,
> when you have levels of indirection, you simply can't do "the right
> thing", which can be objectively demonstrated,

You have demonstrated nothing of the sort so far. Repeating an assertion
doesn't make it true.

>> What I do know is that a language should allow one to express design
>> concepts as clearly as possible. There is an exteremely important
>> concept like 'this thing is supposed to be logically unchanged' in
>> any design. One ought to be able to express it at the language level.
>
> ...But you wouldn't quantify /that/ knowledge (by measuring the
> benefits of "clear expression", for example), would you? :)

Sure I would. I am paid N dollars for every minute of work. Therefore every
minute I spend trying to figure out non-obvious things, typing code that I
shouldn't have, verifying things that ought to be known etc. costs my
employer N dollars.
If the code requires lots of external documentation (not the problem domain
one but the kind that explains what code is doing) or if the meaning of
expressions can only be figured from their implemenation and usage then
this
is going to be money wasted. Clear code, that is the code which is easy to
understand simply costs less money.
Note that this concept is only a part of a larger concept of
maintainability. The full maintainability also deals with thing like how
expensive is any given change to the entire system which are outside of the
language issues.
Based with this principle every language feature and every approach to
writing code can be analyzed to see how much money it wastes.

> However, these
> specifications can also cause problems, by not exactly matching the
> constraints you'd really like to specify,

An example please?

> and/or by simply cluttering
> the code and/or producing compile time error messages where no run
> time error would occur.

This is backwards. You seem to think that runtime error is the only error
worth worrying about. Almost every time you get a "const conflict" in your
code where you absolutely must cast it reveals a design problem. Which is
far more important in the long run than a runtime failure.

>>> From my experience, a
>>> language without const is sure usable.
>>
>> For *your* definition of usable and from *your* experience.
>
> Well, quantifying is hard, but in this particular case, you have to
> agree that lots of people using Java, C#, VB, Perl, Python, Ruby and
> JavaScript (among lots of other languages) don't care much about not
> having const.

"Appeal To Widespread Belief" again.

>> To address your confusion about levels of indirection, in C++
>>
>> struct Foo
>> {
>> Bar * pBar;
>>
>> };
>>
>> doesn't mean 'Bar is a part of Foo' or 'Bar is owned by Foo' as
>> people coming from other languages often believe. What it really
>> means is 'Foo knows about Bar'. As such, immutability of Foo doesn't
>> mean immutability of Bar.
>
> I don't have any "confusion" about this, I know exactly what const
> means for years,

You missed the point entirely. The above wasn't about const. It was about
your famous levels of indirection. In short: if you use raw pointers to
model relationships other than 'knows about' you will have to do lots of
manual work which includes manually adjusting const-ness. And yes it is a
language deficiency that we don't have other kinds of pointers. Thankfully
this can be addressed almost completely by creating appropriate smart
pointers.

> Here's an example which I brought up earlier in the thread - a real
> example of a simple attempt to use const and the standard library
> given the C++ definition of const you've just correctly described:
>
> http://yosefk.com/c++fqa/const.html#fqa-18.1
>
> Why isn't this a problem?

Because of a fundamental principle that if Foo IS-A Bar then a
Collection<Foo> *not* IS-A Collection<Bar>. In other words your expectation
that this "ought to work" is similar to a beginner expectation that you can
pass an Array<Apple> to a function that expects an Array<Fruit>.
You can argue with this princple but it is a widely followed one (see I can
use "Appeal To Widespread Belief" too).
The solution is simply to have a function that takes an Array<Anything> or
(in a better language) an Array<Anything which IS-A Fruit>. This is exactly
what you have been offered here and if it isn't good enough for you there
isn't anything left to discuss.

> Why wouldn't having no const in the language
> be better /in terms of clarity/? Why is /not knowing/ from the
> function prototype that something isn't going to change so bad,

Consider this idiomatic Java or C#.

Bar bar = foo.getBar();
....
bar.setBogosity(42);

Did you just modify foo or didn't you? You will have to either read the
documentation or implementation to find out. Which will take ~2 minutes and
so cost 2N dollars. Feel free to pass bar to any function to complicate
things even further and increase the cost. Since such constructs are very
widespread your cost will grow up very quickly. This is the reason for the
advice "make Bar immutable to avoid this issue altogether.

> especially considering the fact that you /yourself/ give an advice to
> cast const away in the body of the function?

No this is not what I have meant. What I did mean was that if you need to
interoprate with const-less library that you know won't change the data you
can cast away const on the boundary. This is no different from thousands of
other 'boundary' issues (translating error handling for example) that you
need to do on module boundary.
This and implementing non-const function in terms of const one are the only
acceptable uses of const_cast. And yes the latter is a language deficiency.

> Furthermore, why is it "good" that to implement an STL-like container,
> you need to implement an iterator and a const_iterator?

First of all you don't. I have plenty of read-only containers that only
supply one.
Second, so what? In one case you specify how to iterate sequence of
read-only entities and in another how to iterate sequence of things that
can
be changed. They don't even have to do the same thing (though STL tries
hard
to limit our freedom there)

> What happens
> if you have a std::vector-like container that can get the storage
> pointer from outside (as opposed to allocating it in the constructor),
> and we want to attach a const storage pointer to it in some contexts
> and non-const storage pointer in other contexts? Should we have a
> vector and a const_vector?

I don't completely understand what you mean by this. What I think you are
trying to describe can be modelled as Container<T> and Container<const T>.

> Writing smart pointer classes to avoid the problems with const is what
> I call fighting the language.

Other people would call it extending. But I do agree that having other
kinds
of pointers in the language itself would be better.

> IMO, the readability gained from understanding which objects
> can be modified by a piece of code is lost many times over by having
> much more code.

FUD. One tiny class that is polished once and never changes again is not
"much more code".

> Writing smart pointer classes to deal with const correctness is also
> manual work.

Once for a given organization. With some luck you can probably find one
already written. The total cost won't exceed ~60N.

> a) Garbage collectors don't work with C++ in practice, see another
> sub- thread.

How did you arrive to this conclusion? I distinctly remember people here
posting that they do in fact use garbage collectors.

> b) This has nothing to do with garbage collection.

Oh it does. If you are going to new everything you better have some form of
GC.

> If C++ didn't have
> const like most languages, and you had a void func(ClassName&), then
> func(ClassName(args)) would work just like func(new ClassName(args)).

You complained that it is impossible to write code while avoiding const. I
have shown you the way which is exactly what you do in other languages. Now
you seem to want to use value-based semantics for this too. Sorry, but you
can't. You don't have value based in most other languages either so I am
not
sure what is there to complain about.

>>> and if you need libraries that use const, it will get worse):
>>
>> Why worse? Do what you do every two lines in other languages: cast.
>
> I don't do it in other languages,

Really? No casts in Java?

>> Are you talking about the language or particular compilers?
>> Incidentally this confusion is all over your site.
>
> This isn't "confusion", either. For someone actually writing software,
> a language is only as good as the best implementations you can use in
> practice. And C++ is old enough and popular enough to give people
> enough time and motivation to create high quality implementations.
> Whatever doesn't work smoothly by now has low chances to improve
> significantly in the future.

Very wrong. Commercial vendors with large resources all almost abandoned
C++
since mid 90-s to pursue their Java, .NET and other dreams. If
Microsoft, to
give one example, would have poured one tenth of the resources they devoted
to .NET into their C++ compiler and libraries we would have seen very
different results now.
In any event blaming language for poor quality implementation without
saying
so prominently is at best intellectually dishonest. Why didn't you put C++
COMPILERS SUCK on your site?

>
> The problems with the build speeds of templates are inherent to the
> design of this language feature:
>
> http://yosefk.com/c++fqa/templates.html#fqa-35.12

I won't even bother to comment on this one except pointing out that
there is
at least one compiler (produced by 3 people with tiny budget I believe)
that
does implement export.

> I've spent lots and lots of time working with C++, and I understand
> your position on this issue quite well. You value precise static
> typing (which you call "clear expression")

Nope. Static typing is not "clear expression", only a part of it.

> very high and the actual
> problems of "implementations" very low,

No. I know and care about practical problems as well as you do and I often
have to work around them too. However, I don't think they are as
troublesome
as you claim.

> nor do you acknowledge that
> these problems really come from the language definition.

They are in some cases. However, your site and postings here don't contain
anything to confirm or refute this fact.

> You're also
> very tolerant to the need to write wrapper code, and prefer the
> benefits of precise modeling to the drawback of having more code to
> read and maintain.

Not exactly. I distinguish between stable library code and volatile
application one. Stable library code (like for example a smart pointer) is
written once and never changes. Its maintainance costs are very close to
zero after that. (Change in a compiler might sometimes cause a change but
this is again small fixed cost). Application code changes often and there
indeed more code means more maintenance costs.

--
Eugene

David Abrahams

unread,
Nov 2, 2007, 10:23:00 PM11/2/07
to

on Fri Nov 02 2007, Yossi Kreinin <yossi.kreinin-AT-gmail.com> wrote:

> What I claim is this. Most languages don't have const and most
> programmers consider this tolerable. C++ does have const, which
> according to most programmers helps in many simple cases. However,
> when you have levels of indirection, you simply can't do "the right
> thing", which can be objectively demonstrated, and programmers end up
> copying objects or casting pointers or making everything template,
> each which has it's own problems and is a way to work around the
> problem, not solve it.

The problem that "making everything a template" addresses would not be
solved by transitive const, as you seem to imply. Templatizing can be
used to deal with the kind of code repetition you get in the const and
non-const versions of a class' operator[], where you can't just use
one non-templated declaration because you need to return either a
constant or a mutable reference depending on the constness of *this.

Frankly, any solution to that problem is going to look a lot like a
template, because that's the only way to serve up different return
types from "one function." I could imagine some nice syntactic
shortcuts that would allow me to put a templated const-ifying shim
over a non-templated function without ever writing angle brackets, but
conceptually, there's still a template there. Incidentally, I wish we
had cv-qualification as a template parameter ;-)

As for the issue of casting pointers, the way const works in C++
almost never forces you to cast; if you often find yourself casting
away const in C++, you're almost certainly doing something very wrong.
That sort of thing should only happen in the gnarliest low-level
systems code and when interfacing with legacy systems that don't know
about const.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

Yossi Kreinin

unread,
Nov 2, 2007, 10:27:23 PM11/2/07
to
On Nov 3, 12:58 am, khall.ggr...@gmail.com wrote:

In general, in this message you argue with those of my statements that
aren't supposed to describe "facts" in the sense that they can't be
proved/refuted formally or experimentally in a feasible way, so I
won't counter in these cases.

Note that most of the FQA answers don't claim that the FAQ answers are
wrong, just mention things it omits (which I think is fairly important
because you can do just about anything using omissions). One example
where this is not the case (the FQA actually /corrects/ the FAQ as
opposed to adding information) is on the "opaque pointer" issue:

http://yosefk.com/c++fqa/class.html#fqa-7.5

>
> [6.1] "Is C++ a practical language?"
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>

> I strongly disagree with that. All you need to do is go to
> SourceForge.Net and go to "software development"->"Debugggers" and see
> how many items are related to C++.

The availability of many tools doesn't mean they are good, just that
people need tools (which is obvious - C++ is used quite widely). Lots
of free debuggers are based on gdb, which means that /most of them/
can't place breakpoints in templates, for example (some vendors hack
on gdb to fix some of the problems, apparently without committing the
changes to the main branch).

> Visual Studio 6.0 to Visual Studio
> 2008 offer incredible capabilities in debugging, with VS2008 showing
> many very cool new features. There are many non-MS and *nix tools
> available too: CodeBlocks, KDevelop, gcc, BoundsChecker, Electric
> Fence, and the list goes on and on and on....

I used VS a lot. For starters, auto-completion and definition look-up
has never been very reliable. I don't blame VS; the problem it faces
is extremely complicated. Free tools doing similar things are a bad
joke.

All of this is of course qualitative, and code bases may exist where
the problems manifest themselves less than in other code bases.
Typically, the amount of problems in this department is proportionate
to the complexity and the number of syntactic C++ features used.

The debugger's problem is an easier one, since the program was already
compiled and all references were resolved in a lengthy process.
However, things like parsing expressions in data display windows will
still cause problems (the debugger has to parse C++ type
specifications, expand macros and typedefs, etc.; some of it usually
does not work).

Note that VS only works for Windows-based targets, and many of them
aren't. In the area of cross-platform/embedded development, tools
based on gcc/gdb/Eclipse combos are quite common; I'd say they are
quite poor at the moment compared to VS. Proprietary debuggers are
better, but they have smaller user base than, say, VS, and will
normally fail in more kinds of creative ways.

>
> You also say "C++ is not "mature" in the sense that different
> compilers will interpret it differently". While the very advanced
> parts of the language may act different on some compilers, most behave
> similarly for parts of the language that produce defined behavior.

There's plenty of undefined behavior in C++ (consider visibility of
overloads and template specializations at the point of usage), and
lots of dark areas easy to stumble upon in relatively naive code
(overload resolution and implicit casting, for example). Even porting
from gcc 3 to gcc 4 may be quite tedious. /All/ compilers I've used
crashed on C++ code of moderate complexity (nothing approaching the
levels of boost, for example; boost libraries are extensively tested
on many compilers and contain lots of compiler-specific build logic).

>
> C++ is powerful (boost::spirit and
> boost::mpl are amazing examples)

I find "Amazing" an appropriate term; what interests me is "wise to
use in real world prototype or production code", though.

> And what does a managed environment buy, do tell? If a program is ill
> formed in a managed environment, the environment may tell you more
> information when the programs behaves incorrectly, but the program
> still behaves incorrectly.

Not just that - the execution will /stop/. This is tremendously
helpful in real life. Experience with /all/ software vendors shows
that you invariably ship ill-formed programs (and I'd say that most
"stable" program versions out there are /still/ ill-formed, it just
manifests itself very rarely). Denying this (as done by proponents of
"performance" as well as many professional methodologists) is very
counter-productive.

Now, in an unmanaged environment, many ill-formed programs keep
running after committing the first "crime" (actually performing an
illegal operation). This means that understanding the problem will be
harder since the program "covers its traces" (destroys the objects
related to the illegal operation), and may in fact never crash, but
instead crash a plane or zero out your savings in a bank account,
depending on the domain.

Of course in many domains unmanaged environments are unavoidable, and
I happen to work on this kind of code myself. However, having lots of
experience with the problems it causes, I claim that most people
greatly underestimate them.

> The managed environment may have garbage
> collection, but there are garbage collectors available for C++ too.

In theory; did you use one in your project? See also adjacent sub-
threads.

>
> And, by the way, C++/CLI does run in managed environments.

C++/CLI is not C++. "Managed C++" isn't C++ either, and some of it
compiles to unmanaged code; I didn't check if the same is true for C++/
CLI.

>
> You also state "Providing C++ interfaces to a software component is
> impossible in practice due to lack of compile time and run time
> interoperability". That's just simply wrong! My proof is the vast
> number of sucessful C++ projects. What's your proof?

Try to compile libraries in gcc and use them with VC++.

Your proof shows that you can ship C++ software (empirically true for
many languages you wouldn't use), not libraries with C++ interfaces.

> There's only one doubly-linked list type, only
> one resizable array with O(1) random access, only one double-ended
> queue, etc....

There's a huge amount of pre-standard (and post-standard) vector,
list, string and map classes, used in lots of software.

> My
> biggest complaint about most recent languages is their lack of
> 'const'. I'm glad D didn't sacrafice this!
>

D changes the definition of const, however, which has the potential to
solve serious usability problems with the C++ const (although I can't
cite my experience or analysis proving it); see adjacent threads.

>
> [6.14]
> ~~~~~~
>
> You state "Asking about things specific to C++ is not very useful."
> That's not true at all. You acknowledge earlier in the FAQ that it
> takes a long time to become proficient in C++. If a company already
> has a C++ project that must be maintained, then simply grabbing a
> talented person who isn't familliar with C++ is a poor buisness
> decision.

I agree; if you want someone to maintain a complicated C++ code base,
you may need to ask these things. But this is far after the point
where language choice is relevant for you, and if your code is
complicated enough for deep knowledge of C++ to matter, you've already
made a poor business decision previously.

I know may kinds (and "instances") of great programmers who'll
probably never know C++ anywhere near my level, which is not at all
close to "completeness". Losing the whole lot of them to your
competitor is a very bad scenario:

file:///E:/sw/fqa/faq.html#faq-4

> First, you can't really tell us what someone else means. That's like
> me telling you that what you really mean by your FQA is that "C++
> sucks", despite your disclaimers to the contrary. This sounds just
> like another rant.

/Of course/ I can tell what someone else means, by analyzing all
places where something is said and summarizing the kinds of contexts.
So can you; in your example, I'd counter with the fact that there's
lots of factual information in addition to the clearly subjective
kind.

> The FAQ afterall is not some hallowed document -- it's just one man's
> opinion. Other people will agree with the FAQ to varying degrees. I
> agree with most, but certainly not all of it.
>

Well, so is any document, including the FQA...

Walter Bright

unread,
Nov 2, 2007, 10:29:43 PM11/2/07
to
James Hopkin wrote:
> On Nov 2, 4:42 pm, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
>> What about when you need a subset of objects kept in std::vector or
>> std::list, and the owning collection is about to be deallocated,
>> because you only need some of the objects, but not all of them
>> anymore?
>>
>> http://yosefk.com/c++fqa/dtor.html#fqa-11.1
>>
>> Do you copy the objects, which has a performance penalty, or keep
>> collections of pointers, so you have no RAII coming from the standard
>> library to help you with exception safety anymore, or do you use smart
>> pointer classes which do reference counting, which breaks when you
>> have cyclic references?
>>
>
> With std::list you can use splice. As for contiguous containers
> (vector, deque) - that's the tradeoff you make for the advantages of
> contiguous allocation.

You can do slicing (taking a contiguous subset of an array of objects)
with contiguous allocation if you have garbage collection. Slicing of
strings and such has turned out to be an enormously effective technique,
as it requires no allocations, copying, reference counting, templates,
etc. It's a big advantage of gc that is not practical with explicit
memory management using smart pointers, reference counting, RAII, etc.

> None of the other popular languages give you
> this ability (or only for a very restricted set of types like C# value
> types).

The D programming language offers slicing for all array types.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Yossi Kreinin

unread,
Nov 2, 2007, 10:42:41 PM11/2/07
to
On Nov 2, 6:35 pm, Hyman Rosen <hyro...@mail.com> wrote:
> vector<T *> vp;
> vector<const T *> vcp;
>
> You know, you keep carping about how these two types are
> incompatible, as if it were a given that this should not
> be so. But in fact, the two classes can be very different,
> because C++ allows specialization. It's not the case for
> std::vector, but it can be the case for other classes.

I am aware of the freedom you have with template specializations, as
well as some of its consequences:

http://yosefk.com/c++fqa/web-vs-c++.html#misfeature-3

I also think typing problems discussed in this sub-thread reproduce
with built-in arrays, which have somewhat similar pairs of
"incompatible" types - an array of const pointers is /not/ a special
case of an array of pointers (and it shouldn't be since that would
allow you to fill an array of non-constant pointers with pointers to
constant objects and then modify the objects in another context):

http://yosefk.com/c++fqa/const.html#fqa-18.17

I'm not questioning the reasons making these pairs of types
"unrelated"/"incompatible", just claim that it makes const unhelpful
when you have levels of indirection. I prefer to use std::vector<T*>
rather than T** in the examples since otherwise many C++ programmers
would simply recommend to avoid built-in arrays.

Alf P. Steinbach

unread,
Nov 2, 2007, 10:42:42 PM11/2/07
to
* Yossi Kreinin:
> On Oct 29, 3:22 am, "Alf P. Steinbach" <al...@start.no> wrote:
>> A Frequently Questioned Answers is a very good idea, and I commend
>> the
>> effort you've put into this.
> Thanks!

>>> One thing I'm definitely interested in is whether I got
>>> any facts wrong, and if I did, which ones.
>> Well, most of it. ;-)

>>
> Well, I meant "facts" :) My definition of "facts" in this context is
> here (together with the first factual correction submitted by a
> reader):
> http://yosefk.com/c++fqa/web-vs-fqa.html

Why not quote that definition: «I mean statements which can be proved
wrong or refuted by a practically feasible test.»

Starting with the first sentence of the first FQA item, numbered 6.1,
«C++ is not "mature" in the sense that different compilers will
interpret it differently, and C++ modules built by different vendors
will not work with each other"». If the latter part of the sentence
is meant to be existentially qualified, that it's possible to write
modules that will not work with each other, then it's meaningless. If
on the other hand it is meant to be universally quantified, that it's
impossible that different vendors write modules that work with each
other, then that's demonstrably false. So as it stands it's either
meaningless or false. It is however not difficult to understand what
is probably meant, that C++ lacks an official (e.g. ISO) standard for
compiled code (an ABI). As it is, one must rely on de facto
standards. But this best-meaning interpretation doesn't help the fact
that the FAQ's statement as it stands is either meaningless or plain
false.

Next sentence, «C++ is not "well-supported" in the sense that

development tools for C++ lack features and are unreliable compared to

other languages.» I would tend to agree with the features
(functionality that requires parsing of C++ code is generally lacking,
due to the complexity of a C++ parser) but not with reliability (on
the contrary). However, this is just opinion that cannot be proven or
disproven. It may be that the author has only used low quality tools.

Third sentence, «These things make one ask "Am I the first one trying
to do this?" all the time.» If this sentence is about the author's
experience then it is presumably true, but not falsifiable. :-) If it
correctly relates the author's experience, rest assured that you're
not the first, and that there are also good tools for C++.

And so on: of the first three sentences, one was either meaningless or
false, and two were not falsifiable.

>> One main high level issue that the FAQ gets wrong is the purpose of a
>> programming language. In general it's a good idea to use the most
>> appropriate language for a task, but your FQA seems to assume that
>> somehow everything has to be done in C++.
> The FQA doesn't say that everything has to be done in one language
> anywhere, and I use several languages myself. The FQA says that /
> nothing/ should be done in C++. This does not imply that "if C++ were
> good, you'd have to do anything in it" or anything like that.
>> Garbage collectors such as the Boehm collector work well.
> One problem is that several libraries may be built to use different
> garbage collectors. Garbage collectors are not entirely non-intrusive;
> they may ask the user to provide some kind of layout information or
> finalization functions, for example. Having many of them in one
> program is thus problematic.

I wouldn't know because I haven't used garbage collection with C++.
The only person I know who does use garbage collection in C++ is James
Kanze. And he has not reported any such problems, instead
recommending garbage collection to newbies.


> Non-intrusive garbage collectors have false positives (some things
> look like pointers, but aren't), and fail when people use formally
> illegal, but normally working techniques like int* one_based_vector =
> (int*)malloc(sz)-1;

I hope James will answer that.


> One thing C++ garbage collectors can't do is heap compaction. AFAIK
> this increases the upper bound on the memory consumed by a program
> with the peak sum of allocated object sizes equal to M from M to
> M*log(max_block_size), which is a huge difference for realistic values
> of max_block_size.

There is nary a computer science problem that can't be solved by
adding a level of indirection. Modern computers typically have built-
in memory management that adds just such a level of indirection. In
the old days before that hardware became commonplace on PC's we had to
use handles to memory (instead of direct pointers), in order to allow
compaction.

With extreme fragmentation and large underlying page size it would be
possible to consume a lot of memory, as opposed to address space.

However, I don't think that's something that a language solves for you.

> However, for
>> many C++ programmers garbage collection is not desirable, because
>> while
>> it can ensure that memory is always reclaimed, it leads to other
>> problems.
> If you refer to /hard/ real time applications (most large ones are
> really /soft/ real time), than AFAIK operator new has the same
> problems in terms of worst case performance. If you refer to resource


> leaks when resources!=memory, then I have to point out that the vast

> majority of resources /is/ memory, and very basic language constructs


> (such as new) depend on being able to acquire memory, and not having a
> simple solution for memory management is likely the single biggest
> problem with "low-level" languages when you try to do "high-level"
> work in them (but C++ is promoted as a pretty high-level language).

> IMO not acknowledging the difference between "memory" and "resource"
> is a manifestation of perfectionism ("solutions that only solve 95% of
> the problem are worthless"), and there's plenty of such things in C++.

Unpredictable and jerky response time is one consideration, yes. But
I was mainly thinking of the impact on design when objects hold
references to non-memory resources. In e.g. Java this leads to
"destroy" methods all over the place, where a call to such a method
zombifies the object. This in turns leads to checks of whether the
object is a zombie, resulting in complication, and undetected invalid
usage of the zombified object.

C# helps a little with its "using" statement, but that's mostly
cosmetic.

My ideal garbage collector for C++ would be one that addressed one
problem only, that of doing actual memory reclamation at idle times.
Such a garbage collector would not invoke destructors. Instead,
memory freed by operator delete or C free would by default be passed
(constant time) to a list, traversed by the garbage collector at
suitable times.

>> >From the FQA:
>>> And what if you make a mistake, one single mistake with all these
>>> deletions?
>> with proper RAII, if an object is still alive, then its class
>> invariant
>> still holds.
> RAII does not at all prevent problems with dangling references. If you
> keep a pointer into an object in an STL container, which acquires
> memory in the constructor and deallocates its memory in the destructor
> (which is what RAII means), and the container dies, you have a
> dangling reference. Which is why C++ programs will frequently make
> themselves private copies of objects - pointing to something you don't
> "own" is dangerous.

You're right that RAII does not prevent problems, it just helps solve
them. Garbage collection doesn't prevent problems either (in
particular early versions of Java's Swing library tended to leak
memory by keeping references to old objects in singletons, garbage
collection didn't prevent that). The solution is in general not to
copy data. Since you assert that that's a common solution it seems
you have been exposed to some really bad programming practices. But
that's not a feature of a language: incompetence is a feature of
programmers.


> You can implement reference counting on top of RAII, making all your
> pointers "smart" (and having the usual trouble when integrating with
> other libraries of equally smart though different, or, most often,
> dumb bare pointers). Then you'll have the problem with cyclic
> references (which, by the way, is AFAIK the single case when a GC
> could leave "zombie objects" around - and note that it took us way
> less work to get to this stage).

Curiously, I've never had a problem with cyclic references in C++. On
the other hand, it is true that blind application of unsuitable
reference counted smart pointers runs a good chance of creating such a
problem. And I've helped resolve such problems created with reference
counting in other languages, mainly Visual Basic. Again it's not the
language's fault, but generally incompetence on the part of the
programmer.

And no, GC doesn't create zombie objects (programmers do), and no, it
doesn't have anything to do with cyclic references.

A zombie object results from manual logical destruction (in order to
release non-memory resources), leaving an existing, zombie, object.

Cheers, & hth.,

- Alf


--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Yossi Kreinin

unread,
Nov 3, 2007, 11:39:00 AM11/3/07
to
On Nov 2, 6:36 pm, khall.ggr...@gmail.com wrote:
>
> Recompilation can be avoided by using the PIMPL pattern:http://en.wikipedia.org/wiki/Opaque_pointer.
> This problem is not unique to C++; C has similar problems.
>

I'm aware of this idiom, and of the solution where you have an
abstract class to decouple interface from implementation, however,
both require you to write wrapper code:

http://yosefk.com/c++fqa/inheritance-abstract.html#fqa-22.1
http://yosefk.com/c++fqa/heap.html#fqa-16.21

C does have this problem, however, its grammar is much simpler, so
recompilation caused by changing the definitions of structures is less
costly:

http://yosefk.com/c++fqa/defective.html#defect-2

In C, there's also a simpler solution - incomplete types (naturally,
you can use it in C++, too if you avoid normal C++ classes and class
members):

http://yosefk.com/c++fqa/class.html#fqa-7.5

Note that if you program in C and place structures in header files, at
least module functions which aren't part of the interface (which
correspond to private methods of C++ classes) can be added/removed/
undergo changes in the prototype without triggering recompilation.
Only changes to structure variable definitions will cause a rebuild.

> First off, there is no restriction that says operators must return by
> value.

I was referring to operators that /compute new values/: a+b*c, the way
built-in operators do. Overloading operator<< to do formatting is a
very special case - if it weren't in the C++ standard, many would
claim that it makes code less readable, defeating the purpose of
operator overloading (I can't quantify "many", but a common advice is
to overload operators such that their function resembles that of built-
in operators).

>
> Also, smart pointers can be used when needing to return "new'd"
> pointers. There's one in the current standard (std::auto_ptr) and a
> few more that will be in the next standard.

Such a smart pointer should do reference counting to avoid unnecessary
copying in the case where a+b*c compute new matrices. If the matrices
are known to be small enough (say, 5x5), new'ing them and maintaining
the reference counts can defeat the purpose, since it's comparable to
simply copying the objects returned by value. AFAIK the only simple
way to guarantee fast execution is something like:

matrix prod, sum; //a built-in fixed size array or a template
mat_mul(prod, b, c); //passed by reference
mat_add(sum, prod, a);

Of course you can have very elaborate library code trying to optimize
various cases of matrix expressions and still have operator
overloading (copying is one problem, mapping the two operators in a
+b*c to a single specialized function optimizing this particular
expression is another one). I claim that if performance is important
to you, so is the ability to easily estimate it by looking at the
code, and hence you'd rather use more straight-forward methods in
production code, and keep the operators in the comments and prototype
code.

>
> The RAII idiom works very well.

RAII works well when there's a single resource owner, and others don't
have to reference the resource after it dies. Most of the "resources"
are simply objects in memory, and sometimes their life cycles are
complicated. You end up copying the objects (slow) or maintain
reference counts (doesn't work with cyclic references):

http://yosefk.com/c++fqa/dtor.html#fqa-11.1

In other cases, you simply end up having dangling references.

I'm glad that it worked for you personally, of course others could
counter with their experience, but discussing the differences between
projects is hard and probably off-topic.

> Similar problems exist in other languages that have
> exceptions too. How does one release a resource in C# when an
> exception occurs? Via a 'finally' statement or a 'using' statement.

The thing is that in languages with garbage collection, "memory" is
not a "resource". This may sound "non-generic" and arbitrary; well, C
and C++ don't treat /stack/ memory as resource, and stacks do
overflow, and sometimes you wish you could handle it more gracefully
than it is handled in most C and C++ implementations. Anyway, the call
stack is special because function calls depend on it to work. Memory
is also a very special kind of resource, because new depends on it,
and the ability to create objects is a very basic one. Having to think
about the way they are disposed is really very tedious (this statement
is of course subjective).

Now, other resources are very different. They are typically more
scarce than memory, and you use less of them. They are handles; you
don't need to think about "resource fragmentation" (and memory
fragmentation is a big problem, especially when it comes to worst case
memory usage; a single built-in "dumb" pointer makes automatic heap
compaction impossible in C++).

And most importantly (and of course arguably :) ), the use of those
other resources /shouldn't/ be "transparent". If a module opens files
or database connections, the calling code should be aware of it at the
"social" (human) level, and carefully handle finalization. "Resources"
are a heavy thing; you don't want someone to mistake an object that
needs resources for a "lightweight" object and create a zillion of
them. Furthermore, it's frequently a good thing to separate the code
doing I/O (which many resources are related to) from the larger bulk
of code processing the data. Synchronization primitives are a special
thing, of which there simply should be few (see the advocacy behind
Intel's TBB library, for example).

> No language feature is 'evil' (the intent with which it is used may be --
> see obfuscated C contest ;] ).
>

The word "evil" is used by C++ FAQ Lite over and over again:

http://yosefk.com/c++fqa/picture.html#fqa-6.15

Many C++ programmers would agree in the sense that they'd go to great
lengths to avoid the features called "evil" by the FAQ, almost
independently of the degree of applicability of these features to the
specific problem at hand. Of course there are other attitudes, too.

> Note also that the reason std::string and std::vector exist is because
> the C facilities have their shortcomings: namely the inability to
> resize themselves. You're right, the std::string and std::vector are
> in some ways inferior to char* and arrays, but in some ways they are
> superior too. There's a tradeoff that must be considered.

I realize that, and in several places warn against "banning" the
criticized C++ features in local coding conventions or similar:

http://yosefk.com/c++fqa/const.html#fqa-18.1
http://yosefk.com/c++fqa/ref.html#fqa-8.6
http://yosefk.com/c++fqa/inline.html#fqa-9.5
http://yosefk.com/c++fqa/ctors.html#fqa-10.6

All I'm saying is that for me, a language user, having C features and
standard library duplicated almost entirely by new C++ features and
standard library causes trouble, while for the designers/promoters of C
++, this was quite beneficial.

>
> You said "C++ doesn't add any built-in types to C."
> What about 'bool'?

Strictly speaking, you are right, and I think I'll publish a
correction. This doesn't contradict the meaning of what the item about
no high-level built-in types in Defective C++ says, however. bool is
very close to the built-in types already in C in terms of its
behavior.

Somewhere else in the FQA there's an amendment about how C++ /is/
willing to add new built-in types if they're a new kind of pointer,
which of course doesn't cover bool.

> Also, depending on your definition of "built in", you could include
> all the standard library classes, which actually is quite a large
> number (std::string, std::ifstream, std::ostream, etc...)

This isn't in my definition of "built-in" in the context of the text
we are discussing - the text explains why:

http://yosefk.com/c++fqa/defective.html#defect-12

>
> Macros are not lame at all. They are useful when created domain
> specific languages (http://en.wikipedia.org/wiki/
> Domain_Specific_Language) as well as helping out sometimes with other
> tasks.

What I really meant was that C macros have well-known and unavoidable
shortcomings. They are more useful than nothing, and I use them myself
at times.

>
> By the way, macros and templates make C and C++ two of the few
> languages to be able to create DSLs that are parsed by the compilers
> themselves rather than having to build a program or library to parse
> DSL. That's nifty (and useful)!

I agree with the spirit (DSLs are useful), but not with the facts :)
With C macros and C++ templates, simple facilities like loops are
something either hard or impossible to implement, depending on the
context. Now, most languages don't have better macros (but Lisp and
Forth dialects do, for example). However, many languages have an eval
function or other ways to compile/execute code generated
programmatically, which gives more options than C++ meta-programming
facilities. Also, sometimes data-driven programming is a sufficient
"approximation" for DSLs (and even has its bonus points such as easier
analysis of the "code"), and many languages support data
specifications better than C++ (having list/dictionary literals helps
tremendously).

>
> I could continue on, but I need to go to bed now as I'm falling asleep
> typing.

Good night, and thanks for a friendly message! :)

Eugene Gershnik

unread,
Nov 3, 2007, 12:42:53 PM11/3/07
to
Walter Bright wrote:

> The const X would have read only access to mutable Y. There is no way
> to modify Y through const X. If you want to mutate Y through X, then
> X must not be const.

[...]

> Essentially, D doesn't support the notion
> of an object pretending to be const, only objects that actually are.
> The idea is that if you see a function signature that says its taking
> a const argument, it really truly cross-my-heart promises that it *will
> not* be mutating it.


I see. IOW in D const means physical rather than logical immutability
and it
is not orthogonal to object relationships.
Well these features are not something I am personally interested in since I
don't see any value in either. Actually I see great reduction in value
compared to C++ const.
Obviously YMMV

--
Eugene

Walter Bright

unread,
Nov 3, 2007, 12:42:58 PM11/3/07
to
Eugene Gershnik wrote:
>> The problems with the build speeds of templates are inherent to the
>> design of this language feature:
>>
>> http://yosefk.com/c++fqa/templates.html#fqa-35.12
>
> I won't even bother to comment on this one except pointing out that
> there is
> at least one compiler (produced by 3 people with tiny budget I believe)
> that does implement export.


Is there any evidence that export in practice improves build times?

I've looked in to implementing export. Based on how it must work, there
is not much prospect of it fundamentally improving build times. And
seeing how export has been available for several years now, yet has
failed to catch fire, doesn't auger well for its utility.

What is proven to substantially reduce build times are precompiled
headers (but p.h., in order to work, has to place restrictions on what
can be put into headers). Even so, templates have other fundamental
problems that cause something like the factorial template to run 10,000
times slower than compiled factorial code.

P.S. Yes, I pulled 10,000 out of the air, but if you look at what must
be done to instantiate a template, it's probably far too small <g>.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Walter Bright

unread,
Nov 3, 2007, 12:42:58 PM11/3/07
to
khall....@gmail.com wrote:
> The nature
> of C++ can make recompilation longer than it otherwise needs to be.
> <insert your explanation of changing private members here and its
> effect on compilation times>. Languages such as C, Ada, and D
> (Walter correct me if I'm wrong) suffer similar problems and share the
> same well-developed workaround: the "opaque pointer" (also commonly
> referred to as the RAII idiom). The downside of using opaque pointers
> is that is requires an extra level of abstraction and requires a bit
> more time designing. Languages that have helped solve these problems
> include: .... <list languages here> ...

You are correct in regards to D. On the other hand, D is designed to be
a fast compiling language, so the build times tend to not be a problem.
I've done a lot of profiling of DMC++, and the design of D has benefited
a lot from designing away issues that cause slow compile times.


> And here's something to consider: in my company (which uses primarily
> C, but also C++, Python, C#, VB.NET, and a variety of other lesser
> known langugages), I'm one of the few C++ advocates (though many
> project are developed using C++). People tell me C++ is a complicated
> language; the syntax is difficult; it's so prone to memory and
> resource leaks. However, they are amazed when they see my C++
> programs. They compliment me on how simple the program is (frequently
> 1/3 the size of a similar C program -- sometimes even much smaller;
> often smaller and more concise than .NET programs too), how well it
> runs, how bug free it is. I'm not trying to toot my own horn, rather
> I'm trying to point out that C++ has the tools to make understandable,
> robust programs. It's a challenging language to master, but when used
> properly is a beautiful tool!

You've certainly piqued my curiosity. Do you have an example?

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Yossi Kreinin

unread,
Nov 3, 2007, 8:58:14 PM11/3/07
to
On Nov 3, 12:52 am, khall.ggr...@gmail.com wrote:
>
> I think you'll do a poor job of convincing people.

When you try to convince professionals (that is, people who normally
have a solid opinion already) to switch tools having network effects,
you should lower your expectations. Nonetheless (or maybe
"therefore"...), the positive feedback I got exceeded my expectations.

However, the fact that I'm so far from convincing /you/ (whom I only
know as the author of other messages in this discussion, but they
stand out in the bulk of feedback I get) does bother me, because your
attitude is quite close to my own attitude several years ago. Which
means the FQA likely wouldn't convince me back then - which is a pity
to realize.

> The reason I
> belive this is because you very frequently use an inflamatory tone in
> your answers while not offering good alternatives.

The tone is primarily supposed to prevent people from falling asleep -
let's face it, the subject is not very entertaining:

http://yosefk.com/c++fqa/faq.html#faq-6

> And the implied
> alternatives frequently contradict one another.

You could get this impression because in some places, I seem to prefer
lower-level languages, and in some places higher-level ones. However,
it isn't a contradiction, just an assertion that C++ is suboptimal to
both kinds of work (yes, I've used it for both, a lot), and that I
haven't found the ultimate programming language suitable for all
domains yet, so I prefer to use several.

> You argue that C++
> exceptions are bad, but C++ exceptions are not really that different
> from other language's exceptions

Important differences are that with garbage collection, you have much
less practical problems with exception safety, and that with many
other languages having exceptions, there are portable ways to get the
context where the exception was raised (not to mention continuations
which most languages lack):

http://yosefk.com/c++fqa/defective.html#defect-10

These problems with exceptions interact badly with the lack of other
ways to handle errors in constructors and overloaded operators.

Of course in languages without these problems, some people would still
avoid exceptions in some projects, claiming that in practice
exceptions lead to less robust code since people simply stop thinking
about errors. I don't think this claim is universally right or wrong,
and never say I do in the FQA.

> (especially when compared against
> your criteria for exceptions being bad).

Which criteria? I never said "exceptions are bad".

> On the other hand, you argue
> that C++ being an unmanaged and non-garbage collected language is
> bad.

/No/, and I explain this very point at the first paragraph of
Defective C++:

http://yosefk.com/c++fqa/defective.html

I'm saying that /if/ a language "decides" not to have garbage
collection (which it has to decide on some level in order to be
applicable to some domains), then /other/ decisions must be compatible
with this. For example, exceptions become problematic because
exception safe code is much harder to write. Therefore, operator
overloading becomes problematic because operators have fixed
prototypes and thus have hard time to signal errors without
exceptions.

I'm not saying that there exists a language to which none of the items
in Defective C++ applies, or that it should exist. I'm saying that the
"defects" wouldn't be /defects/ at all /by themselves/; it's the
interaction that causes the problems.

> Well, most mangaged, garbage collected languages out there use
> exceptions.

In such languages, exceptions don't lead to many kinds of trouble -
see above.

>
> It would be better to have a more objective tone. For example in the
> "No compile time encapsulation" section, you could write: The nature
> of C++ can make recompilation longer than it otherwise needs to be.
> <insert your explanation of changing private members here and its
> effect on compilation times>. Languages such as C, Ada, and D
> (Walter correct me if I'm wrong) suffer similar problems and share the
> same well-developed workaround: the "opaque pointer" (also commonly
> referred to as the RAII idiom). The downside of using opaque pointers
> is that is requires an extra level of abstraction and requires a bit
> more time designing. Languages that have helped solve these problems
> include: .... <list languages here> ...

See the reply to one of your other messages regarding opaque pointers.

>
> If you answered each question in this manner, most C++ developers
> would probably wouldn't have much of a problem with it. Sure, there
> will always be die-hards, but you would not quite have the backlash
> you currently have.
>

I decided to discuss this on comp.lang.c++.moderated because it's
probably the best forum in terms of the level of competence of the
opponents, its widely acknowledged central place in the C++ community
(so you miss less relevant reviewers than elsewhere) and the high
signal/noise ratio (due to the .moderated part). However, the document
was not written solely for people in this NG; I could only write one
FQA, and IMO very few people can read a large technical document
containing calm and "objectively sounding" criticism.

And then, why does it matter how it sounds? After all, "objectively
sounding" documents aren't and can't be objective, either. Either you
agree with the information, or you don't. The style of the document is
a matter of rhetorics; you can see that I use very different style in
more personal communication. So why don't we concentrate on the actual
arguments and counter arguments?

> And here's something to consider: in my company (which uses primarily
> C, but also C++, Python, C#, VB.NET, and a variety of other lesser
> known langugages), I'm one of the few C++ advocates (though many
> project are developed using C++). People tell me C++ is a complicated
> language; the syntax is difficult; it's so prone to memory and
> resource leaks. However, they are amazed when they see my C++
> programs. They compliment me on how simple the program is (frequently
> 1/3 the size of a similar C program -- sometimes even much smaller;
> often smaller and more concise than .NET programs too), how well it
> runs, how bug free it is. I'm not trying to toot my own horn, rather
> I'm trying to point out that C++ has the tools to make understandable,
> robust programs. It's a challenging language to master, but when used
> properly is a beautiful tool!
>

I really sympathize, partly because of the resemblance of your
situation to my own, several years ago. I have lots of C++ experience
and I think I understand the beauty you talk about. IMO the greatest
power of C++ is its appeal to a large group of responsible programmers
striving to write well-behaved and elegant programs (whether it
ultimately works out and especially scales is another matter). This
isn't the forum to get into details, but your claims about managed
environments ("ill-formed programs are still ill-formed") are one
example of your words I'd definitely sign 3-4 years ago.

I can say two things in this context: (1) If anything I wrote sounded
personally offensive, I sincerely apologize and (2) The last thing I'd
want is for anything I wrote to become "ammunition" in the hands of
technically incompetent people advocating to prevent others from using
C++. I hope that the very style of the document you've criticized
makes it incompatible with the worst kinds of corporate culture where
this sort of thing happens.

I would be happy if you responded to my counter arguments regarding
your messages (although most likely we'll simply reach a point where
we disagree, but our arguments are clearly stated), and equally happy
to discuss anything off-topic for c.l.c++.m in private e-mail.

Jerry Coffin

unread,
Nov 3, 2007, 8:54:50 PM11/3/07
to
In article <MoWdnbGz8eme87ba...@comcast.com>,
wal...@digitalmars-nospamm.com says...

[ ... ]

> Although conceptgcc does exist and that is great, realistically, a
> complex feature like that needs to be in real use for years by a lot of
> people before it becomes clear whether or not it solves the problem. For
> example, namespaces were implemented and in wide use for years before
> people realized that they didn't really work.

You seem to be implying that conceptgcc (to continue the same example)
is brand new and available only to a select few people. Neither is true.

> See the infamous "so sue me":
> http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/
> thread/d9eb5db7019673e0/1aaae5a80f53bd4f?hl=en&lnk=st&q=namespaces+scott
> +meyers+%22so+sue+me%22#1aaae5a80f53bd4f

This is hardly proof that "namespaces...didn't really work" -- quite the
contrary, it's simply a statement Scott Meyers' disagreement with how
they do work. If this had been published in a (fictional) newspaper on
C++, it would have belonged on the op-ed page rather than as a report of
news.

I find namespaces sufficiently useful that I disagree with him, at least
in part. At the same time, I agree that namespaces are open to
considerable improvement. For one example, I think Daveed Vandevoorde's
"Modules in C++" proposal is quite nicely thought out. Anybody who cares
to can read it and judge for themselves though:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2316.pdf

--
Later,
Jerry.

The universe is a figment of its own imagination.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Yossi Kreinin

unread,
Nov 3, 2007, 9:00:15 PM11/3/07
to
On Nov 3, 4:23 am, David Abrahams <d...@boost-consulting.com> wrote:
>
> The problem that "making everything a template" addresses would not be
> solved by transitive const, as you seem to imply. Templatizing can be
> used to deal with the kind of code repetition you get in the const and
> non-const versions of a class' operator[], where you can't just use
> one non-templated declaration because you need to return either a
> constant or a mutable reference depending on the constness of *this.

Well, "making everything a template" tries to address several
problems, and we're talking about two different ones :)

I think that if const is transitive, you can automatically say that
any pointer returned by a method called for that object is const, too,
so it addresses the problem you mention. I don't know if that's what D
does and independently of that didn't analyze any other consequences
of this approach.

What I'm saying is (1) you mentioned a different problem - the one I
was talking about is described below and (2) both that problem and the
one you mention show that const may be hard to use (without
quantifying the cases when it helps and the ones where it gets in the
way, of course).

> Incidentally, I wish we
> had cv-qualification as a template parameter ;-)

I used to wish these kinds of things at the time I was writing many
templates, too, although I probably never got anywhere near the
complexity that you face ;-)

>
> As for the issue of casting pointers, the way const works in C++
> almost never forces you to cast; if you often find yourself casting
> away const in C++, you're almost certainly doing something very wrong.
> That sort of thing should only happen in the gnarliest low-level
> systems code and when interfacing with legacy systems that don't know
> about const.

I don't know if my gnarly systems code is "the gnarliest", but it's
not there where I met most cases when you'd have to cast away const :)
The problem I refer to is when you have levels of indirection, the
simple example being passing a vector<T*> (which means "go ahead and
change everything") to a function expecting a const vector<const T*>&
(thus saying "I'm not going to change anything").

"Intuitively" (when you consider what const "means" at the high
level), this should work. I know why it doesn't work, and I know why
the case where the function expects a non-const vector<const T*>& /
shouldn't/ work "at the high level" (the function could stuff pointers
to immutable objects into the vector to have them modified later by
the caller, for example).

What I don't understand is, why don't some other C++ programmers
consider this a problem, at some level? Yes, we all understand why it
doesn't work according to the language definition, but why isn't the
problem at all important? People in this NG said that they don't const-
qualify container element types. Well, this problem sure teaches you
to do so, but sometimes you /obtain/ the objects const-qualified, and
you have to cast const away at /that/ point. And I think that code in
a language without const (telling "you figure out what I change, maybe
using a naming convention") is at some level more clear than code
which appears to use const at the prototype (telling you to "relax")
and then casts it away in the definition.


--

Walter Bright

unread,
Nov 3, 2007, 9:01:27 PM11/3/07
to
Eugene Gershnik wrote:
> Walter Bright wrote:
>
>> The const X would have read only access to mutable Y. There is no way
>> to modify Y through const X. If you want to mutate Y through X, then
>> X must not be const.
>
> [...]
>
>> Essentially, D doesn't support the notion
>> of an object pretending to be const, only objects that actually are.
>> The idea is that if you see a function signature that says its taking
>> a const argument, it really truly cross-my-heart promises that it *will
>> not* be mutating it.
>
>
> I see. IOW in D const means physical rather than logical immutability
> and it is not orthogonal to object relationships.

That's right.

> Well these features are not something I am personally interested in since I
> don't see any value in either. Actually I see great reduction in value
> compared to C++ const.
> Obviously YMMV

D's const is definitely a different programming paradigm than C++'s
const, and can be initially off-putting to experienced C++ const users.
What we did was go back to basic principles and try to figure out just
what we wanted to accomplish with const. I think the result is a
fundamentally more useful paradigm, but only time will tell if that is
right or not.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

David Abrahams

unread,
Nov 4, 2007, 1:59:28 AM11/4/07
to

on Fri Nov 02 2007, Walter Bright <walter-AT-digitalmars-nospamm.com> wrote:

> James Hopkin wrote:
>> On Nov 2, 4:42 pm, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
>>> What about when you need a subset of objects kept in std::vector or
>>> std::list, and the owning collection is about to be deallocated,
>>> because you only need some of the objects, but not all of them
>>> anymore?
>>>
>>> http://yosefk.com/c++fqa/dtor.html#fqa-11.1
>>>
>>> Do you copy the objects, which has a performance penalty, or keep
>>> collections of pointers, so you have no RAII coming from the standard
>>> library to help you with exception safety anymore, or do you use smart
>>> pointer classes which do reference counting, which breaks when you
>>> have cyclic references?
>>>
>>
>> With std::list you can use splice. As for contiguous containers
>> (vector, deque) - that's the tradeoff you make for the advantages of
>> contiguous allocation.
>
> You can do slicing (taking a contiguous subset of an array of objects)
> with contiguous allocation if you have garbage collection.

Slice != Splice.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

David Abrahams

unread,
Nov 4, 2007, 1:59:56 AM11/4/07
to

on Fri Nov 02 2007, Walter Bright <walter-AT-digitalmars-nospamm.com> wrote:

> Hyman Rosen wrote:
>> Yossi Kreinin wrote:
>>> The problems with the build speeds of templates are inherent to the
>>> design of this language feature
>>
>> I work with a project that contains many hundreds of C++ files,
>> pretty much all of which use class and function templates from
>> the standard library and our own. I can do a clean recompile of
>> the whole system in ten or fifteen minutes or so on my Solaris
>> x86 machine using the Sun CC 11 compiler. For normal work, the
>> source compilation time is inconsequential - it's linking that
>> takes most of the time, and even that isn't too long.
>
> I've heard many reports of heavily templated C++ code having build times
> measured in hours - such as overnight builds. Even so, a build time of
> ten minutes is very consequential. Having a build time of a few seconds
> can completely transform how one develops code.
>
> I know very well how and why templates slow down compilation, and this
> is inherent to how C++ templates work. It is not fixable.

We did a fair amount of testing of template instantiation speed for
http://www.amazon.com/Template-Metaprogramming-Concepts-Techniques-Depth/dp/0321227255
and we found a huge variation between the slowest and fastest template
instantiators. So there's great room for improvement in at least some
compilers.

But even the fastest ones have performance problems because they do a
linear walk through a list of template specializations each time a
specialization is mentioned. An O(1) hash table lookup should make a
big difference. Why do you think it's not fixable?

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Hyman Rosen

unread,
Nov 4, 2007, 2:05:20 AM11/4/07
to
Walter Bright wrote:
> Even so, a build time of ten minutes is very consequential.
> Having a build time of a few seconds can completely transform
> how one develops code.

It takes far less most of the time, since I don't do complete
rebuilds very often. Most of the time, as I said, I'm bound by
the speed of the Solaris linker an dthe size of the executable.

> I know very well how and why templates slow down compilation,
> and this is inherent to how C++ templates work. It is not fixable.

The code that I work on drags in messaging libraries, database
libraries, GUI libraries, math libraries, and lord knows what
else. I don't believe that the templates are particularly a
bottleneck in my compiler.

LR

unread,
Nov 4, 2007, 2:02:03 AM11/4/07
to


If I understand the above, it seems you are specifically referring to
the problem of const when a container like std::vector has an element
data type that is a pointer.

I wonder how many people reading this ng use containers with element
data types that are pointers. Well, raw pointers.

Any comments on the pros and cons of doing that?

Or if you meant something more general, could you please be more explicit.

LR

Walter Bright

unread,
Nov 4, 2007, 2:01:48 AM11/4/07
to
Jerry Coffin wrote:
> In article <MoWdnbGz8eme87ba...@comcast.com>,
> wal...@digitalmars-nospamm.com says...
>> Although conceptgcc does exist and that is great, realistically, a
>> complex feature like that needs to be in real use for years by a lot of
>> people before it becomes clear whether or not it solves the problem. For
>> example, namespaces were implemented and in wide use for years before
>> people realized that they didn't really work.
>
> You seem to be implying that conceptgcc (to continue the same example)
> is brand new and available only to a select few people. Neither is true.

Not at all. I'm saying it does not have a track record of wide use and a
history of successful application development demonstrating the
soundness of concepts. On the contrary, conceptgcc is undergoing
constant revision in how concepts work, and that's a good thing at this
stage.


>> See the infamous "so sue me":
>> http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/
>> thread/d9eb5db7019673e0/1aaae5a80f53bd4f?hl=en&lnk=st&q=namespaces+scott
>> +meyers+%22so+sue+me%22#1aaae5a80f53bd4f
>
> This is hardly proof that "namespaces...didn't really work" -- quite the
> contrary, it's simply a statement Scott Meyers' disagreement with how
> they do work. If this had been published in a (fictional) newspaper on
> C++, it would have belonged on the op-ed page rather than as a report of
> news.

I agree it doesn't rise to the level of "proof", but then again, it is a
reasonable argument that there are serious shortcomings in the design of
namespaces. But if you don't agree, that's ok, I'll just present another
one: exception specifications (other than nothrow). As proof (!), I've
never noticed e.s. (other than nothrow) used anywhere outside of a test
suite.


> I find namespaces sufficiently useful that I disagree with him, at least
> in part. At the same time, I agree that namespaces are open to
> considerable improvement. For one example, I think Daveed Vandevoorde's
> "Modules in C++" proposal is quite nicely thought out. Anybody who cares
> to can read it and judge for themselves though:
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2316.pdf

I tend to view namespaces as a failed attempt at modules. It's good that
Daveed is trying to fix it. But I sure hope that Daveed's proposal gets
some wide use to prove its efficacy before freezing it into the
standard. It's too hard to tell just by looking at a spec.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com


--

David Abrahams

unread,
Nov 4, 2007, 2:13:04 AM11/4/07
to
on Fri Nov 02 2007, Walter Bright <walter-AT-digitalmars-nospamm.com> wrote:

> Although conceptgcc does exist and that is great, realistically, a
> complex feature like that needs to be in real use for years by a lot of
> people before it becomes clear whether or not it solves the
> problem.

There is no one "the problem" being solved by the new concept support.
It certainly does solve many real problems, and I disagree that we
need years of real-world use to know that. Will it be the optimal
solution? Probably not; it's very likely we'll learn that we could've
done some things better after several years in the field.

> For example, namespaces were implemented and in wide use for years
> before people realized that they didn't really work.

That's sort of a meaningless statement to make without defining
"work." What problem do you think they're trying to solve that they
don't solve?

With all due respect to Scott, I don't think his experience with
namespaces as of six years ago can be used to make a definitive
blanket judgement about whether namespaces "work."

The only "problem with namespaces" that I've ever seen identified is
really a problem with ADL, but that's a different topic.

Field experience before standardization is good; more field experience
is better. Unfortunately, once you standardize the language you can't
really get deep, broad field experience with new core language
features until you standardize them. Few vendors, and thus few users,
will bite, no matter how good the features are.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Yossi Kreinin

unread,
Nov 4, 2007, 5:43:52 AM11/4/07
to
On Nov 4, 9:02 am, LR <lr...@superlink.net> wrote:
>
> If I understand the above, it seems you are specifically referring to
> the problem of const when a container like std::vector has an element
> data type that is a pointer.
>

The example of std::vector<std::list<T>::const_iterator> and
std::vector<std::list<T>::iterator> isn't very different from the
example with raw pointers, except casting your way out of the problem
becomes a bit trickier (casting const_iterator to iterator is not
supposed to work and shouldn't be done, so you'd have to cast the
reference returned by *vec[i]). Well, that and having much longer type
names.

Hyman Rosen

unread,
Nov 4, 2007, 5:43:33 AM11/4/07
to
Yossi Kreinin wrote:
> I'm not questioning the reasons making these pairs of types
> "unrelated"/"incompatible", just claim that it makes const unhelpful
> when you have levels of indirection.

I guess you just have more arrays of pointers than I do.
Thinking about my own code, I believe that when we do keep
pointers in containers, they're usually smart pointers to
non-const objects.

Some people here should be telling you that you shouldn't be
passing vectors around anyway. The proper idiom is to write
function templates that take pairs of iterators instead. But
I won't be one of those people :-)

Walter Bright

unread,
Nov 4, 2007, 5:48:29 AM11/4/07
to
David Abrahams wrote:
> on Fri Nov 02 2007, Walter Bright <walter-AT-digitalmars-nospamm.com>
wrote:
>
>> Although conceptgcc does exist and that is great, realistically, a
>> complex feature like that needs to be in real use for years by a lot of
>> people before it becomes clear whether or not it solves the
>> problem.
>
> There is no one "the problem" being solved by the new concept support.
> It certainly does solve many real problems, and I disagree that we
> need years of real-world use to know that.

I think we do, but I know that won't happen before it is standardized.
Concepts are a complex feature with at complex implementation. I'm not
at all convinced it is the right solution; only time will tell.


> Will it be the optimal
> solution? Probably not; it's very likely we'll learn that we could've
> done some things better after several years in the field.

Exactly <g>.


>> For example, namespaces were implemented and in wide use for years
>> before people realized that they didn't really work.
>
> That's sort of a meaningless statement to make without defining
> "work." What problem do you think they're trying to solve that they
> don't solve?
>
>> See the infamous "so sue me":
>>
http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/d9eb5db7019673e0/1aaae5a80f53bd4f?hl=en&lnk=st&q=namespaces+scott+meyers+%22so+sue+me%22#1aaae5a80f53bd4f
>
> With all due respect to Scott, I don't think his experience with
> namespaces as of six years ago can be used to make a definitive
> blanket judgement about whether namespaces "work."

> The only "problem with namespaces" that I've ever seen identified is
> really a problem with ADL, but that's a different topic.

To me, the problem with namespaces is they are modules lite, and failed
badly enough at that that exported templates were tried, and that failed
too, so now Daveed's module proposal is the third try. This is a bit
harsh, but my basis for saying this is that D has true modules, and the
need for namespaces and exported templates has, as a consequence, just
evaporated.

I understand why ADL exists, but never liked it, and was always looking
for a better answer. We came up with 'overload sets', which I hope solve
the problem (but only time will tell, of course).


> Field experience before standardization is good; more field experience
> is better. Unfortunately, once you standardize the language you can't
> really get deep, broad field experience with new core language
> features until you standardize them. Few vendors, and thus few users,
> will bite, no matter how good the features are.

I agree completely. I added several extensions to DMC++, but nobody used
them because they were not in the standard. And I understood the reasons
why - everybody wants to have their source code portable, and C++
programmers are by and large a conservative group of people. I've had
people tell me "you know, it would be really nice if you added Feature X
to your C++ compiler", only to have me tell them "yes, it would be, and
in fact X has been in it for the last 10 years!" But nobody notices it,
and even the people who ask for X *still* won't use X.

One of the nice things about D is that there's a whole different
attitude by the users of it - they're way ahead of me in eagerness to
try out new things. Some of the ideas in hindsight turn out to be stupid
(like the bit data type, an embarrassing failure), but at least a lot of
people give them a thrashing to see if they work or not. They're
merciless if it doesn't work, and adopt it if it does. This makes it a
whole lot of fun for me.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Walter Bright

unread,
Nov 4, 2007, 5:49:56 AM11/4/07
to
David Abrahams wrote:
> on Fri Nov 02 2007, Walter Bright <walter-AT-digitalmars-nospamm.com>
wrote:
>> I know very well how and why templates slow down compilation, and this
>> is inherent to how C++ templates work. It is not fixable.
>
> We did a fair amount of testing of template instantiation speed for
>
http://www.amazon.com/Template-Metaprogramming-Concepts-Techniques-Depth/dp/0321227255
> and we found a huge variation between the slowest and fastest template
> instantiators. So there's great room for improvement in at least some
> compilers.

Yes.

> But even the fastest ones have performance problems because they do a
> linear walk through a list of template specializations each time a
> specialization is mentioned. An O(1) hash table lookup should make a
> big difference. Why do you think it's not fixable?

Because a template must be generated for every state. So, given
something like a factorial template:

template<int n> class factorial
{
public:
enum
{
result = n * factorial<n - 1>::result
};
};

template<> class factorial<1>
{
public:
enum { result = 1 };
};

void test()
{
// prints 24
printf("%d\n", factorial<4>::result);
}

If we try to do a factorial<10>, then 10 templates get instantiated. If
you're using templates to do metaprogramming, this can consume a serious
amount of memory for even fairly simple loops (and a lot of computation
just to generate all those templates). Essentially, you're reduced to
doing only simple things with TMP because of the problem of an explosion
in the number of template instantiations.

I don't see how this is fixable.

P.S. You're right that a hash lookup will deal with one aspect of the
template instantiation explosion. But the compiler still must generate
some data structure for that template just to look it up, and it's got
to do it for *every* state in the template metaprogram. Such effort
makes even the worst interpreter run like a tachyon in comparison <g>.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Alex Shulgin

unread,
Nov 4, 2007, 5:45:03 AM11/4/07
to
On Nov 2, 6:42 pm, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
> On Oct 28, 8:01 pm, Alex Shulgin <alex.shul...@gmail.com> wrote:
>
> > "No compile time encapsulation"
>
> > This item implies that you have to ship the implementation class
> > declaration to clients, which is not necessarily true. Have you ever
> > heard about abstract classes ("interfaces")?
>
> I know the ways to work around the problems with private:
>
> http://yosefk.com/c++fqa/inheritance-abstract.html#fqa-22.1 (abstract
> classes)

I think you have severely misunderstood the "Defining interfaces is
hard, throwing together an implementation is easy." part of the
original FAQ item. From my experience, it is really harder to define
interfaces than providing implementation, since making changes to an
interface have much bigger costs than making changes to the
implementation. Designing the interfaces the way you never have the
need to change them, or at least minimizing the changes in future is
what exactly makes it hard (up to the point being harder than
implementing them).

This, of course, applies not only to C++ but to any interface/
implementation problem in general. ;-)

> http://yosefk.com/c++fqa/heap.html#fqa-16.21 (pimpl)

Have never used pimpls, so no comments this time. :-)

> http://yosefk.com/c++fqa/class.html#fqa-7.5 (incomplete types)

This is interesting, and I agree that the original FAQ item is
(possibly intentionally) wrong. However, could you please elaborate
on the last sentences:

"C++ is extremely unsafe because every pointer can be used to modify
every piece of memory from any point in code."

An example (i.e. some real code) would be very helpful.

> However, workarounds aside, C++ does have private, and there's a
> separate question of whether it's useful or not. I claim that the
> advantages (getting a compile time error when someone accesses a
> member you didn't want them to) are dwarfed by the drawbacks. Most C++
> classes are neither pimpls nor implementations of base classes (that
> takes manually written wrapper/bridge code) and of course not C-like
> incomplete types (naturally, people most often use C++ classes when
> they program in C++). Changes done to the private members of those
> classes are responsible for a significant amount of time spent for
> rebuilding C++ code (of course I don't have numeric data proving this,
> and you don't have numeric data refuting this; all we can do is to
> point out the things that happen, not to measure their importance).

Agreed, except for the "most" part... How do you know they really
are? ;-)

> > "No run time encapsulation"
>
> I'm not claiming that every language should have run time
> encapsulation. Frequently, language features can not be judged by
> themselves, but only if you consider how they interact.
>
> The problem with having no run time encapsulation /in C++/ is that the
> language has no reflection and several different and non-trivial
> commercially significant ABIs. This means, for example, that when a
> program crashes, figuring out what's going on in the core dump is hard
> compared to, say, a program compiled from C source (C doesn't have
> reflection, either, but maps more straight-forwardly to executable
> code):
>
> http://yosefk.com/c++fqa/defective.html#defect-4

Personally, I don't having recall that sort of problems... But may be
you can clarify on what exactly makes it harder as compared to C
dumps? I can think of inlining and templates... Anyway that's quite
natural and expectable, since the language itself is way more complex
than C.

> > "No binary implementation rules"
>
> > Frankly, I don't see your point here. You have tried to link g++-
> > compiled libraries with msvc-compiled code, or what exactly?
>
> I didn't, because I know it doesn't work. I wish it did though. Are
> you saying that this is a non-problem, and if so, why?

Well, it was never a problem for me, and I wish I'd never have this
one. Is sticking to a single compiler a problem to anyone?

> > Anyway, I don't recall myself applying cv-qualifiers to the type of
> > contained elements in a sensible and useful way. Anyone?
>
> What about when you fill your vector with objects obtained by calling
> a method returning a const reference in a loop?

As others have noted it would be extremely nice to have an example for
this one. Let's get to some real code already. :-)

> > > ... Exception safe C++ code is almost infeasible to achieve in a
non-trivial program.
>
> > Maybe, if you prefer to write everything from scratch instead of
> > trying to stick to standard library.


>
> What about when you need a subset of objects kept in std::vector or
> std::list, and the owning collection is about to be deallocated,
> because you only need some of the objects, but not all of them
> anymore?
>
> http://yosefk.com/c++fqa/dtor.html#fqa-11.1

Another example please? From what I can see it's all about storing
raw pointers in the containers... very suspicious.


--
Kind regards,
Alex

Walter Bright

unread,
Nov 4, 2007, 5:49:10 AM11/4/07
to
Hyman Rosen wrote:
> Walter Bright wrote:
>> Even so, a build time of ten minutes is very consequential.
>> Having a build time of a few seconds can completely transform
>> how one develops code.
>
> It takes far less most of the time, since I don't do complete
> rebuilds very often. Most of the time, as I said, I'm bound by
> the speed of the Solaris linker an dthe size of the executable.
>
>> I know very well how and why templates slow down compilation,
>> and this is inherent to how C++ templates work. It is not fixable.
>
> The code that I work on drags in messaging libraries, database
> libraries, GUI libraries, math libraries, and lord knows what
> else. I don't believe that the templates are particularly a
> bottleneck in my compiler.

Linkers often have a lot to do to factor out all the redundant template
instantiations. These instantiations bloat up the object files, then
have to be removed by the linker. Templates are an indirect source of
slowdown in the linker.

If you're using the gnu linker, ld, it's a painfully slow linker anyway
<g>, but it doesn't have to be that way.

P.S. It's a fool's game to guess what the particular bottlenecks in a
compiler are without access to the source and a profiler. But I know
what must happen when templates are instantiated, and that cannot be
waved away. See my reply to David.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

James Dennett

unread,
Nov 4, 2007, 5:51:24 AM11/4/07
to
Yossi Kreinin wrote:
> On Nov 3, 12:52 am, khall.ggr...@gmail.com wrote:
>> I think you'll do a poor job of convincing people.
>
> When you try to convince professionals (that is, people who normally
> have a solid opinion already) to switch tools having network effects,
> you should lower your expectations. Nonetheless (or maybe
> "therefore"...), the positive feedback I got exceeded my expectations.
>
> However, the fact that I'm so far from convincing /you/ (whom I only
> know as the author of other messages in this discussion, but they
> stand out in the bulk of feedback I get) does bother me, because your
> attitude is quite close to my own attitude several years ago. Which
> means the FQA likely wouldn't convince me back then - which is a pity
> to realize.

I'd be surprised if it convinced any well-informed person.

>> The reason I
>> belive this is because you very frequently use an inflamatory tone in
>> your answers while not offering good alternatives.
>
> The tone is primarily supposed to prevent people from falling asleep -
> let's face it, the subject is not very entertaining:

It just annoyed me; it comes across as yet another emotional
rant against C++ rather than a considered discussion.

[snip]

>> You argue that C++
>> exceptions are bad, but C++ exceptions are not really that different
>> from other language's exceptions
>
> Important differences are that with garbage collection, you have much
> less practical problems with exception safety

Ironically, that tends to be untrue in practice. GC'd
languages have non-uniform treatment of resources, which
makes exception-safe code unusual. By having a uniform
mechanism (RAII), C++ makes it easier to write code that
is exception-safe.

[snip]

>> On the other hand, you argue
>> that C++ being an unmanaged and non-garbage collected language is
>> bad.
>
> /No/, and I explain this very point at the first paragraph of
> Defective C++:
>
> http://yosefk.com/c++fqa/defective.html
>
> I'm saying that /if/ a language "decides" not to have garbage
> collection (which it has to decide on some level in order to be
> applicable to some domains), then /other/ decisions must be compatible
> with this. For example, exceptions become problematic because
> exception safe code is much harder to write.

The opposite is true.

[snip]

>> Well, most mangaged, garbage collected languages out there use
>> exceptions.
>
> In such languages, exceptions don't lead to many kinds of trouble -
> see above.

But they do. It's just that the C++ community has studied itself
hard enough, and set its standards high enough, to come to understand
what's required to write exception-safe code (which is actually not
much more than writing robust code in the presence of *any* mechanism
for handling failures), and C++ provides better facilities to implement
it than most GC-requiring languages.

[snip]

>> If you answered each question in this manner, most C++ developers
>> would probably wouldn't have much of a problem with it. Sure, there
>> will always be die-hards, but you would not quite have the backlash
>> you currently have.
>>
>
> I decided to discuss this on comp.lang.c++.moderated because it's
> probably the best forum in terms of the level of competence of the
> opponents, its widely acknowledged central place in the C++ community
> (so you miss less relevant reviewers than elsewhere) and the high
> signal/noise ratio (due to the .moderated part). However, the document
> was not written solely for people in this NG; I could only write one
> FQA, and IMO very few people can read a large technical document
> containing calm and "objectively sounding" criticism.

Your target audience, then, isn't the people who would be
persuaded by calm and objective reasoning?

> And then, why does it matter how it sounds? After all, "objectively
> sounding" documents aren't and can't be objective, either. Either you
> agree with the information, or you don't. The style of the document is
> a matter of rhetorics; you can see that I use very different style in
> more personal communication. So why don't we concentrate on the actual
> arguments and counter arguments?

Few were made in your FQA; it focused on subjective claims,
it seems to me.

[snip]

> I can say two things in this context: (1) If anything I wrote sounded
> personally offensive, I sincerely apologize and (2) The last thing I'd
> want is for anything I wrote to become "ammunition" in the hands of
> technically incompetent people advocating to prevent others from using
> C++. I hope that the very style of the document you've criticized
> makes it incompatible with the worst kinds of corporate culture where
> this sort of thing happens.

I'd honestly have guessed, from the FQA alone, that it's goal
was exactly to become such ammunition. I'll accept your word
that it's not the case, but that's how it appeared to me.

-- James

Yossi Kreinin

unread,
Nov 4, 2007, 6:26:39 PM11/4/07
to
On Nov 4, 12:45 pm, Alex Shulgin <alex.shul...@gmail.com> wrote:
> >http://yosefk.com/c++fqa/inheritance-abstract.html#fqa-22.1(abstract

> > classes)
>
> I think you have severely misunderstood the "Defining interfaces is
> hard, throwing together an implementation is easy." part of the
> original FAQ item. From my experience, it is really harder to define
> interfaces than providing implementation, since making changes to an
> interface have much bigger costs than making changes to the
> implementation. Designing the interfaces the way you never have the
> need to change them, or at least minimizing the changes in future is
> what exactly makes it hard (up to the point being harder than
> implementing them).
>

I mentioned this item to show that the FQA refers to abstract classes
and acknowledges their benefits related to separating interface from
implementation with respect to compilation dependencies (again, if
that's all the decoupling you need, incomplete types are a cheaper way
to get it since there's no wrapper code).

As to the "interface is easy / implementation is hard" issue - the FQA
gives examples where the interface is easy and the implementation is
hard. A related example: a compiler. Here, the interface /visible at
the level of class definitions/ is easy
("compile(source_stream,asm_stream)"); the hard part - the grammar and
meaning of the source language and the assemblers of the different
targets - is not visible at the level of classes. There's lots of
structure in what looks like unstructured data to a compiler - images,
text, etc.

If you want each and every interface /inside/ the compiler to be
stable, since you want to never modify them, then you may have a hard
problem. However, the stability of the interfaces inside the compiler
is only important for some of them (for example, the way intermediate
code is represented and the way a "single pass" is represented), other
parts can be easily changed in terms of the extent to which they
affect the rest of the system. On the other hand, the algorithms of
each pass may be pretty complicated.

>
> >http://yosefk.com/c++fqa/heap.html#fqa-16.21(pimpl)


>
> Have never used pimpls, so no comments this time. :-)
>

> >http://yosefk.com/c++fqa/class.html#fqa-7.5(incomplete types)


>
> This is interesting, and I agree that the original FAQ item is
> (possibly intentionally) wrong.

Hope this helps in some practical way :)

>
> "C++ is extremely unsafe because every pointer can be used to modify
> every piece of memory from any point in code."
>
> An example (i.e. some real code) would be very helpful.
>

Consider this snippet (I use iterators to skip the part where we talk
about the problems of C arrays, etc.):

template<class Iter> void inc_range(Iter b, Iter e) {
for(; b!=e; ++b) {
*b = *b + 1;
}
}

Now, b & e could have been obtained with vec.begin() and vec.end(),
and vec could have been resized or cleared since then. inc_range()
will modify objects which happen to be allocated where the vector
storage used to be (or it may crash the program, but the first
possibility is the harder one to debug).

In other languages, this either can't happen or (more frequently)
doesn't happen unless you "work hard" to get the possibility for this
effect to occur (by explicitly "going unchecked"). That is, "every
pointer can /not/ be used to modify every piece of memory from any
point in code" - a piece of code knows that it's supposed to modify an
array and so it checks its length, or that it's supposed to modify a
member aaa of an object of some class CCC, and will refuse to modify
the member bbb of a DDD object (this can happen in C++ with the
example in the next link; this is one case where I'd easily agree that
"seasoned" C++ programmers won't write this code - it's more of a
beginner's pitfall):

http://yosefk.com/c++fqa/web-vs-c++.html#misfeature-1

Now, I realize that run time checks are not always acceptable; I work
on real time apps, among other things. What I claim, based on lots of
experience with debugging "unmanaged" applications, is that people
underestimate the problems with having no checks, and overestimate the
cost of these checks.

In theory, a program that does things like out-of-bounds access is
malformed, so we don't care about it; in practice, /all/ software
shipped to customers or otherwise released to the world is malformed
(think about browsers and operating systems; everything I saw in this
department crashed and had security holes). And you have to debug the
problems, which is much, much easier when a program halts upon the
first violation of language rules compared to the case when it keeps
running and reading/modifying the wrong data, covers its traces by
deleting the objects involved in the error and possibly never crashes.

>
> Agreed, except for the "most" part... How do you know they really
> are? ;-)
>

You mean "most" in "most classes are 'straight' C++ classes without
pimpls/ABCs/incomplete types involved"? Well, if they aren't, this
sort of proves my point about the problems with "straight" C++
classes... Of course I don't have numeric data here, either :) Do you
really think it's wrong though?

>
> >http://yosefk.com/c++fqa/defective.html#defect-4
>
> Personally, I don't having recall that sort of problems... But may be
> you can clarify on what exactly makes it harder as compared to C
> dumps? I can think of inlining and templates...

Inlining - yes, for example, inlining of implicitly-generated
operator= of large classes (I mentioned it in Defective C++ using the
example of a gargantuan class; with reasonably large classes, figuring
out the offset, then the name of the offending member from the huge
disassembly listing all attributed to the single source line where
assignment was done is no picnic, either).

Templates - especially in the context of inlining (people tend to
write all that code inline because it's in the header file anyway - a
"social" problem if you like; I try not to discuss such things too
much in the FQA). Then, debuggers are not very good with things like
placing breakpoints in templates, parsing type names involving
templates in object view windows, etc.

But I meant another thing in that item - you don't know how objects
will look like, from the layout of classes with virtual functions to
the memory layout of std::map. Some debuggers will know how to display
those - unless too much memory is corrupted, in which point you have
to kick in. In C, a (custom) hashtable would look practically the same
in the memory of all targets; the standard C++ types look different
everywhere.

> Anyway that's quite
> natural and expectable, since the language itself is way more complex
> than C.
>

Ah, but that's my point. If the language is "managed" or mostly safe,
then I don't care about the complexity of internal representations
that much, since I don't get to shovel through them. If it's unsafe
though, I'd rather have it simple enough for me to understand what the
pieces mean - I mean the little pieces to which programs will
invariably break into from time to time :)

>
> Well, it was never a problem for me, and I wish I'd never have this
> one. Is sticking to a single compiler a problem to anyone?
>

Of course it is - sometimes you have compiled third-party libraries,
and sometimes you ship your own libraries to someone using a compiler
which won't compile your C++ code at the front-end level (for example,
I saw pretty simple code using templates, 3 levels below the
complexity of boost, crashing VC++ 2005; gcc 3 and 4 happened to work
with it - that's one reason they bothered to implement mingw; of
course gcc will crash elsewhere, and I saw that, too). Sometimes a
compiler supports things that another one doesn't (platform-specific
intrinsics, special tricks for ISA subsets like floating point, high
quality debug info) but you want to link to libraries compiled with
something else.

Sometimes this is "not a problem", except you have to deliver your own
libraries compiled and tested with 3 compilers, each with its own
front-end (grammar) and back-end (codegen) bugs.

> > What about when you fill your vector with objects obtained by calling
> > a method returning a const reference in a loop?
>
> As others have noted it would be extremely nice to have an example for
> this one. Let's get to some real code already. :-)

Here's real code from a social network back-end :)

class Lamer { public: const LameComments& getLameComments(); };

void getTheMostLameComments(
vector<const LameComments*>& comments, //which type would you use?
const vector<Lamer>& lamers
)
{
//or we could use iterators...
for(int i=0; i<(int)lamers.size(); ++i)
{
const LameComments& lc = lamers[i].getLameComments();
if(lc.areTotallyPointless()) {
//lamers write lots of comments, better copy
//by reference... Has to be const - can't modify
//the lamer's precious comments, and we can't
//have vectors of references, so it's either a dumb
//const pointer or some non-standard smart pointer
comments.push_back(&lc);
}
}
}

I think this is a pretty common pattern with code massaging data
structures with even minor levels of nesting/indirection.

> > What about when you need a subset of objects kept in std::vector or
> > std::list, and the owning collection is about to be deallocated,
> > because you only need some of the objects, but not all of them
> > anymore?
>
> >http://yosefk.com/c++fqa/dtor.html#fqa-11.1
>
> Another example please? From what I can see it's all about storing
> raw pointers in the containers... very suspicious.
>

Do you know how much time the previous real example took me to invent?
What do you want me to do - grab a complete snapshot of an app I
worked on with memory management bugs in it and post it to Usenet? :)

What's wrong with the "English" example? You iterate over the nodes of
a 3D model, wishing to carve out the interesting ones and dispose of
the model. With garbage collection, the unused parts of the model
would become garbage; with RAII, you'd have to ask the model to
"forget" that it owns the objects. Some "owners" (like std::list) can
do it, some can't. Or you could use reference counting; simple B-rep
3D models are not unlikely to have cyclic references in them.

I've written a (lame) PE executable parser in D a couple of days ago,
where you carve out sections of the byte array; in D, you do it with
slicing, and don't think about the life cycles. With std::vector, you
have to explicitly keep the full vector around so that the references
to sub-chunks won't become dangling references, and you can't use
std::vector to represent the sub-chunks - you need a different,
custom, non-owning container; or you have to copy from the large
vector into smaller ones. It's a really tame example in terms of data
structure complexity, the only good thing about it is that it is real
and I might publish it someday soon :)

Cheers,
Yossi

Jerry Coffin

unread,
Nov 4, 2007, 6:25:04 PM11/4/07
to
In article <qOydncgL97IVsLDa...@comcast.com>,
wal...@digitalmars-nospamm.com says...

[ ... ]

> > You seem to be implying that conceptgcc (to continue the same example)
> > is brand new and available only to a select few people. Neither is true.
>
> Not at all. I'm saying it does not have a track record of wide use and a
> history of successful application development demonstrating the
> soundness of concepts. On the contrary, conceptgcc is undergoing
> constant revision in how concepts work, and that's a good thing at this
> stage.

Well, it's certainly true that more use would be a good thing. Given
that short of a drastic change of direction at the last minute, concepts
(in some form) are going to be in the next standard, I think it behooves
C++ developers to try them out, put them to the test, and see if they're
really useful.

[ ... ]



> I agree it doesn't rise to the level of "proof", but then again, it is a
> reasonable argument that there are serious shortcomings in the design of
> namespaces. But if you don't agree, that's ok, I'll just present another
> one: exception specifications (other than nothrow). As proof (!), I've
> never noticed e.s. (other than nothrow) used anywhere outside of a test
> suite.

I'm not sure what you're really trying to show here -- I thought we were
discussing namespaces, but it sounds like you're primarily intent on
showing that some of the "features" in the original C++ standard weren't
well thought out and haven't worked well in practice.

I tend to agree with that overall: exception specifications and export
(to name a couple of obvious ones) haven't shown any real utility, at
least IMO. I even use Comeau C++, one of the only compilers to implement
export, and I still don't use it in any real code.

Exception specifications are, IMO, even worse: I'm not sure export buys
a lot, but I think exception specifications are actively harmful to your
code as a rule.

[ ... ]

> I tend to view namespaces as a failed attempt at modules. It's good that
> Daveed is trying to fix it. But I sure hope that Daveed's proposal gets
> some wide use to prove its efficacy before freezing it into the
> standard. It's too hard to tell just by looking at a spec.

I guess that's where we differ -- I don't see namespaces as an attempt
at modules, failed or otherwise. In fact, I have a hard time seeing
enough similarity to figure out how they could be viewed as an attempt
at modules, period. A namespace defines a scope for names -- nothing
more and nothing less.

Modules, at least as usually defined (and Daveed's proposal is in accord
on this point) have much broader implications for things like order of
initialization and overall program organization. The fact that they
happen to also define a scope is important, but hardly their sole, or
even primary, reason for existence.

--
Later,
Jerry.

The universe is a figment of its own imagination.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Todd Gardner

unread,
Nov 4, 2007, 6:22:22 PM11/4/07
to
Yossi Kreinin wrote:
> On Nov 3, 4:23 am, David Abrahams <d...@boost-consulting.com> wrote:
[...]

>> As for the issue of casting pointers, the way const works in C++
>> almost never forces you to cast; if you often find yourself casting
>> away const in C++, you're almost certainly doing something very wrong.
>> That sort of thing should only happen in the gnarliest low-level
>> systems code and when interfacing with legacy systems that don't know
>> about const.
>
> I don't know if my gnarly systems code is "the gnarliest", but it's
> not there where I met most cases when you'd have to cast away const :)
> The problem I refer to is when you have levels of indirection, the
> simple example being passing a vector<T*> (which means "go ahead and
> change everything") to a function expecting a const vector<const T*>&
> (thus saying "I'm not going to change anything").
>
>

What do you see as the additional guarantee offered by const
vector<const T*>& that isn't offered by const vector<T*>&?

It seems like having a vector<const T*> doesn't really offer any
worthwhile invariants. It says, don't change anything about the
contained elements, but feel free to change the vector arbitrarily. So
once the function returns, you can't be sure that the element at
position, say, 5 is the same as it was when the vector was passed to the
function, because the vector might have been reordered, so the const is
worthless.

Todd Gardner

Jerry Coffin

unread,
Nov 4, 2007, 6:26:34 PM11/4/07
to
In article <yvednftUI9Hh5bDa...@comcast.com>,
wal...@digitalmars-nospamm.com says...

[ ... ]

> To me, the problem with namespaces is they are modules lite, and failed
> badly enough at that that exported templates were tried, and that failed
> too, so now Daveed's module proposal is the third try. This is a bit
> harsh, but my basis for saying this is that D has true modules, and the
> need for namespaces and exported templates has, as a consequence, just
> evaporated.

If (for example) there had been two previous versions of the standard,
and the first included only namespaces, then the second added export,
your argument might make sense. As it stands, I don't see a lot of sense
to it at all. It's true that namespaces were added before export, but
the two deal with entirely separate problems: namespaces deal purely
with name visibility, while export deals with separate compilation. The
two just aren't very closely related at all. While it's true that
modules tend to deal with both concepts together (and it may well be
better to do so) the fact remains that these two features dealt with
entirely separate and (at least as defined) unrelated issues.

--
Later,
Jerry.

The universe is a figment of its own imagination.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Jerry Coffin

unread,
Nov 4, 2007, 6:26:37 PM11/4/07
to
In article <kcCdnVUXx_8j7bDa...@comcast.com>,
wal...@digitalmars-nospamm.com says...

[ ... ]

> Because a template must be generated for every state. So, given
> something like a factorial template:

[ ... ]

> If we try to do a factorial<10>, then 10 templates get instantiated. If
> you're using templates to do metaprogramming, this can consume a serious
> amount of memory for even fairly simple loops (and a lot of computation
> just to generate all those templates). Essentially, you're reduced to
> doing only simple things with TMP because of the problem of an explosion
> in the number of template instantiations.

It seems to me that you and David are talking almost at cross-purposes
here. While it's certainly true that factorial<10> instantiates 10
templates, it's open to question whether that needs to be particularly
slow. Most current compilers take quite a long time to instantiate each
template. I'm not _sure_ exactly how much better that can be made, but
doing some poking around in the gcc source code makes it look (at least
to me) like there's room for considerable improvement.

I'd also note that while factorial<10> involves 10 instantiations, the
differences between the instantiations are quite minor. Again, I'm not
entirely certain, but it looks to me like quite a bit of factoring could
be done for cases like this, especially when you're dealing with non-
type template parameters. For different types, there's little
opportunity for factoring, but for tail-recursion on a non-type
parameter it looks to me like you could factor it in a typical case to a
single instantiation with a list of parameters (with a truly separate
instantiation for the end-case).

The one thing is that I'm not sure how much that really buys -- while
things like factorials and Fibonacci numbers make simple demonstrations
of TMP, I can hardly recall when I've used something like this in real
code.



> I don't see how this is fixable.

Notwithstanding the above, the number of instantiations is typically
nearly fixed -- but the time for a single instantiation is NOT...

--
Later,
Jerry.

The universe is a figment of its own imagination.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Yossi Kreinin

unread,
Nov 4, 2007, 6:24:54 PM11/4/07
to
On Nov 4, 8:59 am, David Abrahams <d...@boost-consulting.com> wrote:
>
> But even the fastest ones have performance problems because they do a
> linear walk through a list of template specializations each time a
> specialization is mentioned. An O(1) hash table lookup should make a
> big difference. Why do you think it's not fixable?
>

I see at least two problems having nothing to do with the compiler
implementation:

1. IMO the hash table look-up you mention can only be "O(1)" if the /
key size/ is O(1). However, apparently the keys in this case are
types, which can be generated by nesting template instantiations
(T<T<T<1> >, X<T<T<1> > > >). When a compiler "realizes" that it
should look up the definition of something like this, how can it do
the look-up in O(1) - isn't /hashing the key/ O(key_size), and is
there any limitation on the key size here?

2. The /compiler/ is not the only one potentially using slow
algorithms here - the user code is a player, too. Consider type lists
(which, BTW, are one "practical" example of types generated by nesting
templates). They are not /type hashes/, are they? If you have a
"compile time algorithm" checking whether a type is included in a type
list, you essentially do it with a linear scan, and if you merge two
type lists, AFAIK it's O(N^2). Now, I think that implementing type
lists to be "compile time type hashes or sets" (that is, represent
hash tables using TMP) is really really hard (both for a human to
write or read and for a compiler to process).

In practice, I think that TMP proponents will claim that compile time
is cheap, not that TMP is easy to implement efficiently in terms of
compiler resources, but this is a social issue, not a technical one,
and I don't have numeric data to back up this claim.


--

Walter Bright

unread,
Nov 5, 2007, 3:09:55 AM11/5/07
to
Jerry Coffin wrote:
> In article <yvednftUI9Hh5bDa...@comcast.com>,
> wal...@digitalmars-nospamm.com says...
>> To me, the problem with namespaces is they are modules lite, and failed
>> badly enough at that that exported templates were tried, and that failed
>> too, so now Daveed's module proposal is the third try. This is a bit
>> harsh, but my basis for saying this is that D has true modules, and the
>> need for namespaces and exported templates has, as a consequence, just
>> evaporated.
>
> If (for example) there had been two previous versions of the standard,
> and the first included only namespaces, then the second added export,
> your argument might make sense. As it stands, I don't see a lot of sense
> to it at all. It's true that namespaces were added before export, but
> the two deal with entirely separate problems: namespaces deal purely
> with name visibility, while export deals with separate compilation. The
> two just aren't very closely related at all. While it's true that
> modules tend to deal with both concepts together (and it may well be
> better to do so) the fact remains that these two features dealt with
> entirely separate and (at least as defined) unrelated issues.

Name visibility and separate compilation are closely related. One of the
selling points for export was coding "hygiene", where unnecessary names
wouldn't be visible. Since this is the problem namespaces were supposed
to solve, evidently namespaces were a failure at it.

The other selling points for export were 2) faster compilation and 3)
source code hiding.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Walter Bright

unread,
Nov 5, 2007, 3:08:31 AM11/5/07
to
Jerry Coffin wrote:
> In article <qOydncgL97IVsLDa...@comcast.com>,
> wal...@digitalmars-nospamm.com says...
>> I agree it doesn't rise to the level of "proof", but then again, it is a
>> reasonable argument that there are serious shortcomings in the design of
>> namespaces. But if you don't agree, that's ok, I'll just present another
>> one: exception specifications (other than nothrow). As proof (!), I've
>> never noticed e.s. (other than nothrow) used anywhere outside of a test
>> suite.
>
> I'm not sure what you're really trying to show here -- I thought we were
> discussing namespaces, but it sounds like you're primarily intent on
> showing that some of the "features" in the original C++ standard weren't
> well thought out and haven't worked well in practice.

I'm trying to show that it's not a done deal that concepts are the
right, or even an effective, answer to the problem C++ has with
incomprehensible error messages. It will take years of experience with
concepts to determine if they are the solution.

As evidence of this, I brought up some other features of C++ that were
standardized with little or no experience with them, and subsequent
experience with those features was disappointing.

One way to mitigate these problems is to adopt successful features from
other languages, or to require that at least one widely available
implementation of each new feature exist before standardization. Concept
gcc is doing that.


> I tend to agree with that overall: exception specifications and export
> (to name a couple of obvious ones) haven't shown any real utility, at
> least IMO. I even use Comeau C++, one of the only compilers to implement
> export, and I still don't use it in any real code.
>
> Exception specifications are, IMO, even worse: I'm not sure export buys
> a lot, but I think exception specifications are actively harmful to your
> code as a rule.

Yes. Exception specifications are a technical success but a semantic
failure, and export is both a technical and semantic failure.


>> I tend to view namespaces as a failed attempt at modules. It's good that
>> Daveed is trying to fix it. But I sure hope that Daveed's proposal gets
>> some wide use to prove its efficacy before freezing it into the
>> standard. It's too hard to tell just by looking at a spec.
>
> I guess that's where we differ -- I don't see namespaces as an attempt
> at modules, failed or otherwise. In fact, I have a hard time seeing
> enough similarity to figure out how they could be viewed as an attempt
> at modules, period. A namespace defines a scope for names -- nothing
> more and nothing less.

In aiming so low, namespaces aren't much of a solution to anything. You
could just as well use prefix_name rather than prefix::name.


> Modules, at least as usually defined (and Daveed's proposal is in accord
> on this point) have much broader implications for things like order of
> initialization and overall program organization. The fact that they
> happen to also define a scope is important, but hardly their sole, or
> even primary, reason for existence.

Well, yes!

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Jerry Coffin

unread,
Nov 5, 2007, 3:12:54 AM11/5/07
to
In article <1194215198....@k79g2000hse.googlegroups.com>,
yossi....@gmail.com says...

[ ... ]

> As to the "interface is easy / implementation is hard" issue - the FQA
> gives examples where the interface is easy and the implementation is
> hard. A related example: a compiler. Here, the interface /visible at
> the level of class definitions/ is easy

Far from it. The interface at the level of class definitions is quite
complex and difficult -- that's why compilers are hard to write, and
learning a typical programming language takes a considerable length of
time.

> ("compile(source_stream,asm_stream)"); the hard part - the grammar and
> meaning of the source language and the assemblers of the different
> targets - is not visible at the level of classes. There's lots of
> structure in what looks like unstructured data to a compiler - images,
> text, etc.

Even at this level, the interface isn't _nearly_ as simple as you've
tried to portray -- just for example, the compiler I use lists somewhere
around 125 different command line switches. Looking through them, some
of these really cover a number of different things, so internally they
almost certainly translate to an even larger number of parameters and/or
state variables. Some of them also interact, so the number of possible
starting states for the compiler is tremendously large.

The linker has somewhere around half as many options, but still a
decidedly nontrivial number, even though (in theory) the job of a linker
is considerably simpler.

Brooks pointed out long ago, that in a large software project,
communication becomes a limiting factor. Object oriented programming is
largely aimed at stabilizing interfaces to control the combinatorial
explosion in communication as a project grows. You've pointed out that
in some cases, that particular task is easy or even trivial. Even though
the examples you've used aren't (IMO) particularly telling, the fact is
that you're right: there are easy and even trivial cases. This has no
effect, however, on the fact that the more difficult cases are
frequently a limiting factor in development.

All in all, it comes down to one thing: the fact that there are a few
(or even quite a few) trivial cases has almost no effect on much of
anything. The difficult cases are what make a difference, and even one
difficult interface that changes a lot can throw an entire project into
havoc, regardless of how many others are simple and straightforward.

--
Later,
Jerry.

The universe is a figment of its own imagination.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

David Abrahams

unread,
Nov 5, 2007, 3:12:35 AM11/5/07
to

on Sun Nov 04 2007, Walter Bright <walter-AT-digitalmars-nospamm.com> wrote:

> David Abrahams wrote:
>> on Fri Nov 02 2007, Walter Bright <walter-AT-digitalmars-nospamm.com>
> wrote:
>>
>>> Although conceptgcc does exist and that is great, realistically, a
>>> complex feature like that needs to be in real use for years by a lot of
>>> people before it becomes clear whether or not it solves the
>>> problem.
>>
>> There is no one "the problem" being solved by the new concept support.
>> It certainly does solve many real problems, and I disagree that we
>> need years of real-world use to know that.
>
> I think we do,

In order just to know that it does solve real problems? C'mon, get
serious. It may introduce unforseen problems of its own, but it very
clearly addresses problems people have today with C++.

>>> For example, namespaces were implemented and in wide use for years
>>> before people realized that they didn't really work.
>>
>> That's sort of a meaningless statement to make without defining
>> "work." What problem do you think they're trying to solve that they
>> don't solve?
>>
>>> See the infamous "so sue me":
>>>
> http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/d9eb5db7019673e0/1aaae5a80f53bd4f?hl=en&lnk=st&q=namespaces+scott+meyers+%22so+sue+me%22#1aaae5a80f53bd4f
>>
>> With all due respect to Scott, I don't think his experience with
>> namespaces as of six years ago can be used to make a definitive
>> blanket judgement about whether namespaces "work."
>
>> The only "problem with namespaces" that I've ever seen identified is
>> really a problem with ADL, but that's a different topic.
>
> To me, the problem with namespaces is they are modules lite

No, as Jerry said, they're just scopes. Nobody I know expected them
to do more than that.

> and failed badly enough at that that exported templates were tried,

??

template export has about as much to do with namespaces as bitfields
do.

The people who advocated template export weren't trying to "do
modules" either, or they'd have tried to make something that also
worked on non-templates.

> and that failed too, so now Daveed's module proposal is the third
> try. This is a bit harsh, but my basis for saying this is that D has
> true modules, and the need for namespaces and exported templates
> has, as a consequence, just evaporated.

In C++ modules will cooperate with namespaces. I'm not sure there
ever was a need for exported templates.

> I understand why ADL exists, but never liked it, and was always looking
> for a better answer. We came up with 'overload sets', which I hope solve
> the problem (but only time will tell, of course).

I don't see how D overload sets address the issue that ADL is meant to
address. Namely:

#include <iostream>
int main()
{
std::cout << "hello, world" << std::endl;
}

how do we look up the << operator, which is defined in namespace std?

D seems to use the Python "solution" for this, which sorta works as
long as you restrict yourself to a predefined list of unary and binary
functions. You have to handle things like swap(x,y) or sin(theta),
which don't show up in the list, differently in such a system, so it
will be somewhat nonuniform.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Nemanja Trifunovic

unread,
Nov 5, 2007, 3:36:58 AM11/5/07
to
On Nov 3, 7:58 pm, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
> I'm saying that /if/ a language "decides" not to have garbage
> collection (which it has to decide on some level in order to be
> applicable to some domains), then /other/ decisions must be compatible
> with this. For example, exceptions become problematic because
> exception safe code is much harder to write.

In fact, that is very different from my personal experience. When I
had to work with C# for a couple of years, one of the most annoying
things was amount of work needed to make exception-safe code, compared
to C++. Same thing with Java, although I worked much less with this
language.

The only "GC" language I know of that enables easy writing of
exception-safe code is C++/CLI (I bet Walter will add D here, but I am
not familiar with his language :) )


--

David Abrahams

unread,
Nov 5, 2007, 4:27:24 AM11/5/07
to

on Sun Nov 04 2007, Walter Bright <walter-AT-digitalmars-nospamm.com> wrote:

> David Abrahams wrote:
>> on Fri Nov 02 2007, Walter Bright <walter-AT-digitalmars-nospamm.com>
> wrote:
>>> I know very well how and why templates slow down compilation, and this
>>> is inherent to how C++ templates work. It is not fixable.
>>
>> We did a fair amount of testing of template instantiation speed for
>>
> http://www.amazon.com/Template-Metaprogramming-Concepts-Techniques-Depth/dp/0321227255
>> and we found a huge variation between the slowest and fastest template
>> instantiators. So there's great room for improvement in at least some
>> compilers.
>
> Yes.
>
>> But even the fastest ones have performance problems because they do a
>> linear walk through a list of template specializations each time a
>> specialization is mentioned. An O(1) hash table lookup should make a
>> big difference. Why do you think it's not fixable?
>
> Because a template must be generated for every state.

You mean "instantiated," and I'm not sure what you mean by "state."
Template metaprogramming is purely functional and thus stateless.

> So, given
> something like a factorial template:
>
> template<int n> class factorial
> {
> public:
> enum
> {
> result = n * factorial<n - 1>::result
> };
> };
>
> template<> class factorial<1>
> {
> public:
> enum { result = 1 };
> };
>
> void test()
> {
> // prints 24
> printf("%d\n", factorial<4>::result);
> }
>
> If we try to do a factorial<10>, then 10 templates get
> instantiated.

One template gets instantiated ten times. But look, there's
memoization, so in theory you can ask for factorial<10>::result as
many times as you want after that without penalty. In fact there's a
linear search in all compilers I know of, so you pay :(.

> If you're using templates to do metaprogramming, this can consume a
> serious amount of memory for even fairly simple loops (and a lot of
> computation just to generate all those templates).

Yeah, that's why it's a good idea to use overload resolution instead
of class template instantiations wherever possible. The MPL
associative containers do that to get lookups in O(1) instantiations.

> Essentially, you're reduced to doing only simple things with TMP
> because of the problem of an explosion in the number of template
> instantiations.
>
> I don't see how this is fixable.

Oh, sure... it's fixable by ece.colorado.edu/~siek/phd-advert.html, a
project for which there is serious NSA grant money ;-)

But yeah, it won't be "_template_ metaprogramming" any more; at least,
I doubt it will.

> P.S. You're right that a hash lookup will deal with one aspect of
> the template instantiation explosion. But the compiler still must
> generate some data structure for that template just to look it up,
> and it's got to do it for *every* state in the template
> metaprogram.

There's no state, but I think I know what you're talking about.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Walter Bright

unread,
Nov 5, 2007, 4:26:49 AM11/5/07
to
Jerry Coffin wrote:
> While it's certainly true that factorial<10> instantiates 10
> templates, it's open to question whether that needs to be particularly
> slow. Most current compilers take quite a long time to instantiate each
> template. I'm not _sure_ exactly how much better that can be made, but
> doing some poking around in the gcc source code makes it look (at least
> to me) like there's room for considerable improvement.

Consider the handful of instructions a factorial function compiles in
to, is there any hope that executing template metaprograms can get
within 3 or 4 orders of magnitude?


> I'd also note that while factorial<10> involves 10 instantiations, the
> differences between the instantiations are quite minor. Again, I'm not
> entirely certain, but it looks to me like quite a bit of factoring could
> be done for cases like this, especially when you're dealing with non-
> type template parameters. For different types, there's little
> opportunity for factoring, but for tail-recursion on a non-type
> parameter it looks to me like you could factor it in a typical case to a
> single instantiation with a list of parameters (with a truly separate
> instantiation for the end-case).

If you can speed up g++ template processing, it's open source!


> The one thing is that I'm not sure how much that really buys -- while
> things like factorials and Fibonacci numbers make simple demonstrations
> of TMP, I can hardly recall when I've used something like this in real
> code.

It could be a chicken-and-egg thing. TMP is so awkward and limited is it
any surprise one doesn't use it much in real code? If it was easy and
efficient, wouldn't people find uses for it?


>> I don't see how this is fixable.
> Notwithstanding the above, the number of instantiations is typically
> nearly fixed -- but the time for a single instantiation is NOT...

I have a challenge for you <g>. Take the classic sieve program:

#include <stdio.h>

/* Eratosthenes Sieve prime number calculation. */

#define true 1
#define false 0
#define size 8190
#define sizepl 8191

char flags[sizepl];

int main()
{ int i, prime, k, count, iter;

printf ("10 iterations\n");
for (iter = 1; iter <= 10; iter++)
{ count = 0;
for (i = 0; i <= size; i++)
flags[i] = true;
for (i = 0; i <= size; i++)
{ if (flags[i])
{ prime = i + i + 3;
k = i + prime;
while (k <= size)
{
flags[k] = false;
k += prime;
}
count += 1;
}
}
}
printf ("\n%d primes", count);
return 0;
}

and rewrite it as a C++ template metaprogram. Benchmark the result. I'd
love to see it!

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

David Abrahams

unread,
Nov 5, 2007, 4:26:50 AM11/5/07
to

on Sun Nov 04 2007, Yossi Kreinin <yossi.kreinin-AT-gmail.com> wrote:

> On Nov 4, 8:59 am, David Abrahams <d...@boost-consulting.com> wrote:
>>
>> But even the fastest ones have performance problems because they do a
>> linear walk through a list of template specializations each time a
>> specialization is mentioned. An O(1) hash table lookup should make a
>> big difference. Why do you think it's not fixable?
>>
>
> I see at least two problems having nothing to do with the compiler
> implementation:
>
> 1. IMO the hash table look-up you mention can only be "O(1)" if the /
> key size/ is O(1). However, apparently the keys in this case are
> types, which can be generated by nesting template instantiations
> (T<T<T<1> >, X<T<T<1> > > >). When a compiler "realizes" that it
> should look up the definition of something like this, how can it do
> the look-up in O(1) - isn't /hashing the key/ O(key_size), and is
> there any limitation on the key size here?

Yes, there's no limit on the size of the type names people can
generate. However, it practice the length of those strings is not a
serious limitation. The memory allocation Walter mentioned and other
factors play a much more significant role.

> 2. The /compiler/ is not the only one potentially using slow
> algorithms here - the user code is a player, too. Consider type lists
> (which, BTW, are one "practical" example of types generated by nesting
> templates). They are not /type hashes/, are they? If you have a
> "compile time algorithm" checking whether a type is included in a type
> list, you essentially do it with a linear scan,

Believe me, I'm well aware of that. That's why the TR1 tuple's get<N>
template is probably a bad idea. If you count from 0 to N-1 doing get
on each index, you get zero reuse of memoized template instantiations,
and it is O(N^2).

> and if you merge two type lists, AFAIK it's O(N^2).

I guess that depends what you mean by "merge." It's certainly not the
case if you mean "produce one sorted type list containing all the
elements of two other sorted type lists."

> Now, I think that implementing type lists to be "compile time type
> hashes or sets" (that is, represent hash tables using TMP) is really
> really hard (both for a human to write or read and for a compiler to
> process).

Already done: http://www.boost.org/libs/mpl/doc/refmanual/set.html, etc.

> In practice, I think that TMP proponents will claim that compile time
> is cheap,

I sure wouldn't claim that.

> not that TMP is easy to implement efficiently in terms of
> compiler resources,

It definitely is not; but in most compilers it can be lots better than
it is.

> but this is a social issue, not a technical one,
> and I don't have numeric data to back up this claim.

See, I do. http://www.boost-consulting.com/mplbook, appendix C.
There's also a very preliminary version some of that research at
http://users.rcn.com/abrahams/instantiation_speed/index.html

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Kirit Sælensminde

unread,
Nov 5, 2007, 10:08:49 AM11/5/07
to

Yossi Kreinin wrote:
> Here's real code from a social network back-end :)
>
> class Lamer { public: const LameComments& getLameComments(); };
>
> void getTheMostLameComments(
> vector<const LameComments*>& comments, //which type would you use?
> const vector<Lamer>& lamers
> )
> {
> //or we could use iterators...
> for(int i=0; i<(int)lamers.size(); ++i)
> {
> const LameComments& lc = lamers[i].getLameComments();
> if(lc.areTotallyPointless()) {
> //lamers write lots of comments, better copy
> //by reference... Has to be const - can't modify
> //the lamer's precious comments, and we can't
> //have vectors of references, so it's either a dumb
> //const pointer or some non-standard smart pointer
> comments.push_back(&lc);
> }
> }
> }
>
> I think this is a pretty common pattern with code massaging data
> structures with even minor levels of nesting/indirection.

As you've discovered, just because it is a common pattern doesn't make
it good.

I think we should all ignore the completely unnecessary C cast and
move on.

This is a filter on top of a data structure. The C++ way of doing this
is to write an iterator that understands the filter rule. A bit more
advanced is to write an iterator adaptor which can be used to wrap
both const and non-const iterators on the underlying data structure.
I'm sure there's an implementation that takes a plug-in filter
predicate somewhere already.

They're not hard to write and have O(1) memory overhead compared to
the code shown above. They also side step many of the problems you
describe with the data types, const handling etc.

If you really need a copy of part of the container then
std::remove_copy_if is what you want. Again it sidesteps all of the
type problems, but I'd have to say that std::copy_if would be a more
useful name to have.

For this sort of code you want to be drawing your inspiration from
functional programming, not from OO programming.

BTW, Boost.Bind or Boost.Lambda will take care of the function
composition if you don't want to do it yourself.


K

Alf P. Steinbach

unread,
Nov 5, 2007, 10:08:07 AM11/5/07
to
* Walter Bright -> Jerry Coffin:

>
> If you can speed up g++ template processing, it's open source!

Yeah, spaghetti K&R C. I tried to fiddle with it once, and got as far
as having the compiler accepting some new syntax for exception handling.
But based on how much time that took, decided it wasn't worth it.

I find it amazing that e.g. concept GCC has been implemented.

I think one must be really dedicated (and perhaps have a /lot/ of free
time) to do that sort of thing.

Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Yossi Kreinin

unread,
Nov 5, 2007, 10:10:41 AM11/5/07
to
On Nov 5, 1:22 am, Todd Gardner <todd.gard...@gmail.com> wrote:
> Yossi Kreinin wrote:
> > On Nov 3, 4:23 am, David Abrahams <d...@boost-consulting.com> wrote:
> [...]
> >> As for the issue of casting pointers, the way const works in C++
> >> almost never forces you to cast; if you often find yourself casting
> >> away const in C++, you're almost certainly doing something very wrong.
> >> That sort of thing should only happen in the gnarliest low-level
> >> systems code and when interfacing with legacy systems that don't know
> >> about const.
>
> > I don't know if my gnarly systems code is "the gnarliest", but it's
> > not there where I met most cases when you'd have to cast away const :)
> > The problem I refer to is when you have levels of indirection, the
> > simple example being passing a vector<T*> (which means "go ahead and
> > change everything") to a function expecting a const vector<const T*>&
> > (thus saying "I'm not going to change anything").
>
> What do you see as the additional guarantee offered by const
> vector<const T*>& that isn't offered by const vector<T*>&?

The "guarantee" (which you can cast away in a snap though) is that you
can't change the objects themselves. Sometimes you get the objects
from accessors returning const references, which dictates the vector
type (or you have to cast at the point where you fill the vector). See
also the getLameComments() example in this sub-thread:

http://groups.google.com/group/comp.lang.c++.moderated/tree/browse_frm/threa
d/870d15c57e831fc5/abb84ab27024de47?rnum=61&_done=%2Fgroup%2Fcomp.lang.c%2B%
2B.moderated%2Fbrowse_frm%2Fthread%2F870d15c57e831fc5%2F5868fd6e57f6ae2c%3Ft
vc%3D1%26#doc_abb84ab27024de47

Yossi Kreinin

unread,
Nov 5, 2007, 10:10:15 AM11/5/07
to
On Nov 5, 11:26 am, David Abrahams <d...@boost-consulting.com> wrote:
>
> Yes, there's no limit on the size of the type names people can
> generate. However, it practice the length of those strings is not a
> serious limitation. The memory allocation Walter mentioned and other
> factors play a much more significant role.
>

Well, I won't argue on the relative importance of this kind of thing
since it's hard to quantify "the average C++ TMP code", and you
certainly have a better "intuitive" estimation of this "average" code
than I do.

>
> Believe me, I'm well aware of that. That's why the TR1 tuple's get<N>
> template is probably a bad idea. If you count from 0 to N-1 doing get
> on each index, you get zero reuse of memoized template instantiations,
> and it is O(N^2).
>

While someone reading this may find any of this new, my chance of
saying something new to /you/ in this department is exactly 0 :)

Here's another problem, which I'd guess you can analyze much more
deeply and precisely than myself - the extent of the need to recompile
templates in each translation unit, making the build time of TMP code
O(f(num_of_cpp_files)):

http://yosefk.com/c++fqa/defective.html#defect-3

I know that you can't parse something like AA BB(CC) without knowing
whether CC is a type name or an object name, and if CC is really
TT<PP>::II, then you must figure out which specialization of TT we're
talking about. I don't know to which extent it's theoretically
possible to cache the results of instantiations across translation
units and save processing, or which problems are the hardest for
practical code; I do know that all implementations I saw weren't very
good at it.

> > and if you merge two type lists, AFAIK it's O(N^2).
>
> I guess that depends what you mean by "merge." It's certainly not the
> case if you mean "produce one sorted type list containing all the
> elements of two other sorted type lists."

I meant non-sorted type lists; sorting the type lists before
manipulating them should be O(N*log(N)), right?

What I meant primarily was that not only do you have (possibly
constant) overheads related to template instantiation (which different
compilers will handle differently in terms of performance), but you
may end up using (in advanced TMP) algorithms which are O(f(N)) where
f(N)!=C for a constant C, which makes the instantiation overheads more
significant. I didn't mean to recommend or describe any particular TMP
technique.

>
> > Now, I think that implementing type lists to be "compile time type
> > hashes or sets" (that is, represent hash tables using TMP) is really
> > really hard (both for a human to write or read and for a compiler to
> > process).
>
> Already done:http://www.boost.org/libs/mpl/doc/refmanual/set.html, etc.
>

As I said in another sub-thread, "amazing" is an appropriate term to
describe this :)

>
> > but this is a social issue, not a technical one,
> > and I don't have numeric data to back up this claim.
>
> See, I do. http://www.boost-consulting.com/mplbook, appendix C.
> There's also a very preliminary version some of that research
athttp://users.rcn.com/abrahams/instantiation_speed/index.html
>

I meant numeric data about what TMP proponents claim, not numeric data
about instantiation speed - the former is somewhat harder to measure :)


--

Yossi Kreinin

unread,
Nov 5, 2007, 1:51:04 PM11/5/07
to
On Nov 5, 11:26 am, David Abrahams <d...@boost-consulting.com> wrote:
>
> > Now, I think that implementing type lists to be "compile time type
> > hashes or sets" (that is, represent hash tables using TMP) is really
> > really hard (both for a human to write or read and for a compiler to
> > process).
>
> Already done:http://www.boost.org/libs/mpl/doc/refmanual/set.html, etc.
>

Now that I think of it, I wonder how you implement random access for
type lists (or arrays...) of arbitrary length; in particular, I saw
this comment in the boost.org docs:

"If seq is a generic name for some Variadic Sequence, its variadic
form allows us to specify a sequence of n elements t1,t2,... tn, for
any n from 0 up to a preprocessor-configurable limit
BOOST_MPL_LIMIT_seq_SIZE"

This seems to imply that the sequence size is bound by a constant, and
this makes sense to me, because AFAIK there's no way to have a "type
array" with random access in the current C++ standard; all you have is
"compile time cons cells" like these:

template<class H, class T> struct List
{
typedef H Head;
typedef T Tail;
};

Apparently the best you can do in terms of search complexity is some
kind of sorted tree structure, but you can't have hash look-up with
random access, for example.


--

Walter Bright

unread,
Nov 5, 2007, 1:58:05 PM11/5/07
to
David Abrahams wrote:
> on Sun Nov 04 2007, Walter Bright <walter-AT-digitalmars-nospamm.com> wrote:
>
>> David Abrahams wrote:
>>> on Fri Nov 02 2007, Walter Bright <walter-AT-digitalmars-nospamm.com>
>> wrote:
>>>> Although conceptgcc does exist and that is great, realistically, a
>>>> complex feature like that needs to be in real use for years by a lot of
>>>> people before it becomes clear whether or not it solves the
>>>> problem.
>>> There is no one "the problem" being solved by the new concept support.
>>> It certainly does solve many real problems, and I disagree that we
>>> need years of real-world use to know that.
>> I think we do,
>
> In order just to know that it does solve real problems? C'mon, get
> serious. It may introduce unforseen problems of its own, but it very
> clearly addresses problems people have today with C++.

It addresses the problem by adding a fair bit of complex new syntax, and
there's going to need to be a fair bit of user education to learn how
they work and how to use them successfully. It's very possible that
ordinary users may find the complexity of concepts to be not worth their
incremental benefits. If that happens, then concepts will have failed,
even if technically they work.

D originally had a fairly complex syntax for function literals. I'd
repeatedly see comments like "D needs function literals", which I found
very frustrating because it did have them. Trying to figure out what was
going wrong, I found a way to drastically simplify the syntax. Suddenly,
people noticed the function literals, and started using them. I learned
that how complicated a feature appears has a very great bearing on its
utility.


> No, as Jerry said, they're just scopes. Nobody I know expected them
> to do more than that.
>
>> and failed badly enough at that that exported templates were tried,
>
> ??
>
> template export has about as much to do with namespaces as bitfields
> do.

As I replied to Jerry, one of the selling points of export was to hide
the names of things relevant to the template body but not relevant to
the template user. This overlaps with what namespaces do.


> The people who advocated template export weren't trying to "do
> modules" either, or they'd have tried to make something that also
> worked on non-templates.

They were, because modules address exactly the same problems that
exported templates try to address. I think one of the problems with
export is that its proponents failed to realize that what they really
needed were modules.

As evidence of this, D has true modules, and exported templates that
work and are easily implemented fell out of modules for free.


>> I understand why ADL exists, but never liked it, and was always looking
>> for a better answer. We came up with 'overload sets', which I hope solve
>> the problem (but only time will tell, of course).
>
> I don't see how D overload sets address the issue that ADL is meant to
> address. Namely:
>
> #include <iostream>
> int main()
> {
> std::cout << "hello, world" << std::endl;
> }
>
> how do we look up the << operator, which is defined in namespace std?

The simple answer is that D doesn't support global operator overloading.
The more complex answer is that classes support 'reverse' operator
overloading, where the object parameter doesn't have to always be the
left operand.

(Use of operator overloading to do I/O is abuse anyway, but that's
another discussion!)


> D seems to use the Python "solution" for this, which sorta works as
> long as you restrict yourself to a predefined list of unary and binary
> functions. You have to handle things like swap(x,y) or sin(theta),
> which don't show up in the list, differently in such a system, so it
> will be somewhat nonuniform.

I'm not sure what you mean here. Function names are looked up in
imported module namespaces without need to qualify the names.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

David Abrahams

unread,
Nov 5, 2007, 1:56:35 PM11/5/07
to

on Mon Nov 05 2007, Walter Bright <walter-AT-digitalmars-nospamm.com> wrote:

> Jerry Coffin wrote:
>> In article <qOydncgL97IVsLDa...@comcast.com>,
>> wal...@digitalmars-nospamm.com says...
>>> I agree it doesn't rise to the level of "proof", but then again, it is a
>>> reasonable argument that there are serious shortcomings in the design of
>>> namespaces. But if you don't agree, that's ok, I'll just present another
>>> one: exception specifications (other than nothrow). As proof (!), I've
>>> never noticed e.s. (other than nothrow) used anywhere outside of a test
>>> suite.
>>
>> I'm not sure what you're really trying to show here -- I thought we were
>> discussing namespaces, but it sounds like you're primarily intent on
>> showing that some of the "features" in the original C++ standard weren't
>> well thought out and haven't worked well in practice.
>
> I'm trying to show that it's not a done deal that concepts are the
> right, or even an effective, answer to the problem C++ has with
> incomprehensible error messages.

Here you go again, saying that C++ feature X is there to solve problem
Y, when it isn't. Incomprehensible error messages *from deep template
instantiations* is addressed by concepts, and addressed well. That
was an important consequence we knew the concepts feature should and
would have, but it is hardly the goal of concept support in the
language.

The *goal*, if it had to be put in a few words, was to
"Support Generic Programming." If I had to elaborate, I'd say, "by
providing first-class support for things that can only be done with
hacks or documentation today, e.g. specifying the requirements of a
template, typechecking templates before they are used, overloading
based on concept conformance, and access to associated types."

> It will take years of experience with concepts to determine if they
> are the solution.

They are not "*the* solution to incomprehensible error messages," but
they will help a lot. Many many issues contribute to C++'s problems
with error messages, some having nothing to do with templates.

>> I tend to agree with that overall: exception specifications and export
>> (to name a couple of obvious ones) haven't shown any real utility, at
>> least IMO. I even use Comeau C++, one of the only compilers to implement
>> export, and I still don't use it in any real code.
>>
>> Exception specifications are, IMO, even worse: I'm not sure export buys
>> a lot, but I think exception specifications are actively harmful to your
>> code as a rule.
>
> Yes. Exception specifications are a technical success

In what sense?

> but a semantic failure, and export is both a technical and semantic
> failure.
>

>> I guess that's where we differ -- I don't see namespaces as an attempt
>> at modules, failed or otherwise. In fact, I have a hard time seeing
>> enough similarity to figure out how they could be viewed as an attempt
>> at modules, period. A namespace defines a scope for names -- nothing
>> more and nothing less.
>
> In aiming so low, namespaces aren't much of a solution to
> anything. You could just as well use prefix_name rather than
> prefix::name.

Just like you could also write this->member when "member" would do.
At least that practice serves some practical purpose in C++.

We use prefixes to avoid macro name clashes, but we don't like it.
Prefixes suck. Namespaces allow expressive code without name clashes,
because local names and explicitly-imported names from other modules
can be used without qualification. As I've implied elsewhere, ADL is
the only thing that messes that up.


--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

David Abrahams

unread,
Nov 5, 2007, 1:55:42 PM11/5/07
to

on Mon Nov 05 2007, Walter Bright <walter-AT-digitalmars-nospamm.com> wrote:

There must be a (bad) word for that sort of fallacious argument. It's
like saying "D is a collosal failure, since it supposed to entirely
replace C++ within just a few years of its introduction."

If namespaces invented to alter name visibility, they would indeed be
a failure, because namespaces have *zero* effect on visibility.
That's not a coincidence: nobody was trying to affect visibility by
introducing namespaces.

> The other selling points for export were 2) faster compilation and 3)
> source code hiding.

Export was a major failure, but let's not get it mixed up with
namespaces. They do just what they were designed to do, modulo ADL.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Francis Glassborow

unread,
Nov 5, 2007, 1:50:21 PM11/5/07
to
Walter Bright wrote:
>
> I tend to view namespaces as a failed attempt at modules. It's good that
> Daveed is trying to fix it. But I sure hope that Daveed's proposal gets
> some wide use to prove its efficacy before freezing it into the
> standard. It's too hard to tell just by looking at a spec.
>

Which is one of the reasons WG21 decided to work on it as a TR rather
than put it directly into the current revision of the Standard. A TR
lets us try it out and, if necessary, refine it without time-pressure.

--

Francis Glassborow

unread,
Nov 5, 2007, 1:50:23 PM11/5/07
to
Yossi Kreinin wrote:
> I'm saying that /if/ a language "decides" not to have garbage
> collection (which it has to decide on some level in order to be
> applicable to some domains), then /other/ decisions must be compatible
> with this. For example, exceptions become problematic because
> exception safe code is much harder to write. Therefore, operator
> overloading becomes problematic because operators have fixed
> prototypes and thus have hard time to signal errors without
> exceptions.
>
If memory were the only dynamically allocated resource I might agree.
Exception safe code is actually easier to write in C++ as long as the
high level author remembers that dynamically acquired resources must NOT
be handled by raw pointers but be encapsulated into an object with a
dtor that will deal with resources.

Given good libraries it is very rare that an application level
programmer needs to use explicit resource acquisition.

Now contrast that with languages using GC and without dtors. Yes dynamic
memory is automatically recovered (but not always when you expect it)
but all other resources have to be handled manually (which includes
tracking the objects holding a dynamic resource.

Toy programs manage quite happily as they rarely place high demands on a
scarce resource (but the same programs can get away without managing
dynamic memory -- it is simply GCed at exit from the program :-)


--
Note that robinton.demon.co.uk addresses are no longer valid.

David Abrahams

unread,
Nov 5, 2007, 1:56:57 PM11/5/07
to

on Mon Nov 05 2007, Yossi Kreinin <yossi.kreinin-AT-gmail.com> wrote:

>
> Here's another problem, which I'd guess you can analyze much more
> deeply and precisely than myself - the extent of the need to recompile
> templates in each translation unit, making the build time of TMP code
> O(f(num_of_cpp_files)):

It depends what you mean by "recompile templates." Is that
instantiation, or just parsing?

Modules will help a lot either way.

Don't forget that some compilers do link-time instantiation, so you
don't pay for N instantiations if vector<foo> is used in N translation
units.

> http://yosefk.com/c++fqa/defective.html#defect-3
>
> I know that you can't parse something like AA BB(CC) without knowing
> whether CC is a type name or an object name, and if CC is really
> TT<PP>::II, then you must figure out which specialization of TT we're
> talking about.

Not really; that's why we have the (evil) typename keyword. It's
required when naming a type in such a context.

typename TT<PP>::II

>> > and if you merge two type lists, AFAIK it's O(N^2).
>>
>> I guess that depends what you mean by "merge." It's certainly not the
>> case if you mean "produce one sorted type list containing all the
>> elements of two other sorted type lists."
>
> I meant non-sorted type lists; sorting the type lists before
> manipulating them should be O(N*log(N)), right?

See mpl::sort.

You can do a reasonably efficient sort with forward sequences, but I
would use a different structure for 99% of all code. See mpl::vector.

> What I meant primarily was that not only do you have (possibly
> constant) overheads related to template instantiation (which different
> compilers will handle differently in terms of performance), but you
> may end up using (in advanced TMP) algorithms which are O(f(N)) where
> f(N)!=C for a constant C, which makes the instantiation overheads more
> significant. I didn't mean to recommend or describe any particular TMP
> technique.

Sure, but that's a hazard of any programming you choose to do.

>> > Now, I think that implementing type lists to be "compile time type
>> > hashes or sets" (that is, represent hash tables using TMP) is really
>> > really hard (both for a human to write or read and for a compiler to
>> > process).
>>
>> Already done:http://www.boost.org/libs/mpl/doc/refmanual/set.html, etc.
>
> As I said in another sub-thread, "amazing" is an appropriate term to
> describe this :)

Thanks :). It relies on the fact that overload resolution has a
constant so low compared template instantiation that it disappears in
the noise. It's probably at least linear in reality, but reality
never gets that big.

>> > but this is a social issue, not a technical one,
>> > and I don't have numeric data to back up this claim.
>>
>> See, I do. http://www.boost-consulting.com/mplbook, appendix C.
>> There's also a very preliminary version some of that research
> athttp://users.rcn.com/abrahams/instantiation_speed/index.html
>>
>
> I meant numeric data about what TMP proponents claim, not numeric data
> about instantiation speed - the former is somewhat harder to measure :)

Which claims are you talking about?

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Walter Bright

unread,
Nov 5, 2007, 6:27:18 PM11/5/07
to
Alf P. Steinbach wrote:
> * Walter Bright -> Jerry Coffin:
>>
>> If you can speed up g++ template processing, it's open source!
>
> Yeah, spaghetti K&R C. I tried to fiddle with it once, and got as far
> as having the compiler accepting some new syntax for exception handling.
> But based on how much time that took, decided it wasn't worth it.
>
> I find it amazing that e.g. concept GCC has been implemented.
>
> I think one must be really dedicated (and perhaps have a /lot/ of free
> time) to do that sort of thing.

To tell the truth, the way templates are implemented in DMC++ is rather
terrifying <g>. The problem is I started trying to implement it with a
rather poor understanding of how templates were supposed to work (and at
the time how templates were supposed to work was rapidly evolving as
well). So, it's a succession of layers of attempts to make it work. I
imagine g++ probably followed the same process.

It's a lot better in DMD because I was able to scrap it and start over
knowing where I needed to go.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

khall....@gmail.com

unread,
Nov 5, 2007, 6:29:59 PM11/5/07
to
On Nov 3, 8:42 am, Walter Bright <wal...@digitalmars-nospamm.com>
wrote:
> khall.ggr...@gmail.com wrote:
> > And here's something to consider: in my company (which uses primarily
> > C, but also C++, Python, C#, VB.NET, and a variety of other lesser
> > known langugages), I'm one of the few C++ advocates (though many
> > project are developed using C++). People tell me C++ is a complicated
> > language; the syntax is difficult; it's so prone to memory and
> > resource leaks. However, they are amazed when they see my C++
> > programs. They compliment me on how simple the program is (frequently
> > 1/3 the size of a similar C program -- sometimes even much smaller;
> > often smaller and more concise than .NET programs too), how well it
> > runs, how bug free it is. I'm not trying to toot my own horn, rather
> > I'm trying to point out that C++ has the tools to make understandable,
> > robust programs. It's a challenging language to master, but when used
> > properly is a beautiful tool!
>
> You've certainly piqued my curiosity. Do you have an example?

Unfortunately, the example are all internal utilities used for testing
or debugging at my work and consequently the source code is not
available for distribution. A recent example though involves a
histogram class (which also measured the mean, standard deviation,
skew, and kurtosis) that I hurredly put together for measuring some
key variables while onsite a customer site. Since my company's test
library is C, I had to translate that class into C. The C++ code was
rather concise: 203 lines. The corresponding code translated into C
was over 800 lines. The main differences was that in C++ I could
throw exceptions and I could use the standard containers. In C, I had
to write my own dynamically sized array to hold the data as well as
check for error codes about every 5th line.

As far as C++ vs. .NET code, the only real comparison I have is some
unit tests that we used for our C++ interface and our .NET interface.
We used C#. Many of our tests were translated from .NET. Since our C+
+ used the RAII idiom for acquiring resources, the .NET code ended up
being sprinkled with "using" statements with more indented
blocks. .NET has a great set of base libraries and is probably more
concise for most applications. Our test code is probably the
exception.

Yossi Kreinin

unread,
Nov 5, 2007, 6:29:15 PM11/5/07
to
On Nov 5, 8:56 pm, David Abrahams <d...@boost-consulting.com> wrote:
>
> It depends what you mean by "recompile templates." Is that
> instantiation, or just parsing?
>

Parsing C++ is pretty heavy, isn't it? And then AFAIK parsing can
trigger instantiation, for example:

http://yosefk.com/c++fqa/web-vs-c++.html#misfeature-3

AFAIK the parsing is hard to separate from semantical analysis in C++.

> Don't forget that some compilers do link-time instantiation, so you
> don't pay for N instantiations if vector<foo> is used in N translation
> units.

Apparently this wouldn't help with instantiations needed to parse
things, unless you cache things across translation units during
compilation and before the linkage (which can make I/O non-trivial and
possibly the new bottleneck; is it done anywhere?)

And I think link-time instantiation is mostly done with functions not
defined inline (and many templates end up inline either because
they're supposed to be thin wrappers in terms of the amount of object
code or because people just save the work of writing the prototype
twice). Inline functions and especially type names defined inside
templates would still have to be generated during compilation, right?

>
> Not really; that's why we have the (evil) typename keyword. It's
> required when naming a type in such a context.
>
> typename TT<PP>::II
>

But the evil typename keyword is used inside templates, not outside,
isn't it? :) And outside of a template, TT<PP>::II can trigger lots of
processing.

I guess that in TMP many of these references do end up inside
templates anyway, so the evil keyword works :)

>
> Sure, but that's a hazard of any programming you choose to do.
>

Well, so is our original subject - the quality of implementation :)

Basically, what we started with was, the C++ implementations available
today tend to implement (some of) the mechanisms on which TMP relies
inefficiently; theoretically, they could improve at least some of the
things. One reason some of them don't is (possibly) that it's hard to
deal with the correct compilation of templates by itself, and they
don't think the ROI justifies further efforts on optimizing resource
consumption.

Now, I think that the quality-of-TMP-implementation issue has a
parallel and arguably equally problematic quality-of-TMP-usage issue.
That is, C++ programmers that use TMP are likely to use inefficient
compile-time algorithms. Theoretically, they could improve some of the
things (but AFAIK not all of them - I don't see how you can do random
access for lists of N types when all you have is essentially cons
cells - I sent a separate reply on that one). One reason some of the
people using TMP won't do it in practice is that it's hard enough to
correctly use TMP, and they don't think the ROI justifies further
optimization efforts.

Or, in other words, TMP means very complicated code relying on very
complicated language mechanisms (these are two different complexities)
to do even simple things.

While I can't speak for C++ compiler writers, I can speak for TMP
users to an extent - I did use it in the past. To go totally
"social" (as opposed to "technical"): I have at least an order of
magnitude less experience with TMP than you do, but then I'm probably
much closer to a "casual" TMP user - someone who understands the basic
rules of the game and now wants to do something useful on a moderate
scale. Hence my claim about TMP being gnarly enough to stop worrying
about things like optimization. If something is really really hard to
do, frequently it won't get done, at least outside of the full time
experts' realm.

>
> Which claims are you talking about?
>

Let's drop this one - it's about claims 4 messages above in the
thread... I meant "numeric data about how many TMP proponents care
about compilation time" as opposed to "numeric data about TMP
performance"... it's just a non-subject that is kept alive by the way
we quote things :)


--

Walter Bright

unread,
Nov 5, 2007, 6:31:02 PM11/5/07
to
David Abrahams wrote:
>> Yes. Exception specifications are a technical success
> In what sense?

They are easily implementable and work according to the Standard (i.e. a
technical success). They aren't useful, though (i.e. a semantic failure).

>> but a semantic failure, and export is both a technical and semantic
>> failure.
>>
>>> I guess that's where we differ -- I don't see namespaces as an attempt
>>> at modules, failed or otherwise. In fact, I have a hard time seeing
>>> enough similarity to figure out how they could be viewed as an attempt
>>> at modules, period. A namespace defines a scope for names -- nothing
>>> more and nothing less.
>> In aiming so low, namespaces aren't much of a solution to
>> anything. You could just as well use prefix_name rather than
>> prefix::name.
>
> Just like you could also write this->member when "member" would do.
> At least that practice serves some practical purpose in C++.
>
> We use prefixes to avoid macro name clashes, but we don't like it.
> Prefixes suck. Namespaces allow expressive code without name clashes,
> because local names and explicitly-imported names from other modules
> can be used without qualification. As I've implied elsewhere, ADL is
> the only thing that messes that up.

What also messes it up (and motivated the development of overload sets
in D) is when you import namespaces so you can skip the explicit
qualification, then later import another namespace with conflicting
names, and now your source code is all messed up. (ADL is C++'s partial
answer to that problem.)

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Walter Bright

unread,
Nov 5, 2007, 6:31:47 PM11/5/07
to
David Abrahams wrote:
> on Mon Nov 05 2007, Walter Bright <walter-AT-digitalmars-nospamm.com> wrote:
>> I'm trying to show that it's not a done deal that concepts are the
>> right, or even an effective, answer to the problem C++ has with
>> incomprehensible error messages.
>
> Here you go again, saying that C++ feature X is there to solve problem
> Y, when it isn't. Incomprehensible error messages *from deep template
> instantiations* is addressed by concepts, and addressed well. That
> was an important consequence we knew the concepts feature should and
> would have, but it is hardly the goal of concept support in the
> language.

Allow me to quote from the first paragraph of "Specifying C++ Concepts"
by Reis and Stroustrup:

"Current work on improving templates
focuses on the notion of concepts (a type system for
templates), which promises significantly improved error diagnostics
and increased expressive power such as concept-based
overloading and function template partial specialization."
-- http://www.research.att.com/~bs/popl06.pdf

It's the FIRST thing mentioned about what concepts are intended for. I
think it is entirely reasonable for me to infer that concepts are there
to solve that problem.


In the Wikipedia entry for C++0x:

"The primary motivation of the introduction of concepts is to improve
the quality of compiler error messages." --
http://en.wikipedia.org/wiki/C%2B%2B0x#Concept


If it's all a mistake, certainly the proponents of concepts need to do a
much better job explaining what concepts are actually for.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Yossi Kreinin

unread,
Nov 5, 2007, 6:33:25 PM11/5/07
to
On Nov 5, 5:08 pm, Kirit Sælensminde <kirit.saelensmi...@gmail.com>
wrote:

> Yossi Kreinin wrote:
> > Here's real code from a social network back-end :)
>
> > class Lamer { public: const LameComments& getLameComments(); };
>
> > void getTheMostLameComments(
> > vector<const LameComments*>& comments, //which type would you use?
> > const vector<Lamer>& lamers
> > )
> > {
> > //or we could use iterators...
> > for(int i=0; i<(int)lamers.size(); ++i)
> > {
> > const LameComments& lc = lamers[i].getLameComments();
> > if(lc.areTotallyPointless()) {
> > //lamers write lots of comments, better copy
> > //by reference... Has to be const - can't modify
> > //the lamer's precious comments, and we can't
> > //have vectors of references, so it's either a dumb
> > //const pointer or some non-standard smart pointer
> > comments.push_back(&lc);
> > }
> > }
> > }
>
> > I think this is a pretty common pattern with code massaging data
> > structures with even minor levels of nesting/indirection.
>
> As you've discovered, just because it is a common pattern doesn't make
> it good.

The thing that I want to compute is certainly not "bad" by itself - it
is a completely trivial filter, it should be trivial to implement, and
it is trivial to implement in all popular programming languages. It is
also pretty trivial in C++, except for the const part; my point was to
show that sometimes you naturally end up with vectors of const
pointers in very simple code, contrary to the arguments in this NG.

>
> I think we should all ignore the completely unnecessary C cast and
> move on.
>

I don't know which cast you mean; the code has no cast. A typing
problem lurking at the horizon is that now we have a vector<const T*>,
so a function written to process it would accept an argument of this
type. We then won't be able to pass a vector<T*> to this function,
which is what about 4 sub-threads somehow converged to discuss. Now, /
this/ problem should certainly not be solved by a C cast because that
would be undefined behavior; it should be solved by copying the
vector. We could have /prevented/ it by a C cast to the example code;
is that (currently invisible) cast the one you meant?

It could be that we're already on the same page, and thus I wasted
space on this description; I justed wanted to make sure we really were
on the same page.

> This is a filter on top of a data structure. The C++ way of doing this
> is to write an iterator that understands the filter rule. A bit more
> advanced is to write an iterator adaptor which can be used to wrap
> both const and non-const iterators on the underlying data structure.
> I'm sure there's an implementation that takes a plug-in filter
> predicate somewhere already.
>
> They're not hard to write and have O(1) memory overhead compared to
> the code shown above. They also side step many of the problems you
> describe with the data types, const handling etc.
>

As you mention yourself later, sometimes it's a bad idea since I
really want to create a container in memory. For example, there could
be lots of Lamer objects but only few comments which are
isTotallyPointless, and these comments later undergo heavy processing.
I wouldn't want to scan through the whole data structure again and
again.

Still, while we're at the process of exchanging "real code", it would
be nice if you contributed an example implementation of your idea for
the cases when it is appropriate in terms of run time (there are few
Lamer objects and/or most comments are isTotallyPointless). The
readers could then judge whether what you call "The C++ way of doing
this" (trivial) thing is really "not hard to write" compared to the
(primitive as I think it should be) code I posted. BTW, here's how you
do it in Python - an example of a language with built-in support for
this kind of thing:

comments = [l.getLameComments() for l in lamers \
if l.getLameComments().isTotallyPointless()]

If you could extend the test to also compare the date of the comment
to a threshold, I'd be happy to post my version of this so that people
can compare the readability.

> If you really need a copy of part of the container then
> std::remove_copy_if is what you want. Again it sidesteps all of the
> type problems, but I'd have to say that std::copy_if would be a more
> useful name to have.

But I /can't/ use remove_copy_if or otherwise modify the container,
because it's typed const vector<Lamer>&, bringing us back to the
subject of the downsides of C++ const correctness. To use
remove_copy_if, I'd have to create a vector of const pointers to Lamer
objects (or LameComments objects or I could use
vector<Lamer>::const_iterator instead of const Lamer*), since this is
the only kind of pointer to the objects I can obtain without a cast.
And having a vector of const pointers or const_iterators brings us
back to square one. Or I'd have to copy the Lamer objects or all of
the LameComments objects, which has its run time cost.

What I'm saying is, to work around this you need to const_cast
pointers or copy objects or copy code (ultimately have two functions
somewhere later, one for vector<const T*> and another one for
vector<T*>). All of these mean that const gets in your way in this
example.

>
> For this sort of code you want to be drawing your inspiration from
> functional programming, not from OO programming.
>

There's another angle: the problem is prosaic and trivial, as the code
solving it should be.

> BTW, Boost.Bind or Boost.Lambda will take care of the function
> composition if you don't want to do it yourself.
>

Well, I certainly don't (I had my share of this years ago); but if you
do, please post example code so people can see what it looks like.
Boost.Lambda is not at all simple from the user's perspective:

http://yosefk.com/c++fqa/function.html#fqa-33.10

Walter Bright

unread,
Nov 5, 2007, 6:32:58 PM11/5/07
to
David Abrahams wrote:
> If namespaces invented to alter name visibility, they would indeed be
> a failure, because namespaces have *zero* effect on visibility.
> That's not a coincidence: nobody was trying to affect visibility by
> introducing namespaces.

You and I must be working with different definitions of visibility. I
regard a name as 'visible' if it is found when looked up. For example:

int x;
void foo()
{ int x;
x++; // global x is not visible, local x is
}

Namespaces enable various means of control over the visibility of the
names in that namespace.

You can wrap a C header in a namespace - doesn't that alter its
visibility? Isn't that the point?


----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Walter Bright

unread,
Nov 5, 2007, 6:32:32 PM11/5/07
to
David Abrahams wrote:
> on Sun Nov 04 2007, Walter Bright <walter-AT-digitalmars-nospamm.com> wrote:
>> David Abrahams wrote:
>>> The only "problem with namespaces" that I've ever seen identified is
>>> really a problem with ADL, but that's a different topic.
>> To me, the problem with namespaces is they are modules lite
> No, as Jerry said, they're just scopes. Nobody I know expected them
> to do more than that.

>From "The C++ Programming Language" 3rd Edition by Stroustrup:

pg. 166: 'In real programs, each "module" represented by a separate
namespace will often have hundreds of functions, classes, templates, etc.'

pg. 167: 'Therefore, the discussion of how to represent modules as
namespaces...'

There's plenty more. Stroustrup's book clearly associates namespaces
with an attempt at modules. With the benefit of years of experience and
hindsight we now know they fall short. That is my point - it's hard to
know in advance how a complex new feature will really work out.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Walter Bright

unread,
Nov 5, 2007, 9:50:18 PM11/5/07
to
David Abrahams wrote:
> If namespaces invented to alter name visibility, they would indeed be
> a failure, because namespaces have *zero* effect on visibility.
> That's not a coincidence: nobody was trying to affect visibility by
> introducing namespaces.

C++98 3.3.7:
"During the lookup of a name qualified by a namespace name, declarations
that would otherwise be made visible by a using-directive can be hidden
by declarations with the same name in the namespace containing the
using-directive; see (3.4.3.2)."

In other words, namespaces do affect visibility. There's more in the
spec about namespaces and visibility, just search for 'visibility'. I
believe the definition I use for name visibility is consistent with the
standard's.

----
Walter Bright
Digital Mars C, C++, D programming language compilers
http://www.digitalmars.com

--

Eugene Gershnik

unread,
Nov 6, 2007, 2:29:56 PM11/6/07
to
On Nov 3, 6:42 pm, Walter Bright <wal...@digitalmars-nospamm.com>
wrote:
>
> Is there any evidence that export in practice improves build times?

Is there any evidence to the contrary?

> I've looked in to implementing export. Based on how it must work, there
> is not much prospect of it fundamentally improving build times.

Since you know infinitely more in this area than I do I'll accept
this, provided nobody with the same level of expertise disagrees.
However, this is beside the point. The problem with C++ build times
has very little to do with templates or any other new language
feature. Even plain C code written 20 years ago still builds way
slower than a big modern Java project.
Had the OP put on his site "C++ build times suck, period" I don't
think anybody would disagree. His claim that templates are somehow
responsible for this is ridiculous.

> And
> seeing how export has been available for several years now, yet has
> failed to catch fire, doesn't auger well for its utility.

Sorry but this argument is simply not true. 'Available' for the vast
majority of C++ users means 'available from the compiler vendor that
they are forced to use for other reasons'. Most programmers had never
seen export-enabled compiler because they have to use MSVC, gcc etc.
Until MSVC, for example, supplies export, it is meaningless to say
that export is somehow available to the huge number of C++ users
locked into it (And isn't it funny that a company that has resources
to create something like .NET claims that it cannot find a few bucks
to implement export in its C++ compiler)

> What is proven to substantially reduce build times are precompiled
> headers (but p.h., in order to work, has to place restrictions on what
> can be put into headers).

As far as language comparisons go, it is like taking advil for cancer.
Even with best code layout and optimal use of precompiled headers C or
C++ are not in the same league as Java or C#.

--
Eugene

David Abrahams

unread,
Nov 6, 2007, 2:39:43 PM11/6/07
to
on Mon Nov 05 2007, Yossi Kreinin <yossi.kreinin-AT-gmail.com> wrote:

> On Nov 5, 11:26 am, David Abrahams <d...@boost-consulting.com> wrote:
>>
>> > Now, I think that implementing type lists to be "compile time type
>> > hashes or sets" (that is, represent hash tables using TMP) is really
>> > really hard (both for a human to write or read and for a compiler to
>> > process).
>>
>> Already done:http://www.boost.org/libs/mpl/doc/refmanual/set.html, etc.
>>
>
> Now that I think of it, I wonder how you implement random access for
> type lists (or arrays...) of arbitrary length; in particular, I saw
> this comment in the boost.org docs:
>
> "If seq is a generic name for some Variadic Sequence, its variadic
> form allows us to specify a sequence of n elements t1,t2,... tn, for
> any n from 0 up to a preprocessor-configurable limit
> BOOST_MPL_LIMIT_seq_SIZE"
>
> This seems to imply that the sequence size is bound by a constant,

Yes. But that's okay; you can change the constant.

> and this makes sense to me, because AFAIK there's no way to have a
> "type array" with random access in the current C++ standard; all you
> have is "compile time cons cells" like these:
>
> template<class H, class T> struct List
> {
> typedef H Head;
> typedef T Tail;
> };

No, you also have

struct vector0 {};
template <class A0> struct vector1 {};
template <class A0, class A1> struct vector2 {};
template <class A0, class A1, class A3> struct vector3 {};

Then you can use a bunch of specializations to get at any vector
element in O(1) template instantiations. If your compiler supports
typeof(), you can even do that with unbounded typelists, by creating
an overload set. See my sieve posting.

> Apparently the best you can do in terms of search complexity is some
> kind of sorted tree structure, but you can't have hash look-up with
> random access, for example.

Sure you can. Anything you can do in other pure functional languages,
you can do with templates. It's just a lot uglier ;-)

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

James Dennett

unread,
Nov 6, 2007, 2:39:14 PM11/6/07
to
Walter Bright wrote:
> David Abrahams wrote:
>> on Sun Nov 04 2007, Walter Bright <walter-AT-digitalmars-nospamm.com>
>> wrote:
>>> David Abrahams wrote:
>>>> The only "problem with namespaces" that I've ever seen identified is
>>>> really a problem with ADL, but that's a different topic.
>>> To me, the problem with namespaces is they are modules lite
>> No, as Jerry said, they're just scopes. Nobody I know expected them
>> to do more than that.
>
>> From "The C++ Programming Language" 3rd Edition by Stroustrup:
>
> pg. 166: 'In real programs, each "module" represented by a separate
> namespace will often have hundreds of functions, classes, templates, etc.'
>
> pg. 167: 'Therefore, the discussion of how to represent modules as
> namespaces...'
>
> There's plenty more. Stroustrup's book clearly associates namespaces
> with an attempt at modules.

Your quotations don't support your conclusion. They show that
namespaces *can be used* to represent some of what modules are
about. They're not an attempt to implement full modules in the
normal programming-language sense of the term, though they do
correspond closely enough to more general notions of modules
(or components, in some sense) of programs.

It's possible to interpret things as if they support your
conclusion, but it seems a stretch to do so. It was very clear
when namespaces were designed that they did not tackle the same
range of issues as "modules" from other languages did -- but
then classes already overlap fairly heavily with modules, and
in *some sense* could be said to subsume common notions of
modules.

However, I do look forward to the first real attempt at module
support in C++. It'll be based on long and broad experience,
and will be well tested in the field before standardization.

-- James

Eugene Gershnik

unread,
Nov 6, 2007, 2:33:35 PM11/6/07
to
On Nov 4, 3:00 am, Yossi Kreinin <yossi.krei...@gmail.com> wrote:
>
> The problem I refer to is when you have levels of indirection, the
> simple example being passing a vector<T*> (which means "go ahead and
> change everything") to a function expecting a const vector<const T*>&
> (thus saying "I'm not going to change anything").
>
> "Intuitively" (when you consider what const "means" at the high
> level), this should work. I know why it doesn't work, and I know why
> the case where the function expects a non-const vector<const T*>& /
> shouldn't/ work "at the high level" (the function could stuff pointers
> to immutable objects into the vector to have them modified later by
> the caller, for example).
>
> What I don't understand is, why don't some other C++ programmers
> consider this a problem, at some level?

It is a problem on some level. One can argue also that container of
apples also must be IS-A immutable container of fruit. If you know of
a language that makes it happen I will be very interested to learn
it.
Regardless, the fact that some rare corner case has a problem doesn't
make const bad or unusable.

> Yes, we all understand why it
> doesn't work according to the language definition, but why isn't the
> problem at all important?

Because a good workaround is simpler than typing this sentence. And
because so far there seems to exist only a single user who both
encounters this issue and claims that he cannot use one of the the
workarounds for some perceived problems.
Very few people need pointers in their containers to begin with and it
has nothing to do with this issue. Raw pointers are bad to model
anything but 'knows about' and there are few instances of pure
'container of know-abouts' in any design.
Personally I have only encountered such constructs in caches, lookup
indices or some OS-level code but that's about it.

> People in this NG said that they don't const-
> qualify container element types.

Why not? I see nothing wrong with container<const T *> declared so to
begin with. (As long as it models what it says).
Your famous problem is the one of expectations only. Since I don't
expect container<T *> to be convertible to const container<const T *>
& I don't hit any problems with such containers in the rare cases I
need them.


--
Eugene

It is loading more messages.
0 new messages