Email conversation between myself and Marshall Cline

Some background: Marshall Cline has served on the ISO C++ and Smalltalk standardisation committees. He maintains the C++ FAQ Lite which is how we ended up conversing.

In the following conversation which required myself to sit down for at least four hours per night for a week, I learned a lot of stuff and possibly Marshall may even have learned one or two things as well. In the following X emails, we cover the following subjects:

You can return to nedprod.com here


From: Niall Douglas <xxx@xxxxxxx.xxx>
To: xxxxx@xxxxxxxxx.xxx
Subject: Comments on your C++ FAQ
Date: Sat, 27 Jul 2002 00:05:45 +0200

Firstly, it looks good, and I learned some things (like delete NULL; 
isn't supposed to cause an exception). Thanks.

However ...

I would prefer if you said making classes final leafs is evil. The 
use of private constructors with static ctors should be evil. 
*Protected* constructors with the same is fine. The reason I say this 
is recently I've had to modify quite a lot of code which kept its 
internal data and constructors private and hence my subclasses 
couldn't access them. My view is that good C++ should never ever 
think it won't be subclassed by someone without the sources - to that 
end I make most private variables protected and I rely on discipline 
not to access them. The same should go for constructors. I of course 
fully support commenting it with "subclass this under threat of death 
signed JS". (I know one solution is for the third party to modify the 
header files but that seems dirty and error prone to me).

Regarding static initialisation problems, for objects the trick is to 
create a helper class which initialises the static objects on 
construction and destructs them appropriately. The static members 
should be pointers or references to the classes. You can either new 
the pointers or else contain the classes in your helper class and 
write the pointers or references in the constructor. Then you simply 
instantiate the helper class first thing in main() and then it 
destructs last thing when main() exits.

Templates are funny things on different platforms. I ran into a 
problem which compiled fine on Linux but MSVC6 fell over where I was 
doing this:
int compareItems(void *a, void *b)
return ((T *) a)<((T *) b);

MSVC complained there was no < operator for T during the template 
definition, not at actual template use to create a class (where I 
think it should throw an error if the T passed doesn't have the 
operator). To get around it, I created a dummy class with the 
operator which I made all T's subclasses thereof and now MSVC shut 
up. Annoying though. My point is, I'd prefer your section on 
templates to point out more the stuff which doesn't work as it should 
- you make it sound like it all works according to spec, which it 
rarely in my experience does.

Lastly, I know you think macros are evil, but I have always used them 
to implement functionality which C++ should have. The biggest of 
these is try...finally without which multithreaded programming is a 
severe pain. Of course, you don't call the macro 'finally' - I've 
used TERRH_TRYF...TERRH_FINALLY...TERRH_ENDTRYF to make it very 
clear. In this context, macros are very useful and I think to be 
encouraged so long as the macro: (a) says where it's defined and (b) 
all caps.

Anyway, hope you don't mind these comments. I would hardly consider 
myself much good at C++, I've only been programming in it for three 
years or so and it still befuddles me at times (unlike in C or 
assembler, where I always know what to do).

Cheers,
Niall Douglas




From: "Marshall Cline" <xxxxx@xxxxxxxxx.xxx>
To: "'Niall Douglas'" <xxx@xxxxxxx.xxx>
Subject: RE: Comments on your C++ FAQ
Date: Fri, 26 Jul 2002 20:01:03 -0500

Hi Niall,

Thanks for your thoughtful note. Unfortunately I disagree with most
of your suggestions (see below for details). But I don't way my
disagreements to detract from my appreciation that you took the time
to write in the first place.

[See details below.]

Niall Douglas wrote:
>I would prefer if you said making classes final leafs is evil. The
>use of private constructors with static ctors should be evil.
>*Protected* constructors with the same is fine. The reason I say
>this is recently I've had to modify quite a lot of code which kept
>its internal data and constructors private and hence my subclasses
>couldn't access them. My view is that good C++ should never ever
>think it won't be subclassed by someone without the sources

Sorry, you're wrong above: good C++ classes very well might (and
often do!) want to make sure they don't get subclassed. There are
lots of reasons for this, including the practical reality that the
constraints on a base class is much more stringent than a leaf
class, and in some cases those constraints make it very expensive,
and perhaps even impossible, to make a class inheritable. When
people inherit anyway, all sorts of bad things happen, such as the
slicing problem (AKA chopped copies), the covariant parameter on the
assignment operator, etc., etc.

So making a class a leaf / final is *not* evil. In some cases *not*
making a class into a leaf / final is evil! In other words, in
cases like these, the programmer is not simply *allowed* to make the
class final, but has an actual fiduciary responsibility to do so.
It would be professionally irresponsible to *not* make the class a
leaf / final.

I recognize that you've had a bad experience trying to inherit from
a class with private stuff. But you must not generalize that bad
experience or take it to mean leaf classes are universally bad.
Your bad experience may mean the base class's programmer did
something wrong, or it may mean you did something wrong. But the
guideline / rule you propose ("final / leaf classes are evil") will
do more harm than good.

>- to
>that end I make most private variables protected and I rely on
>discipline not to access them. The same should go for
>constructors. I of course fully support commenting it with "subclass
>this under threat of death signed JS". (I know one solution is for
>the third party to modify the header files but that seems dirty and
>error prone to me).
>
>Regarding static initialisation problems, for objects the trick is
>to create a helper class which initialises the static objects on
>construction and destructs them appropriately. The static members
>should be pointers or references to the classes. You can either new
>the pointers or else contain the classes in your helper class and
>write the pointers or references in the constructor. Then you simply
>instantiate the helper class first thing in main() and then it
>destructs last thing when main() exits.

Sorry, but this is a very poor solution. It certainly works in a
few cases, but it violates the first premise of pluggable software.
The three solutions described in the FAQ are much better: they
handle all the trivial cases that yours handles, and in addition
they handle the more sophisticated "plugability" cases.


>Templates are funny things on different platforms. I ran into a
>problem which compiled fine on Linux but MSVC6 fell over where I was
>doing this:
>
>int compareItems(void *a, void *b)
>return ((T *) a)<((T *) b);
>
>MSVC complained there was no < operator for T during the template
>definition, not at actual template use to create a class (where I
>think it should throw an error if the T passed doesn't have the
>operator).

There must be something wrong with your code or your description,
because the code you've given above will never generate an error
irrespective of the type 'T'. The '<' is applied between two
pointers, not between two 'T's.

(I'm obviously assuming you fix the compile-time bugs in the code,
e.g., by adding 'template<class T>', and by wrapping the function
body in '{' and '}'.)


>To get around it, I created a dummy class with the
>operator which I made all T's subclasses thereof and now MSVC shut
>up. Annoying though. My point is, I'd prefer your section on
>templates to point out more the stuff which doesn't work as it
>should - you make it sound like it all works according to spec,
>which it rarely in my experience does.

I am interested in this error, but I'm only interested in it if it
is real, that is, if you can get MS VC++ to actually generate an
incorrect error message. I'd suggest rewriting the example, trying
it, and seeing if you can actually create a case that fails with MS
VC++.


>Lastly, I know you think macros are evil, but I have always used
>them to implement functionality which C++ should have. The biggest
>of these is try...finally without which multithreaded programming is
>a severe pain. Of course, you don't call the macro 'finally' - I've
>used TERRH_TRYF...TERRH_FINALLY...TERRH_ENDTRYF to make it very
>clear. In this context, macros are very useful and I think to be
>encouraged so long as the macro: (a) says where it's defined and (b)
>all caps.

Sorry, but I would strongly recommend against the above. That's
just not the C++ way of doing things. To be honest, it makes you
sound like you're a Java programmer who has learned C++ syntax but
still hasn't mastered C++ idioms.


>Anyway, hope you don't mind these comments.

Not at all.

Marshall




From: Niall Douglas <xxx@xxxxxxx.xxx>
To: "Marshall Cline" <xxxxx@xxxxxxxxx.xxx>
Subject: RE: Comments on your C++ FAQ
Date: Sun, 28 Jul 2002 03:42:12 +0200

On 26 Jul 2002 at 20:01, Marshall Cline wrote:

> Sorry, you're wrong above: good C++ classes very well might (and
> often do!) want to make sure they don't get subclassed. There are
> lots of reasons for this, including the practical reality that the
> constraints on a base class is much more stringent than a leaf class,
> and in some cases those constraints make it very expensive, and
> perhaps even impossible, to make a class inheritable. 

I had thought one was meant to encourage reusability in every line of 
C++ you wrote? Where possible obviously.

To that end, and I will admit I have only worked on small C++ 
projects (less than 5k lines), it has always appeared to me not 
hugely difficult to structure your code appropriately to ensure 
maximum reusability.

> When people
> inherit anyway, all sorts of bad things happen, such as the slicing
> problem (AKA chopped copies), the covariant parameter on the
> assignment operator, etc., etc.

Surely chopped copies are an optimisation problem for the compiler? 
You can help it of course by trying to ensure subclasses never undo 
or redo operations the base class did.

I'm afraid I don't understand covariant parameter on the assignment 
operator.

If what you say about inheriting is bad, then I would say my code is 
very foul. I have huge depths of subclassing as a normal part of 
writing my code. It's not just me - I picked up the idea from Qt 
(http://www.trolltech.com/). I've basically learned C++ by copying 
their example.

> I recognize that you've had a bad experience trying to inherit from a
> class with private stuff. But you must not generalize that bad
> experience or take it to mean leaf classes are universally bad. Your
> bad experience may mean the base class's programmer did something
> wrong, or it may mean you did something wrong. But the guideline /
> rule you propose ("final / leaf classes are evil") will do more harm
> than good.

No all he/she did wrong was assume no one would ever want to subclass 
their class. Not only is that arrogant and assumes themselves 
infallible, it seems to me bad practice and against the whole idea of 
software reusability.

> >Regarding static initialisation problems, for objects the trick is to
> >create a helper class which initialises the static objects on
> >construction and destructs them appropriately. The static members
> >should be pointers or references to the classes. You can either new
> >the pointers or else contain the classes in your helper class and
> >write the pointers or references in the constructor. Then you simply
> >instantiate the helper class first thing in main() and then it
> >destructs last thing when main() exits.
> 
> Sorry, but this is a very poor solution. It certainly works in a few
> cases, but it violates the first premise of pluggable software. The
> three solutions described in the FAQ are much better: they handle all
> the trivial cases that yours handles, and in addition they handle the
> more sophisticated "plugability" cases.

I'll review your FAQ's suggestions again. Maybe I missed something, 
but they all seemed to have fairly substantial caveats.

> There must be something wrong with your code or your description,
> because the code you've given above will never generate an error
> irrespective of the type 'T'. The '<' is applied between two
> pointers, not between two 'T's.

Agreed. Here is the actual code:
template<class type> class TEXPORT_TCOMMON TSortedList : public 
QList<type>
{
...
virtual int compareItems( QCollection::Item s1, QCollection::Item s2 
)
{
if(*((type *) s1)==*((type *) s2)) return 0;
return (*((type *) s1)<*((type *) s2) ? -1 : 1 );
}
}

This code generates an error saying operators == and < for class type 
don't exist. Changing type to T or anything else doesn't help. Oddly, 
almost identical code compiles elsewhere plus it worked on Linux last 
time I compiled it (quite some time ago).

That's MSVC 6 SP6.

> I am interested in this error, but I'm only interested in it if it is
> real, that is, if you can get MS VC++ to actually generate an
> incorrect error message. I'd suggest rewriting the example, trying
> it, and seeing if you can actually create a case that fails with MS
> VC++.

Hardly important unless Visual Studio .NET has the same problem. MS 
no longer consider MSVC6 a primary support product.

I have tried changing it around, but in the end I have bigger 
priorties. In the end, if it causes me too many problems, I'll switch 
to GCC.

> >Lastly, I know you think macros are evil, but I have always used them
> >to implement functionality which C++ should have. The biggest of
> >these is try...finally without which multithreaded programming is a
> >severe pain. Of course, you don't call the macro 'finally' - I've
> >used TERRH_TRYF...TERRH_FINALLY...TERRH_ENDTRYF to make it very
> >clear. In this context, macros are very useful and I think to be
> >encouraged so long as the macro: (a) says where it's defined and (b)
> >all caps.
> 
> Sorry, but I would strongly recommend against the above. That's
> just not the C++ way of doing things. To be honest, it makes you
> sound like you're a Java programmer who has learned C++ syntax but
> still hasn't mastered C++ idioms.

God no, I hated Java. I'm actually an assembler programmer originally 
which of course uses loads of macros all the time.

How, might I ask, would you suggest you implement try...finally 
without macros in "the C++ way of doing things"? I am assuming you 
will surely agree multithreaded programming is not fun without 
try...finally (if you don't, I want reasons, it'll be interesting to 
see what you'd come up with).

> >Anyway, hope you don't mind these comments.
> 
> Not at all.

I used to read the reports of the ANSI committee meetings regarding 
C++ as it was still being formalised and it always struck me as being 
an awful hodge-podge. The more I learn about it, the more I realise I 
was correct! I never had much experience with it, but Objective C 
always seemed a lot cleaner. Unfortunately, today is now and the 
world we live in uses C++.

I take it you come from a Smalltalk background? I know this will 
sound approaching blasphemous - and I don't mean at all to be 
offensive, but merely to garner an opinion - but I have always 
considered OO to be a good way of organising maintainable source but 
really crap for designing code. I suppose I still write C++ like I 
did assembler (and thereafter C) in that fashion, and hence our great 
difference in style.

Cheers,
Niall




From: "Marshall Cline" <xxxxx@xxxxxxxxx.xxx>
To: "'Niall Douglas'" <xxx@xxxxxxx.xxx>
Subject: RE: Comments on your C++ FAQ
Date: Sat, 27 Jul 2002 20:26:46 -0500

Niall Douglas wrote:
>On 26 Jul 2002 at 20:01, Marshall Cline wrote:
>
>> Sorry, you're wrong above: good C++ classes very well might (and
often 
>> do!) want to make sure they don't get subclassed. There are lots of 
>> reasons for this, including the practical reality that the
constraints 
>> on a base class is much more stringent than a leaf class, and in some

>> cases those constraints make it very expensive, and perhaps even 
>> impossible, to make a class inheritable.
>
>I had thought one was meant to encourage reusability in every line of 
>C++ you wrote? Where possible obviously.

Nope. In fact, that approach normally causes projects to fail.

Instead of encouraging reusability in every line of C++ code (where
possible), the goal is to be a responsible professional who sometimes
invests effort in a future pay-back (AKA reuse), and sometimes does not.
A responsible professional does not invest in a future pay-back when the
ROI isn't right (return-on-investment) or when doing so would add
unacceptable risk or cost or time to the current project. Balancing the
future with the present is a very subtle task, but that is the task of a
responsible professional.


>To that end, and I will admit I have only worked on small C++ 
>projects (less than 5k lines), it has always appeared to me not 
>hugely difficult to structure your code appropriately to ensure 
>maximum reusability.

Everyone, including you, is to some extent a prisoner of their past.
Your experience with very small projects limits your ability to
understand how things work in the real world with large projects. I
don't fault you for your lack of experience with large projects, but in
a similar way you must not presume that your small-system experiences
are applicable or relevant to the way things happen in that other world.


>> When people
>> inherit anyway, all sorts of bad things happen, such as the slicing 
>> problem (AKA chopped copies), the covariant parameter on the 
>> assignment operator, etc., etc.
>
>Surely chopped copies are an optimisation problem for the compiler? 

No, not at all. They are logical errors - places where the compiler is
*required* by the language to generate code that does "the wrong thing."
So even if the compiler had a *perfect* optimizer, it would still be
required by the language generate code that the programmer would
ultimately consider a bug. But it's not a bug in the compiler; it's a
bug in the programmer's code.


>You can help it of course by trying to ensure subclasses never undo 
>or redo operations the base class did.

No, not at all.

I don't think you understand what the slicing problem (AKA chopped
copies) is all about. I suggest you read the FAQ on that one.


>I'm afraid I don't understand covariant parameter on the assignment 
>operator.

I don't think the FAQ covers this one. I'll try to give a very brief
overview. Suppose someone inherits D from B, and passes a D to a
function expecting a B& (reference-to-B). If that function assigns to
the B&, then only the B part of the D object is changed, and the object
often goes into a nonsensical state. It's kind of like genetic
engineering, where biologists scoop out the chromosomes from one animal,
and replace them with the chromosomes of another. It's real easy to get
garbage when you do that.


>If what you say about inheriting is bad, then I would say my code is 
>very foul. I have huge depths of subclassing as a normal part of 
>writing my code. 

Ouch, that's certainly a very dubious design style. It's a typical
hacker's style, and it comes from the Smalltalk world, but it's
generally inappropriate for C++ or Java or any other statically typed OO
language.


>It's not just me - I picked up the idea from Qt 
>(http://www.trolltech.com/). I've basically learned C++ by copying 
>their example.

If you're building an application framework (especially if you're
building a GUI framework), you might have chosen a reasonable piece of
software to learn from. If you're simply *using* a framework to build
some other app, you've chosen poorly.


>> I recognize that you've had a bad experience trying to inherit from a

>> class with private stuff. But you must not generalize that bad 
>> experience or take it to mean leaf classes are universally bad. Your 
>> bad experience may mean the base class's programmer did something 
>> wrong, or it may mean you did something wrong. But the guideline / 
>> rule you propose ("final / leaf classes are evil") will do more harm 
>> than good.
>
>No all he/she did wrong was assume no one would ever want to subclass 
>their class. Not only is that arrogant and assumes themselves 
>infallible, it seems to me bad practice and against the whole idea of 
>software reusability.

You seem to have a wrong notion of how reusability and inheritance are
supposed to mix. Inheritance is not "for" reuse. One does *not*
inherit from something to reuse that thing.


>> >Regarding static initialisation problems, for objects the trick is
to 
>> >create a helper class which initialises the static objects on 
>> >construction and destructs them appropriately. The static members 
>> >should be pointers or references to the classes. You can either new 
>> >the pointers or else contain the classes in your helper class and 
>> >write the pointers or references in the constructor. Then you simply
>> >instantiate the helper class first thing in main() and then it 
>> >destructs last thing when main() exits.
>> 
>> Sorry, but this is a very poor solution. It certainly works in a few
>> cases, but it violates the first premise of pluggable software. The 
>> three solutions described in the FAQ are much better: they handle all
>> the trivial cases that yours handles, and in addition they handle the
>> more sophisticated "plugability" cases.
>
>I'll review your FAQ's suggestions again. Maybe I missed something, 
>but they all seemed to have fairly substantial caveats.

They all do. But at least they all solve the pluggability problem,
which is typically the core reason for using this sort of syntax /
technique.


>> There must be something wrong with your code or your description, 
>> because the code you've given above will never generate an error 
>> irrespective of the type 'T'. The '<' is applied between two 
>> pointers, not between two 'T's.
>
>Agreed. Here is the actual code:
>template<class type> class TEXPORT_TCOMMON TSortedList : public 
>QList<type>
>{
>...
> virtual int compareItems( QCollection::Item s1,
QCollection::Item s2 
>)
> {
> if(*((type *) s1)==*((type *) s2)) return 0;
> return (*((type *) s1)<*((type *) s2) ? -1 : 1 );
> }
>}

This is an interesting example. Please do three things:

1. Send me the code for template class QList (Qt has a template called
QValueList, but I didn't find one called QList).
2. What is the type of QCollection::Item?
3. What is the '___' in TSortedList<____> that caused this error? Or
are you saying it always generates an error independent of any
TSortedList<___> usage?? If the latter, better send me the whole
TSortedList template.


>This code generates an error saying operators == and < for class type 
>don't exist. Changing type to T or anything else doesn't help. Oddly, 
>almost identical code compiles elsewhere plus it worked on Linux last 
>time I compiled it (quite some time ago).
>
>That's MSVC 6 SP6.
>
>> I am interested in this error, but I'm only interested in it if it is
>> real, that is, if you can get MS VC++ to actually generate an 
>> incorrect error message. I'd suggest rewriting the example, trying 
>> it, and seeing if you can actually create a case that fails with MS
>> VC++.
>
>Hardly important unless Visual Studio .NET has the same problem. MS 
>no longer consider MSVC6 a primary support product.

Not true. Many companies will continue to use MS VC++ 6 for years to
come. I know a company that's still using MS VC++ version 1.62 for some
of their embedded systems programming.


>I have tried changing it around, but in the end I have bigger 
>priorties. In the end, if it causes me too many problems, I'll switch 
>to GCC.
>
>> >Lastly, I know you think macros are evil, but I have always used
them 
>> >to implement functionality which C++ should have. The biggest of 
>> >these is try...finally without which multithreaded programming is a 
>> >severe pain. Of course, you don't call the macro 'finally' - I've 
>> >used TERRH_TRYF...TERRH_FINALLY...TERRH_ENDTRYF to make it very 
>> >clear. In this context, macros are very useful and I think to be 
>> >encouraged so long as the macro: (a) says where it's defined and (b)
>> >all caps.
>> 
>> Sorry, but I would strongly recommend against the above. That's just
>> not the C++ way of doing things. To be honest, it makes you sound 
>> like you're a Java programmer who has learned C++ syntax but still 
>> hasn't mastered C++ idioms.
>
>God no, I hated Java. I'm actually an assembler programmer originally 
>which of course uses loads of macros all the time.
>
>How, might I ask, would you suggest you implement try...finally 
>without macros in "the C++ way of doing things"? 

Use the C++ idiom that "destruction is resource reclamation."


>I am assuming you 
>will surely agree multithreaded programming is not fun without 
>try...finally (if you don't, I want reasons, it'll be interesting to 
>see what you'd come up with).

Constructor/destructor <==> resource acquisition/reclamation.


>> >Anyway, hope you don't mind these comments.
>> 
>> Not at all.
>
>I used to read the reports of the ANSI committee meetings regarding 
>C++ as it was still being formalised and it always struck me as being
>an awful hodge-podge. The more I learn about it, the more I realise I 
>was correct!

Have you ever been on *any* ANSI or ISO standardization committee? If
not, it must be easy for you to sit there with zero experience and throw
insults at the hard work of others who have selflessly sacrificed their
time and money to do something big.


>I never had much experience with it, but Objective C 
>always seemed a lot cleaner.

If ObjC is so much better, why is it so unpopular?


>Unfortunately, today is now and the 
>world we live in uses C++.
>
>I take it you come from a Smalltalk background? 

Not at all. My C++ "smells like" C++, not like Smalltalk or assembler
or anything else. Similarly my C code smells like C code, and it uses C
idioms, etc., and my Java smells like Java, etc. I am language neutral,
e.g., I've been a member of both the ANSI C++ and ANSI Smalltalk
committees.


>I know this will 
>sound approaching blasphemous - and I don't mean at all to be 
>offensive, but merely to garner an opinion - but I have always 
>considered OO to be a good way of organising maintainable source but 
>really crap for designing code.

Another really big error. OO is primarily a design approach. The
concept of "OO programming" is very close to a misnomer, since OO
programming cannot stand on its own - it needs OO *design*.

Marshall




From: Niall Douglas <xxx@xxxxxxx.xxx>
To: "Marshall Cline" <xxxxx@xxxxxxxxx.xxx>
Subject: RE: Comments on your C++ FAQ
Date: Mon, 29 Jul 2002 15:06:30 +0200

On 27 Jul 2002 at 20:26, Marshall Cline wrote:

> >I had thought one was meant to encourage reusability in every line of
> > C++ you wrote? Where possible obviously.
> 
> Nope. In fact, that approach normally causes projects to fail.
> 
> Instead of encouraging reusability in every line of C++ code (where
> possible), the goal is to be a responsible professional who sometimes
> invests effort in a future pay-back (AKA reuse), and sometimes does
> not. A responsible professional does not invest in a future pay-back
> when the ROI isn't right (return-on-investment) or when doing so would
> add unacceptable risk or cost or time to the current project. 
> Balancing the future with the present is a very subtle task, but that
> is the task of a responsible professional.

I think we're actually agreeing here, but it's a case of opinion 
dictating emphasis. I tend to always overengineer because I believe 
15% extra development time halves your debugging time and quarters 
augmentation time and many projects suffer from fuzzy definition, so 
this approach makes sense (and I have used it successfully many 
times).

However, I also completely agree with your statement.

> >To that end, and I will admit I have only worked on small C++ 
> >projects (less than 5k lines), it has always appeared to me not
> >hugely difficult to structure your code appropriately to ensure
> >maximum reusability.
> 
> Everyone, including you, is to some extent a prisoner of their past.
> Your experience with very small projects limits your ability to
> understand how things work in the real world with large projects. I
> don't fault you for your lack of experience with large projects, but
> in a similar way you must not presume that your small-system
> experiences are applicable or relevant to the way things happen in
> that other world.

Remember I have worked on >30k C projects and a number of obscenely 
large all-assembler projects and I've been doing all this 
successfully for a decade now. Now I don't claim for a moment to know 
much about C++, but in the end code is code and while organising C++ 
like assembler might not be a good idea it's still better than no 
organisation at all. Hence, by analogy, I believe my considerable 
prior experience does give me an advantage overall, although I will 
freely admit some things will have to be unlearned for C++. 
Unfortunately, that is something that only comes with experience and 
time - although talking with people like yourself, examining existing 
code and reading resources like your C++ FAQ accelerate the process.

> >You can help it of course by trying to ensure subclasses never undo
> >or redo operations the base class did.
> 
> No, not at all.
> 
> I don't think you understand what the slicing problem (AKA chopped
> copies) is all about. I suggest you read the FAQ on that one.

No I didn't, but I do now. It's strongly related to below.

> >I'm afraid I don't understand covariant parameter on the assignment
> >operator.
> 
> I don't think the FAQ covers this one. I'll try to give a very brief
> overview. Suppose someone inherits D from B, and passes a D to a
> function expecting a B& (reference-to-B). If that function assigns to
> the B&, then only the B part of the D object is changed, and the
> object often goes into a nonsensical state. It's kind of like genetic
> engineering, where biologists scoop out the chromosomes from one
> animal, and replace them with the chromosomes of another. It's real
> easy to get garbage when you do that.

I was under the /strong/ impression references were treated 
syntaxically identical to their non-referenced version. Hence, you 
can't pass anything other than B to a function expecting a B&. I 
distinctly remember twigging I should no longer use C style pointers 
except when I explicitly expect D* and all ptrs to subclasses thereof 
(and where I think in the future I may pass a subclass).

> >If what you say about inheriting is bad, then I would say my code is
> >very foul. I have huge depths of subclassing as a normal part of
> >writing my code. 
> 
> Ouch, that's certainly a very dubious design style. It's a typical
> hacker's style, and it comes from the Smalltalk world, but it's
> generally inappropriate for C++ or Java or any other statically typed
> OO language.

Can you point me to resources explaining why this is bad and not just 
a question of individual style? I would have thought it /better/ for 
statically typed languages because the compiler is given more 
knowledge with which to optimise.

> >It's not just me - I picked up the idea from Qt 
> >(http://www.trolltech.com/). I've basically learned C++ by copying
> >their example.
> 
> If you're building an application framework (especially if you're
> building a GUI framework), you might have chosen a reasonable piece of
> software to learn from. If you're simply *using* a framework to build
> some other app, you've chosen poorly.

I'd like to think I accumulate ideas from whatever existing source I 
look at. It is after all how I originally taught myself how to 
program. Again, I'd like to know precisely why this style would be a 
poor choice for some other app.

> >No all he/she did wrong was assume no one would ever want to subclass
> > their class. Not only is that arrogant and assumes themselves
> >infallible, it seems to me bad practice and against the whole idea of
> > software reusability.
> 
> You seem to have a wrong notion of how reusability and inheritance are
> supposed to mix. Inheritance is not "for" reuse. One does *not*
> inherit from something to reuse that thing.

I really don't get you here. I reread the appropriate sections in 
your FAQ and I *still* don't get this. I can't escape thinking that 
what I and you mean by "reusing code" is not the same thing - for me, 
whenever you inherit something you inherit its structure (API) and 
its code - that, to me, is reusing already written and tested code 
which is a good thing. Hence inheritance = code reuse.

> >> Sorry, but this is a very poor solution. It certainly works in a
> >> few
> >> cases, but it violates the first premise of pluggable software. The
> >> three solutions described in the FAQ are much better: they handle
> >> all
> >> the trivial cases that yours handles, and in addition they handle
> >> the
> >> more sophisticated "plugability" cases.
> >
> >I'll review your FAQ's suggestions again. Maybe I missed something,
> >but they all seemed to have fairly substantial caveats.
> 
> They all do. But at least they all solve the pluggability problem,
> which is typically the core reason for using this sort of syntax /
> technique.

Pluggability = ability to link in or out a "module" of code easily 
yes?

If so, static class constructs worry me because I can't guarantee 
their order before main(). My approach solves this, and hence answers 
the main caveat your FAQ stated.

> This is an interesting example. Please do three things:
> 
> 1. Send me the code for template class QList (Qt has a template called
> QValueList, but I didn't find one called QList).

Yeah Trolltech renamed QList to QPtrList in Qt 3.0.

> 2. What is the type
> of QCollection::Item?

MSVC thinks it's a void *. QGList I included should say more on this.

> 3. What is the '___' in TSortedList<____> that
> caused this error? Or are you saying it always generates an error
> independent of any TSortedList<___> usage?? If the latter, better
> send me the whole TSortedList template.

It's the latter. I've commented out the original code in what I've 
sent to get it to compile. If you compare it to QSortedList.h, the 
two are almost identical (which is intentional) but QSortedList.h 
compiles perfectly whereas mine stops complaining with:

> d:\atoms\tclient\include\tsortedlist.h(57) : error C2678: binary '=='
> : no operator defined which takes a left-hand operand of type 'class
> type' (or there is no acceptable conversion)
> d:\atoms\tclient\include\tsortedlist.h(56) : while compiling
> class-template member function 'int __thiscall
> TSortedList<class type>::compareItems(void *,void *)'
> d:\atoms\tclient\include\tsortedlist.h(58) : error C2678: binary '<' :
> no operator defined which takes a left-hand operand of type 'class
> type' (or there is no acceptable conversion)
> d:\atoms\tclient\include\tsortedlist.h(56) : while compiling
> class-template member function 'int __thiscall
> TSortedList<class type>::compareItems(void *,void *)'

This is at template definition, not at template use.

> >Hardly important unless Visual Studio .NET has the same problem. MS
> >no longer consider MSVC6 a primary support product.
> 
> Not true. Many companies will continue to use MS VC++ 6 for years to
> come. I know a company that's still using MS VC++ version 1.62 for
> some of their embedded systems programming.

Companies may continue to use a product, but it's not in Microsoft's 
commercial interests to encourage them. Based on historical 
precident, it is extremely clear Microsoft take a bug much more 
seriously if it's in the current top-of-the-line product. Bugs in 
older products are more likely to be fixed if (a) new product's fix 
can be retro-engineered easily and (b) if their reputation would 
suffer if they didn't. I'm guessing (a) won't apply given the likely 
substantial redesign to accommodate C#.

> >How, might I ask, would you suggest you implement try...finally
> >without macros in "the C++ way of doing things"? 
> 
> Use the C++ idiom that "destruction is resource reclamation."
> 
> >I am assuming you 
> >will surely agree multithreaded programming is not fun without 
> >try...finally (if you don't, I want reasons, it'll be interesting to
> >see what you'd come up with).
> 
> Constructor/destructor <==> resource acquisition/reclamation.

That is a cracking idea I am kicking myself for not having thought of 
earlier. I was already concerned about the overhead of throwing an 
exception every try...finally, but your approach is far simpler and 
more efficient. It'll require quite a lot of code refitting, but I 
think it's worth it.

Thank you!

> >I used to read the reports of the ANSI committee meetings regarding
> >C++ as it was still being formalised and it always struck me as being
> >an awful hodge-podge. The more I learn about it, the more I realise I
> > was correct!
> 
> Have you ever been on *any* ANSI or ISO standardization committee? If
> not, it must be easy for you to sit there with zero experience and
> throw insults at the hard work of others who have selflessly
> sacrificed their time and money to do something big.

I apologise if you interpreted my words as throwing insults for they 
were not intended as such. I have the utmost respect and admiration 
for any standardisation committee (with possible exception of the 
POSIX threads committee, their poor design really screws C++ stack 
unwinding which is unforgiveable given how recently it was designed).

However, this does not changed my statement that C++ is an awful 
hodge-podge. I am not saying everyone involved in standardisation 
didn't move heaven and earth to make things as good as they could, 
but with an albatross like keeping existing code compatibility with 
AT&T C++ and C there was only so much that could be done. I remember 
the passionate debates about what compromises to strike well.

Put it this way: when you try something which seems logical in C it 
generally works the way you think it should. The same in C++ is much 
less true - I keep finding myself running into limitations which have 
no good reason. For example, the concept of destination type seems to 
have no effect in C++ eg;

TQString foo;
foo="Hello world";

Now TQString is a subclass of QString, and both have const char * 
ctors. The compiler will refuse to compile the above code because 
there are two methods of resolving it. Now, to me, that seems stupid 
because quite clearly the destination type is TQString and the 
shortest route to that is to use the TQString const char * ctor ie; I 
clearly am inferring to use the shortest route. The same sort of 
thing applies to overloading functions - you cannot overload based on 
return type, something I find particularly annoying.

> >I never had much experience with it, but Objective C 
> >always seemed a lot cleaner.
> 
> If ObjC is so much better, why is it so unpopular?

Lots of reasons. If I remember correctly, there were many problems 
with the run-time library on different platforms. There were issues 
regarding Next and Apple and all that. Of course, as well, there were 
culture issues - programmer inclinations. Also, there was good 
competition between many C++ vendors which brought C++ tools to a 
decent quality pretty quickly.

Computer history is strewn with cases of an inferior product 
destroying a superior product. It's hardly unique.

> >I take it you come from a Smalltalk background? 
> 
> Not at all. My C++ "smells like" C++, not like Smalltalk or assembler
> or anything else. Similarly my C code smells like C code, and it uses
> C idioms, etc., and my Java smells like Java, etc. I am language
> neutral, e.g., I've been a member of both the ANSI C++ and ANSI
> Smalltalk committees.

In which case you are a better programmer than I. I essentially 
program the same in any language using an internal methodology and my 
measure of my liking a language is how little it distorts what I 
actually want to do (hence my strong dislike of Java and 
VisualBasic). Nothing I program is what other people call a typical 
style of that language. You may think that irresponsible and arrogant 
of me, but I know it is an innate quality of mine - it's the same 
when I learn human languages (I still retain my own speech formation 
and pronounciation irrespective).

Hence, I am more of a functional programmer than anything else. It is 
my dream to some day design an imperitive/functional hybrid language 
which would perfectly reflect how I like to program.

> >I know this will 
> >sound approaching blasphemous - and I don't mean at all to be 
> >offensive, but merely to garner an opinion - but I have always 
> >considered OO to be a good way of organising maintainable source but
> >really crap for designing code.
> 
> Another really big error. OO is primarily a design approach. The
> concept of "OO programming" is very close to a misnomer, since OO
> programming cannot stand on its own - it needs OO *design*.

No, I must disagree with you there: design is independent of 
language. I have never agreed with OO design as my university 
lecturers found out - I quite simply think it's wrong. Computers 
don't work naturally with objects - it's an ill-fit.

What computers do do is work with data. If you base your design 
entirely around data, you produce far superior programs. Now I will 
agree OO is good for organising source for improved maintainability, 
but as a design approach I think it lacking.

An example: take your typical novice with OO. Tell them the rules and 
look at what they design. Invariably, pure OO as designed against the 
rules is as efficient as a one legged dog. In fact, in my opinion, OO 
experience is actually learning when to break pure OO and experienced 
OO advocates do not realise that they so automatically break the pure 
application of what they advocate.

A practical example: at university, we had to design a program to 
sort post office regional codes. The typical class effort, for which 
they received top marks, sorted the list in about ten to twenty 
seconds. My effort did it so quickly there wasn't a delay in the 
command prompt returing - and may I add, I received a bare pass mark 
because I adopted a data-centric solution and not an OO one. Now I 
couldn't fault that (the story of my entire degree), but it painfully 
reminded me of how OO is fundamentally incorrect for computers - good 
for humans, but not computers.

Anyway, I've enclosed the files you requested plus others I thought 
would aid you. TSortedList.cpp should almost stand alone against Qt 
3.0 - at least, anything not defined should have an obvious 
equivalent.

Cheers,
Niall




From: "Marshall Cline" <xxxxx@xxxxxxxxx.xxx>
To: "'Niall Douglas'" <xxx@xxxxxxx.xxx>
Subject: RE: Comments on your C++ FAQ
Date: Sun, 28 Jul 2002 22:31:36 -0500

Niall Douglas wrote:
>On 27 Jul 2002 at 20:26, Marshall Cline wrote:
>
>>>I had thought one was meant to encourage reusability in every line of
>>> C++ you wrote? Where possible obviously.
>>
>>Nope. In fact, that approach normally causes projects to fail.
>>
>>Instead of encouraging reusability in every line of C++ code (where 
>>possible), the goal is to be a responsible professional who sometimes 
>>invests effort in a future pay-back (AKA reuse), and sometimes does 
>>not. A responsible professional does not invest in a future pay-back 
>>when the ROI isn't right (return-on-investment) or when doing so would
>>add unacceptable risk or cost or time to the current project. 
>>Balancing the future with the present is a very subtle task, but that 
>>is the task of a responsible professional.
>
>I think we're actually agreeing here, but it's a case of opinion 
>dictating emphasis. I tend to always overengineer because I believe 
>15% extra development time halves your debugging time and quarters 
>augmentation time and many projects suffer from fuzzy definition, so 
>this approach makes sense (and I have used it successfully many 
>times).
>
>However, I also completely agree with your statement.

Sounds like the difference may be one of degrees, as you said.

I spoke last time about being a prisoner of our pasts. My past includes
acting as "senior technology consultant" to IBM throughout North
America, which meant advising on product strategy, mentoring, and (most
relevant to this situation) performing internal audits. The audits
included a number of important engagements with IBM's clients, and
required me to perform assessments of people and technology. During
these audits and assessments, I saw a lot of large projects that failed
because of overengineering. Many of the technologists on these sick or
dead projects had a similar perspective to what you articulated above.
Their basic approach was often that overengineering is better than
underengineering, that it's cheaper in the long run, and perhaps cheaper
in the short run, so let's overengineer just in case.

As a result of seeing in excess of one hundred million dollars worth of
effort (and numerous careers) washed down the drain, I tend to make sure
there is a realistic ROI before adding any effort that has a
future-payback.


>>>To that end, and I will admit I have only worked on small C++
>>>projects (less than 5k lines), it has always appeared to me not
>>>hugely difficult to structure your code appropriately to ensure
>>>maximum reusability.
>>
>>Everyone, including you, is to some extent a prisoner of their past. 
>>Your experience with very small projects limits your ability to 
>>understand how things work in the real world with large projects. I 
>>don't fault you for your lack of experience with large projects, but 
>>in a similar way you must not presume that your small-system 
>>experiences are applicable or relevant to the way things happen in 
>>that other world.
>
>Remember I have worked on >30k C projects and a number of obscenely 
>large all-assembler projects and I've been doing all this 
>successfully for a decade now. 

Okay, I didn't realize that earlier. That makes some sense now.


>Now I don't claim for a moment to know 
>much about C++, but in the end code is code and while organising C++ 
>like assembler might not be a good idea it's still better than no 
>organisation at all. Hence, by analogy, I believe my considerable 
>prior experience does give me an advantage overall, 

Certainly true.

>although I will 
>freely admit some things will have to be unlearned for C++. 

Also true.

>Unfortunately, that is something that only comes with experience and 
>time - although talking with people like yourself, examining existing 
>code and reading resources like your C++ FAQ accelerate the process.
>
>>>You can help it of course by trying to ensure subclasses never undo 
>>>or redo operations the base class did.
>>
>>No, not at all.
>>
>>I don't think you understand what the slicing problem (AKA chopped
>>copies) is all about. I suggest you read the FAQ on that one.
>
>No I didn't, but I do now. It's strongly related to below.

Agreed.


>>>I'm afraid I don't understand covariant parameter on the assignment 
>>>operator.
>>
>>I don't think the FAQ covers this one. I'll try to give a very brief 
>>overview. Suppose someone inherits D from B, and passes a D to a 
>>function expecting a B& (reference-to-B). If that function assigns to
>>the B&, then only the B part of the D object is changed, and the 
>>object often goes into a nonsensical state. It's kind of like genetic
>>engineering, where biologists scoop out the chromosomes from one 
>>animal, and replace them with the chromosomes of another. It's real 
>>easy to get garbage when you do that.
>
>I was under the /strong/ impression references were treated 
>syntaxically identical to their non-referenced version. Hence, you 
>can't pass anything other than B to a function expecting a B&. I 
>distinctly remember twigging I should no longer use C style pointers 
>except when I explicitly expect D* and all ptrs to subclasses thereof 
>(and where I think in the future I may pass a subclass).

Hopefully this new understanding about references will help your coding.
In any case, I agree that references should be used more often than
pointers, but for a different reason. The reason is that references are
restricted compared to pointers, and that restriction is (often) a good
thing. A pointer can be NULL, but a reference cannot (legally) be NULL,
so if you have a function that must not be passed NULL, the easy way to
make that explicit is for the function's parameter to be a reference
rather than a pointer. That way there's one less condition to test for
and one less 'if' at the beginning of your function.


>>>If what you say about inheriting is bad, then I would say my code is 
>>>very foul. I have huge depths of subclassing as a normal part of 
>>>writing my code.
>>
>>Ouch, that's certainly a very dubious design style. It's a typical 
>>hacker's style, and it comes from the Smalltalk world, but it's 
>>generally inappropriate for C++ or Java or any other statically typed 
>>OO language.
>
>Can you point me to resources explaining why this is bad and not just 
>a question of individual style? 

Sure no problem. Start with our book ("C++ FAQs", Addison Wesley), then
go to Scott Meyer's books ("Effective C++" and "More Effective C++",
also Addison Wesley), and probably most any other book that deals with
design/programming style in C++.

>I would have thought it /better/ for 
>statically typed languages because the compiler is given more 
>knowledge with which to optimise.

Nope, it's a very Smalltalk-ish style, and it causes lots of problems in
a statically typed OO language since today's statically typed OO
languages (C++, Java, Eiffel, etc.) equate inheritance with subtyping.
In any language that equates inheritance with subtyping, using
inheritance as a reuse mechanism, as opposed to using inheritance
strictly for subtyping purposes, ultimately causes lots of design and
extensibility problems. It can even effect performance.


>>>It's not just me - I picked up the idea from Qt
>>>(http://www.trolltech.com/). I've basically learned C++ by copying
>>>their example.
>>
>>If you're building an application framework (especially if you're 
>>building a GUI framework), you might have chosen a reasonable piece of
>>software to learn from. If you're simply *using* a framework to build
>>some other app, you've chosen poorly.
>
>I'd like to think I accumulate ideas from whatever existing source I 
>look at. It is after all how I originally taught myself how to 
>program. Again, I'd like to know precisely why this style would be a 
>poor choice for some other app.

Mostly because it creates all sorts of problems for users. Take, for
example, your TSortedList class. You have removed the append() and
prepend() methods because you can't implement them properly in your
class. Nonetheless someone might easily pass an object of your derived
class via pointer or reference to its base class, and within that
function the methods you tried to remove are suddenly available again,
only this time with potentially disastrous results. Take, for example,
this function:

void f(QList<Foo>& x)
{
x.prepend(...); // change '...' to some Foo object
x.append(...); // change '...' to some Foo object
}

Now suppose someone passes a TSortedList object to this function:

void g()
{
TSortedList<Foo> x;
f(x);
...what happens here??
}

In the '...what happens here??' part, anything you do to the TSortedList
is likely to cause problems since the list might not be sorted. E.g.,
if f() adds Foo objects to 'x' in some order other than the sorted
order, then the '...what happens here??' part is likely to cause serious
problems.

You can't blame this problem on references, since the same exact thing
would happen if you changed pass-by-reference to pass-by-pointer.

You can't blame this problem on the C++ compiler, because it can't
possibly detect one of these errors, particularly when the functions f()
and g() were part of two different .cpp files ("compilation units") that
were compiled on different days of the week.

You can't blame this problem on the author of g(), because he believed
the contract of TSortedList. In particular, he believed a TSortedList
was a kind-of a QList. After all that is the meaning of subtyping, and
subtyping is equated in C++ with inheritance. The author of g() simply
believed what you said in this line: 'class TSortedList : public QList',
and you can't blame him for believing what you said.

You can't blame this problem on the author of f(), because he believed
the contract of QList. In particular, he believed he can append()
and/or prepend() values in any order onto any QList. Besides, he wrote
and compiled his code long before you even thought of deriving
TSortedList, and by the rules of extensibility (e.g., see the sections
on Inheritance in the C++ FAQ, or any similar chapters in any book on
the subject), he is not required to predict the future - he is supposed
to be able to write code based on today's realities, and have tomorrow's
subclasses obey today's realities. That is the notion of is-a, and is
codified in many places, including the C++ FAQ, Liskov's
Substitutability Principle ("LSP"), and many other places.

So who is at fault? Ans: the author of TSortedList. Why is the author
of TSortedList at fault? Because of false advertising: he said
TSortedList was a kind-of a QList (or, using precise terminology, that
TSortedList was substitutable for QList), but in the end he violated
that substitutability by removing methods that were promised by QList.

To work a more down-to-earth example, suppose all Plumbers have a
fixPipes() method, and further suppose I claim to be a kind-of Plumber
but I don't have a fixPipes() method. My claim is false: I am not a
kind-of Plumber, but am instead a fraud. Any company that contracted
for my services under my false advertising would be in the right by
claiming I have defrauded them, after all, I claimed to be something I
am not. Similarly a used-car salesman that sells a car which lacks
brakes and/or an engine is in the wrong. Society wouldn't blame the
car, the engine, or the driver; they would blame the salesman for
falsely representing that a particular kind of car is substitutable for
the generally agreed-upon definition of "car." The good news is that in
OO, we don't have to rely on "generally agreed upon definitions."
Instead we look at the base class and that *precisely* defines what is
or is not a "car" (or in this case, a QList).


>>>No all he/she did wrong was assume no one would ever want to subclass
>>>their class. Not only is that arrogant and assumes themselves 
>>>infallible, it seems to me bad practice and against the whole idea of
>>>software reusability.
>>
>>You seem to have a wrong notion of how reusability and inheritance are
>>supposed to mix. Inheritance is not "for" reuse. One does *not* 
>>inherit from something to reuse that thing.
>
>I really don't get you here. I reread the appropriate sections in 
>your FAQ and I *still* don't get this. I can't escape thinking that 
>what I and you mean by "reusing code" is not the same thing 

Agreed: we are saying totally different things for reuse. What I'm
saying is that inheritance is NOT "for" reuse. Inheritance is for
subtyping - for substitutability. Ultimately inheritance is so my code
CAN *BE* reused; not so it can reuse. Has-a (AKA composition AKA
aggregation) is for reuse. Is-a (AKA inheritance AKA substitutability)
is for BEING REUSED.

Put it this way: you inherit from "it" to *be* what it *is*, not simply
to have what it has. If you simply want to have what it has, use has-a
(AKA aggregation AKA composition).

> - for me, 
>whenever you inherit something you inherit its structure (API) and 
>its code - that, to me, is reusing already written and tested code 
>which is a good thing. Hence inheritance = code reuse.

We are totally different here. And unfortunately the extended
experience of a vast majority of C++ programmers has proven your
approach is very short-sighted.

(BTW I will quickly add that your approach is perfectly fine in a very
small project, since in very small projects you can control the damage
of "improper" or "bad" inheritance. Some of my colleagues won't agree
and will say your approach is *always* wrong, and in a sense I would
agree. But from a practical basis, your approach doesn't really cost
too much in the way of time, money, or risk with a small enough project.
If you use your approach on a big project, however, everyone seems to
agree, and everyone's experience seems to prove, that your approach is
very dangerous and expensive.)


>
>>>> Sorry, but this is a very poor solution. It certainly works in a 
>>>> few cases, but it violates the first premise of pluggable software.
>>>> The three solutions described in the FAQ are much better: they 
>>>> handle all
>>>> the trivial cases that yours handles, and in addition they handle
>>>> the
>>>> more sophisticated "plugability" cases.
>>>
>>>I'll review your FAQ's suggestions again. Maybe I missed something, 
>>>but they all seemed to have fairly substantial caveats.
>>
>>They all do. But at least they all solve the pluggability problem, 
>>which is typically the core reason for using this sort of syntax / 
>>technique.
>
>Pluggability = ability to link in or out a "module" of code easily 
>yes?

Yes, sort of. The idea is to add a new feature without changing *any*
existing code -- to add something new without changing any existing .cpp
file, .h file, or any other chunk of code anywhere. Think Netscape
plug-ins or Internet Explorer plug-ins and you'll see what I mean:
people don't need to get a new version of Netscape / IE when some
company creates a new plug-in. People can plug the new plug-in into
their old browser without ANY change to ANY line of code within the
browser itself.


>If so, static class constructs worry me because I can't guarantee 
>their order before main(). 

That's why we use things like construct-on-first-use: so we don't rely
on their order before main().


>My approach solves this, and hence answers 
>the main caveat your FAQ stated.

Yes, but the cost of that benefit is to sacrifice plugability.


>>This is an interesting example. Please do three things:
>>
>>1. Send me the code for template class QList (Qt has a template called
>>QValueList, but I didn't find one called QList).
>
>Yeah Trolltech renamed QList to QPtrList in Qt 3.0.
>
>>2. What is the type
>>of QCollection::Item?
>
>MSVC thinks it's a void *. QGList I included should say more on this.
>
>>3. What is the '___' in TSortedList<____> that
>>caused this error? Or are you saying it always generates an error 
>>independent of any TSortedList<___> usage?? If the latter, better 
>>send me the whole TSortedList template.
>
>It's the latter. I've commented out the original code in what I've 
>sent to get it to compile. If you compare it to QSortedList.h, the 
>two are almost identical (which is intentional) but QSortedList.h 
>compiles perfectly whereas mine stops complaining with:
>
>>d:\atoms\tclient\include\tsortedlist.h(57) : error C2678: binary '=='
>>: no operator defined which takes a left-hand operand of type 'class 
>>type' (or there is no acceptable conversion)
>> d:\atoms\tclient\include\tsortedlist.h(56) : while compiling
>> class-template member function 'int __thiscall
>> TSortedList<class type>::compareItems(void *,void *)'
>>d:\atoms\tclient\include\tsortedlist.h(58) : error C2678: binary '<' :
>>no operator defined which takes a left-hand operand of type 'class 
>>type' (or there is no acceptable conversion)
>> d:\atoms\tclient\include\tsortedlist.h(56) : while compiling
>> class-template member function 'int __thiscall
>> TSortedList<class type>::compareItems(void *,void *)'
>
>This is at template definition, not at template use.

Very, very strange. Are you sure the code was compiled exactly as you
sent it to me? I.e., the definition for compareItems() was within the
class body as you had it in the .h file? I don't have MS VC++ 6
installed right now or I'd check it myself, but on the surface this
seems to be a totally bizarre error message since 'type' is the template
parameter, and therefore the compiler could never check to see if it had
an operator == or < or anything else (including simple assignment!).

In any case, your solution will, unfortunately, cause serious problems
if you use a TSortedList<Foo> when Foo doesn't inherit from
TSortedListItem. If you use TSortedList<Foo> when Foo doesn't inherit
from TSortedListItem, or if Foo multiply inherits from something along
with TSortedListItem, then it will end up calling a rather random
virtual function and it will end up doing rather random things. I would
consider this to be a very fragile piece of code, at best. The compiler
shouldn't stop you from doing what you want it to do.

**DING** I just found your bug. In TSortedList.cpp, all your methods
are listed like this:

int TSortedList<class type>::find( const type *d )
{
...
}

But instead they should be listed like this:

template<class type>
int TSortedList::find( const type *d )
{
...
}

Your syntax ("TSortedList<class type>::") explains everything, including
the bizarre use of 'class type' within the error messages. What
happened is that the compiler saw you using a TSortedList<class type>,
and it therefore tried to compile all the virtual methods within
TSortedList<class type>. When it saw that 'type' really isn't a genuine
class type, it complained (eventually) that the class called 'type'
doesn't have an == or < operator.

When you fix this problem, you will end up with additional problems,
mainly because you have moved template code into a .cpp file. The C++
FAQ covers this issue; suggest you read that for details. (It's
probably in the section on templates and/or containers.)


>>>Hardly important unless Visual Studio .NET has the same problem. MS 
>>>no longer consider MSVC6 a primary support product.
>>
>>Not true. Many companies will continue to use MS VC++ 6 for years to 
>>come. I know a company that's still using MS VC++ version 1.62 for 
>>some of their embedded systems programming.
>
>Companies may continue to use a product, but it's not in Microsoft's 
>commercial interests to encourage them. Based on historical 
>precident, it is extremely clear Microsoft take a bug much more 
>seriously if it's in the current top-of-the-line product. Bugs in 
>older products are more likely to be fixed if (a) new product's fix 
>can be retro-engineered easily and (b) if their reputation would 
>suffer if they didn't. I'm guessing (a) won't apply given the likely 
>substantial redesign to accommodate C#.

My point is that the issue is (was) important to thousands and thousands
and thousands of C++ programmers. Yes Microsoft might choose to ignore
it, but that doesn't mean the issue is no longer relevant or important.


>>>How, might I ask, would you suggest you implement try...finally 
>>>without macros in "the C++ way of doing things"?
>>
>>Use the C++ idiom that "destruction is resource reclamation."
>>
>>>I am assuming you
>>>will surely agree multithreaded programming is not fun without 
>>>try...finally (if you don't, I want reasons, it'll be interesting to
>>>see what you'd come up with).
>>
>>Constructor/destructor <==> resource acquisition/reclamation.
>
>That is a cracking idea I am kicking myself for not having thought of 
>earlier. 

I'm glad I could help.

>I was already concerned about the overhead of throwing an 
>exception every try...finally, but your approach is far simpler and 
>more efficient. It'll require quite a lot of code refitting, but I 
>think it's worth it.
>
>Thank you!

No problem. BTW I consider this an idiom of C++. Part of being
competent in using a language is knowing the syntax and semantics, but
another critical part is knowing the idioms of the language. You're an
expert (I assume) in certain varieties of assembler, and perhaps also in
C. As a competent C programmer, you know the C idioms, such as

while (*dest++ = *src++)
;

This, of course, is the idiom that copies an array of things pointed to
by 'src' into an array pointed to by 'dest', and it stops copying after
it copies the item whose value is zero. If the arrays are arrays of
'char', this is equivalent to strcpy(), since it copies everything
including the terminating '\0'. Obviously other types are similar.

Other idioms in C abound, such as Duff's device:

while (n >= 0) {
switch (n) {
default: xyzzy;
case 7: xyzzy;
case 6: xyzzy;
case 5: xyzzy;
case 4: xyzzy;
case 3: xyzzy;
case 2: xyzzy;
case 1: xyzzy;
}
n -= 8;
}

If you replace 'xyzzy' with some piece of code, this applies that piece
of code exactly 'n' times, but it is much faster than the equivalent:

while (n-- > 0) {
xyzzy;
}

Since the latter executes 'n' decrements, 'n' comparisons, and 'n'
conditional jumps, whereas Duff's device executes only 1/8'th as many
decrements, comparisons, or conditional jumps. Of course the key is
that there is no 'break' statement after each 'case' -- each case "falls
through" to the next case. The other key, obviously, is that the cases
are listed in backwards order.

The point, of course, is that this is another idiom of C, and competent
C programmers know these sorts of things. As you become better and
better at C++, you will learn the idioms of C++, and this is one of
them.


>>>I used to read the reports of the ANSI committee meetings regarding
>>>C++ as it was still being formalised and it always struck me as being
>>>an awful hodge-podge. The more I learn about it, the more I realise I
>>>was correct!
>>
>>Have you ever been on *any* ANSI or ISO standardization committee? If
>>not, it must be easy for you to sit there with zero experience and 
>>throw insults at the hard work of others who have selflessly 
>>sacrificed their time and money to do something big.
>
>I apologise if you interpreted my words as throwing insults for they 
>were not intended as such. 

Apology accepted. I was a little ticked off, but mainly because I get
frustrated when people who really don't know what it's like to steer a
very popular programming language assume they could do a better job.
I'm better now :-)

>I have the utmost respect and admiration 
>for any standardisation committee (with possible exception of the 
>POSIX threads committee, their poor design really screws C++ stack 
>unwinding which is unforgiveable given how recently it was designed).

Not knowing any better, I'd guess they were dealing with very subtle
constraints of existing code or existing practice. Most people on those
sorts of committees are competent, plus the entire world (literally) has
a chance to comment on the spec before it gets finalized, so if there
were gaping holes *that* *could* *be* *fixed* (e.g., without breaking
oodles and oodles of existing code), I'm sure *someone* *somewhere* in
the world would have pointed it out.

Like I said, I don't know the details of this particular situation, but
I would guess that they were fully aware of the problem, that they
investigated all the alternatives, and that they chose the "least bad"
of the alternatives.

>
>However, this does not changed my statement that C++ is an awful 
>hodge-podge. I am not saying everyone involved in standardisation 
>didn't move heaven and earth to make things as good as they could, 
>but with an albatross like keeping existing code compatibility with 
>AT&T C++ and C there was only so much that could be done. I remember 
>the passionate debates about what compromises to strike well.

Yes, existing code puts everyone in a very difficult position, and often
causes compromises. But that's the nature of the beast. The cost of
breaking existing code is much, much greater than canonizing it. Yes
there are compromises with purity, but without those compromises, no one
would use standard C++. After all, there is no law that requires
vendors to implement compilers or libraries that conform to the
standard, so the only way for things to work is for everyone (including
the standardization committees, the compiler vendors, the library
vendors, etc.) to do everything possible to avoid forcing everyone to
rewrite their code. If even 10% of the world's C++ code had to get
rewritten, I predict the C++ standard would get rejected by large
companies and therefore those large companies would ask their vendors to
support the old-fashioned, non-standard syntax/semantics, and all the
good that would have come as a result of having a standard would be for
naught.

>
>Put it this way: when you try something which seems logical in C it 
>generally works the way you think it should. 

Really? No less a light as Dennis Ritchie bemoans the precedence of
some of the operators, and certainly the rather bizarre use of 'static'
has caused more than one C programmer to wonder what's going on. Plus
the issue of order of evaluation, or aliasing, or any number of other
things has caused lots of consternation.

But I guess I agree to this extent: C++ is larger than C, and as such
C++ has more confusing issues. I believe that C99 is causing some of
those same problems, however, since C99 is much bigger than its
predecessor. The same thing will be true of C++0x: it will be bigger
and have more compromises.


>The same in C++ is much 
>less true - I keep finding myself running into limitations which have 
>no good reason. For example, the concept of destination type seems to 
>have no effect in C++ eg;
>
>TQString foo;
>foo="Hello world";
>
>Now TQString is a subclass of QString, and both have const char * 
>ctors. The compiler will refuse to compile the above code because 
>there are two methods of resolving it. 

I may not understand what you mean by "two methods of resolving it," but
I don't understand why the compiler doesn't do what you think it should
above. If TQString has a const char* ctor, then I think that should
promote "Hello world" to a TQString and then use TQString's assignment
operator to change 'foo'.

>Now, to me, that seems stupid 
>because quite clearly the destination type is TQString and the 
>shortest route to that is to use the TQString const char * ctor ie; I 
>clearly am inferring to use the shortest route. The same sort of 
>thing applies to overloading functions - you cannot overload based on 
>return type, something I find particularly annoying.

Another C++ idiom lets you do just that. I'll have to show that one to
you when I have more time. Ask if you're interested.

>
>>>I never had much experience with it, but Objective C
>>>always seemed a lot cleaner.
>>
>>If ObjC is so much better, why is it so unpopular?
>
>Lots of reasons. If I remember correctly, there were many problems 
>with the run-time library on different platforms. There were issues 
>regarding Next and Apple and all that. Of course, as well, there were 
>culture issues - programmer inclinations. Also, there was good 
>competition between many C++ vendors which brought C++ tools to a 
>decent quality pretty quickly.
>
>Computer history is strewn with cases of an inferior product 
>destroying a superior product. It's hardly unique.

I agree. I guess my point is simply this: any popular language is going
to have warts that an unpopular language will not. Take Eiffel for
example. Way back when Eiffel was very young, Bertrand Meyer derided
C++'s 'friend' construct, claiming it violated encapsulation. Then he
began to get real users who were building real systems using Eiffel, and
suddenly he began to see how something like the 'friend' construct
actually *improves* encapsulation. So he added it to Eiffel. At first
the language seemed cleaner and simpler, then gradually it added more
stuff as it became more practical.

C++ is saddled with three basic goals: it tries to be a good procedural
programming language ("C++ as a better C"), and at the same time a good
OO language, and at the same time a good language for programming with
"generics." Trying to be a jack of all trades is difficult, and
ultimately involves compromises. However if you pointed out any
particular compromise, I could probably tell you why it was done and in
fact could (I hope!) make you realize that "cleaning up" that compromise
would cause more harm than good.

In any case, I agree that good products don't always win in the
marketplace.


>>>I take it you come from a Smalltalk background?
>>
>>Not at all. My C++ "smells like" C++, not like Smalltalk or assembler
>>or anything else. Similarly my C code smells like C code, and it uses
>>C idioms, etc., and my Java smells like Java, etc. I am language 
>>neutral, e.g., I've been a member of both the ANSI C++ and ANSI 
>>Smalltalk committees.
>
>In which case you are a better programmer than I. I essentially 
>program the same in any language using an internal methodology and my 
>measure of my liking a language is how little it distorts what I 
>actually want to do (hence my strong dislike of Java and 
>VisualBasic). Nothing I program is what other people call a typical 
>style of that language. You may think that irresponsible and arrogant 
>of me, but I know it is an innate quality of mine - it's the same 
>when I learn human languages (I still retain my own speech formation 
>and pronounciation irrespective).
>
>Hence, I am more of a functional programmer than anything else. 

Do you really mean "functional" or "procedural" here? The Functional
style is rather difficult to do in C++ (think Scheme). Functional
programming means never allowing any changes to any piece of data, so
instead of inserting something into a linked list, one creates a new
linked list and returns the new linked list that contains the new item.

>It is 
>my dream to some day design an imperative/functional hybrid language 
>which would perfectly reflect how I like to program.
>
>>>I know this will
>>>sound approaching blasphemous - and I don't mean at all to be 
>>>offensive, but merely to garner an opinion - but I have always 
>>>considered OO to be a good way of organising maintainable source but
>>>really crap for designing code.
>>
>>Another really big error. OO is primarily a design approach. The 
>>concept of "OO programming" is very close to a misnomer, since OO 
>>programming cannot stand on its own - it needs OO *design*.
>
>No, I must disagree with you there: design is independent of 
>language. 

Nope, not true at all. A design that works for Functional languages is
horrible for Procedural languages, and vice versa. And both those
designs are wholly inappropriate for OO languages, Logic-oriented
languages, or Constraint-oriented languages. In short, the paradigm
*very* much effects the design.

Try your belief out sometime. Try implementing your favorite program in
Prolog (logic-oriented) or Scheme (function-oriented) and see what
happens. Guaranteed that if your program is nontrivial, a radically
different design will emerge. Either that or you'll constantly be
fighting with the paradigm and the underlying language, trying to force,
for example, Prolog to be procedural.

I'll go further: design isn't even independent of language *within* a
paradigm. In other words, a design that is appropriate for Smalltalk is
typically inappropriate for C++, even when you are trying very hard to
use OO thinking throughout.

>I have never agreed with OO design as my university 
>lecturers found out - I quite simply think it's wrong. Computers 
>don't work naturally with objects - it's an ill-fit.
>
>What computers do do is work with data. If you base your design 
>entirely around data, you produce far superior programs. 

In your experience, this may be true. But trust me: it's a big world
out there, and in the *vast* majority of that world, your view is very
dangerous.

Be careful: you are painting yourself into a very narrow corner. You
may end up limiting your career as a result.


>Now I will 
>agree OO is good for organising source for improved maintainability, 
>but as a design approach I think it lacking.

You really should read "OO Design Patterns" by Gamma, et al (also
published by Addison Wesley). Read especially chapter 2. I think
you'll see a whole world of OO design -- and you'll see ways to use OO
at the design level that are totally different (and, I dare say, totally
superior) to the approach you are describing here.

Or take the example of IBM's AS/400. In the early 90s, IBM retained me
to train and mentor all their developers in Rochester MN (and some at
Endicott NY) because they had decided to rewrite the kernel of their
AS/400 operating system using OO design and C++. When we started, they
had a business problem: it took their people 9 months to add certain
features and capabilities. This particular category of "feature and
capability" was added often enough that they wanted to fix that problem.
But being the kernel of an operating system, they couldn't do *anything*
that added *any* overhead.

This project ended up being around half a person-millennium (150-200
developers over a 3 year period). I ended up training and mentoring
them all, and we had lots and lots of design sessions. When they were
finished, the things that used to take 9 months could be done by a
single person in less than a day. The success-story was written up in
Communications of the ACM -- it was the lead article in the Special
Issue on Object-Oriented Experiences. It was also written up in IEEE
Software and perhaps a few other places. (And, by the way, there was no
loss of performance as a result. That was *very* hard to achieve, but
we did it. In the end, customers gained 2x MIPS/dollar.)

The point is that these benefits came as result of OO *design*, not as a
result of programming-level issues.

One more example: UPS (another of my clients; in fact I was there just
last week) has new "rating" and "validation" rules that change every 6
months. For example, if Detroit passes a law saying it's no longer
legal to drive hazardous materials through its downtown area, the code
needs to change to prevent any package containing hazmat from going
through downtown Detroit. In their old system, which was built using
your style of C++, it took 5 months out of every 6 to integrate these
sorts of changes. Then someone created a framework using OO design (not
just C++ programming), and as a result, they could do the same thing in
2 weeks.

Okay, one more - this is the last one -- I promise :-) IBM has an 800
number you can call when you want to buy a ThinkPad or some other
equipment. This division generates *billions* of dollars per year, and
as a result, quality and performance were very important. But
flexibility was also important, because companies like Dell were
promoting their build-to-order systems, and were able to offer
on-the-spot deals that IBM's system couldn't match. Simply put, IBM's
high-performance and high-quality constraints were working against the
system's flexibility, and it was taking IBM wayyyyyy too long to add
promotional deals or other competitive ideas. (Their old approach was
built using non-OO *design* even though it was built using C++.)

When we were done with their system, they could create and install most
changes in minutes. All that without loss of performance and with an
improvement in quality/stability.


>An example: take your typical novice with OO. Tell them the rules and 
>look at what they design. Invariably, pure OO as designed against the 
>rules is as efficient as a one legged dog. 

The way you have learned OO, yes, it will have performance problems.
But the way I am proposing OO should be done, either it won't have
performance problems at all, or if it does, those problems will be
reparable.

>In fact, in my opinion, OO 
>experience is actually learning when to break pure OO and experienced 
>OO advocates do not realise that they so automatically break the pure 
>application of what they advocate.

We agree that purity is never the goal. Pure OO or pure procedural or
pure anything else. The goal is (or *should* be) to achieve the
business objectives. In my experience, OO *design* brings the real
value, and not just programming-level issues.


>A practical example: at university, we had to design a program to 
>sort post office regional codes. The typical class effort, for which 
>they received top marks, sorted the list in about ten to twenty 
>seconds. My effort did it so quickly there wasn't a delay in the 
>command prompt returing - and may I add, I received a bare pass mark 
>because I adopted a data-centric solution and not an OO one. Now I 
>couldn't fault that (the story of my entire degree), but it painfully 
>reminded me of how OO is fundamentally incorrect for computers - good 
>for humans, but not computers.

I agree with everything except your last phrase. OO design is good for
both people and computers.

Marshall




From: Niall Douglas <xxx@xxxxxxx.xxx>
To: "Marshall Cline" <xxxxx@xxxxxxxxx.xxx>
Subject: RE: Comments on your C++ FAQ
Date: Tue, 30 Jul 2002 22:39:24 +0200

On 28 Jul 2002 at 22:31, Marshall Cline wrote:

Firstly, may I ask your permission to distribute a digest of our 
conversation to others? I believe quite a few people could do with 
reading it because (and this may worry you) I am considered one of 
the better C++ people out of our class' graduates. If I didn't know, 
I'm very sure they didn't simply because it was never taught.

> I spoke last time about being a prisoner of our pasts. My past
> includes acting as "senior technology consultant" to IBM throughout
> North America, which meant advising on product strategy, mentoring,
> and (most relevant to this situation) performing internal audits. The
> audits included a number of important engagements with IBM's clients,
> and required me to perform assessments of people and technology. 
> During these audits and assessments, I saw a lot of large projects
> that failed because of overengineering. Many of the technologists on
> these sick or dead projects had a similar perspective to what you
> articulated above. Their basic approach was often that overengineering
> is better than underengineering, that it's cheaper in the long run,
> and perhaps cheaper in the short run, so let's overengineer just in
> case.

I think there are two types of overengineering: controlled and 
uncontrolled. The latter happens when the people doing the design 
aren't really sure what they're doing. The former happens when the 
designers take into proper account the likely extensions in the 
future, possible client changes in specification, ramifications on 
maintainability etc. and balance all of these against time of 
implementation, worth to the project etc. Essentially, what I am 
really saying, is if you spend plenty of time on *design* then your 
project comes in on time and within budget.

BTW, have you heard of extreme programming 
(http://www.extremeprogramming.org/)? Daft name, but it's an 
interesting looking way of managing and overseeing computer projects. 
It certainly is less intrusive than auditing, and establishes more 
trust between customer and provider.

> As a result of seeing in excess of one hundred million dollars worth
> of effort (and numerous careers) washed down the drain, I tend to make
> sure there is a realistic ROI before adding any effort that has a
> future-payback.

Again I think we're saying precisely the same thing with different 
words.

Let me give you a bit of background on myself (helps later on): My 
role in recent years is saving troubled projects. I am brought in 
when things have gone horribly wrong - for example, my last two 
positions were saving a handheld GPS project in Canada and saving a 
EuroFighter component test bench control software project here in 
Spain. Usually, I come in, assess the situation (code, employees and 
most importantly management) and fix it. In both projects, I have 
been spectacularly successful, albeit at the cost of my own job - to 
save a troubled project you need to work on many areas, but the most 
obstinate in my experience is management who employ a "pass the buck" 
methodology whilst firing good programmers to divert the blame. In 
the end, I always come up against what was killing the project 
beforehand, at which stage it's time to move on.

However, my background is in a little British computer called an 
Acorn which ran on ARM processors (nowadays Acorn is liquidated and 
ARM, its offshoot, is one of the bigger UK companies). Acorn's ran an 
OS called RISC-OS which was the last general purpose all-assembler OS 
ever written. And I will tell you now, it was vastly ahead of 
anything else at the time - and I include Unix. Obviously, everything 
in the system was designed around writing in assembler, and hence 
large applications (DTP, editors, spreadsheets, photo-editing, music 
composition etc.) often were entirely written in hand-coded ARM. 
Hence all of us did stuff which most people consider unlikely in 
assembler - for example, we used what you could call an object in 
that some code would have instance data and a ctor and destructor. We 
had the equivalent of virtual functions using API offset tables. Some 
silly people used self-modifying code, which is worse that goto's 
IMHO.

What is important to get from this is that until the US 
multinationals crushed our indigenous European computer industry, we 
were in many ways considerably ahead of the status quo. This is why I 
don't fit easily into boxes others like to assign me to.

> >>Ouch, that's certainly a very dubious design style. It's a typical
> >>hacker's style, and it comes from the Smalltalk world, but it's
> >>generally inappropriate for C++ or Java or any other statically
> >>typed OO language.
> >
> >Can you point me to resources explaining why this is bad and not just
> > a question of individual style? 
> 
> Sure no problem. Start with our book ("C++ FAQs", Addison Wesley),
> then go to Scott Meyer's books ("Effective C++" and "More Effective
> C++", also Addison Wesley), and probably most any other book that
> deals with design/programming style in C++.

Not being able to obtain these books easily (I live in Spain plus 
money is somewhat tight right now), I looked around the web for more 
on this. I specifically found what not to do when inheriting plus how 
deep subclassing usually results in code coupling increasing. Is that 
the general gist?

> >I would have thought it /better/ for 
> >statically typed languages because the compiler is given more 
> >knowledge with which to optimise.
> 
> Nope, it's a very Smalltalk-ish style, and it causes lots of problems
> in a statically typed OO language since today's statically typed OO
> languages (C++, Java, Eiffel, etc.) equate inheritance with subtyping.
> In any language that equates inheritance with subtyping, using
> inheritance as a reuse mechanism, as opposed to using inheritance
> strictly for subtyping purposes, ultimately causes lots of design and
> extensibility problems. It can even effect performance.

In other words, precisely the trap I was falling myself into. I 
should however mention that having examined my code, I was peforming 
this trap only in the areas where Qt wasn't providing what I needed. 
In the code generated entirely by myself, I tend to use a top-down 
approach with an abstract base class defining the reusable parts.

I should mention that much of the subclassing I have had to do will 
disappear with future versions of Qt as they have very kindly mostly 
agreed with my ideas. Hence, in fact, until v4.0, it's mostly stop-
gap code.

> >Again, I'd like to know precisely why this style would be a
> >poor choice for some other app.
> 
> Mostly because it creates all sorts of problems for users. Take, for
> example, your TSortedList class. You have removed the append() and
> prepend() methods because you can't implement them properly in your
> class. Nonetheless someone might easily pass an object of your
> derived class via pointer or reference to its base class, and within
> that function the methods you tried to remove are suddenly available
> again, only this time with potentially disastrous results. Take, for
> example, this function:
> 
> void f(QList<Foo>& x)
> {
> x.prepend(...); // change '...' to some Foo object
> x.append(...); // change '...' to some Foo object
> }
> 
> Now suppose someone passes a TSortedList object to this function:
> 
> void g()
> {
> TSortedList<Foo> x;
> f(x);
> ...what happens here??
> }

Err, prepend and append aren't virtual in the base class, so the base 
class' versions would be called. I had realised previously that it's 
a very bad idea to disable virtual inherited methods - or if you were 
to, you'd want a fatal exception in there to trap during debug.

> In the '...what happens here??' part, anything you do to the
> TSortedList is likely to cause problems since the list might not be
> sorted. E.g., if f() adds Foo objects to 'x' in some order other than
> the sorted order, then the '...what happens here??' part is likely to
> cause serious problems.
> 
> You can't blame this problem on references, since the same exact thing
> would happen if you changed pass-by-reference to pass-by-pointer.
> 
> You can't blame this problem on the C++ compiler, because it can't
> possibly detect one of these errors, particularly when the functions
> f() and g() were part of two different .cpp files ("compilation
> units") that were compiled on different days of the week.
> 
> You can't blame this problem on the author of g(), because he believed
> the contract of TSortedList. In particular, he believed a TSortedList
> was a kind-of a QList. After all that is the meaning of subtyping,
> and subtyping is equated in C++ with inheritance. The author of g()
> simply believed what you said in this line: 'class TSortedList :
> public QList', and you can't blame him for believing what you said.
> 
> You can't blame this problem on the author of f(), because he believed
> the contract of QList. In particular, he believed he can append()
> and/or prepend() values in any order onto any QList. Besides, he
> wrote and compiled his code long before you even thought of deriving
> TSortedList, and by the rules of extensibility (e.g., see the sections
> on Inheritance in the C++ FAQ, or any similar chapters in any book on
> the subject), he is not required to predict the future - he is
> supposed to be able to write code based on today's realities, and have
> tomorrow's subclasses obey today's realities. That is the notion of
> is-a, and is codified in many places, including the C++ FAQ, Liskov's
> Substitutability Principle ("LSP"), and many other places.
> 
> So who is at fault? Ans: the author of TSortedList. Why is the
> author of TSortedList at fault? Because of false advertising: he said
> TSortedList was a kind-of a QList (or, using precise terminology, that
> TSortedList was substitutable for QList), but in the end he violated
> that substitutability by removing methods that were promised by QList.

Worse I think would be redefining inherited methods to do something 
completely different. But yes, I understand now.

The rule should be that subclasses must always behave like their base 
class(es). Code reuse should be done via composition.

Hence, that TSortedList should now derive off QGList which doesn't 
have the append and prepend methods so I can safely ensure it does 
what its parent does.

> Put it this way: you inherit from "it" to *be* what it *is*, not
> simply to have what it has. If you simply want to have what it has,
> use has-a (AKA aggregation AKA composition).

Yes, I definitely understand you now. It is a pity explanations like 
this weren't more universally available, because I know a lot of C++ 
programmers learned from MSVC's online help (eg; initially me - it's 
where I learned C from as well). I however did subscribe to the 
Association of C & C++ Users which is why I know about 
standardisation debates - but even though at the time I subscribed 
for the C coverage, I did read the C++ sections.

A lot of people say pointers are the devil's tool in C and I have met 
a disturbing number of programmers who just don't understand them. 
However, it seems to me pointers are child's play in unenforced 
danger when compared to problems like you and your FAQ have 
mentioned. If more warning were out there, we'd all have less 
problems with other people's code.

> (BTW I will quickly add that your approach is perfectly fine in a very
> small project, since in very small projects you can control the damage
> of "improper" or "bad" inheritance. Some of my colleagues won't agree
> and will say your approach is *always* wrong, and in a sense I would
> agree. But from a practical basis, your approach doesn't really cost
> too much in the way of time, money, or risk with a small enough
> project. If you use your approach on a big project, however, everyone
> seems to agree, and everyone's experience seems to prove, that your
> approach is very dangerous and expensive.)

Unfortunately what I am working on now I expect to exceed 100,000 
lines before I'll consider it reasonably done. I'll explain later.

> [content clipped]
> **DING** I just found your bug. In TSortedList.cpp, all your methods
> are listed like this:
> [code chopped]
> Your syntax ("TSortedList<class type>::") explains everything,
> including the bizarre use of 'class type' within the error messages. 
> What happened is that the compiler saw you using a TSortedList<class
> type>, and it therefore tried to compile all the virtual methods
> within TSortedList<class type>. When it saw that 'type' really isn't
> a genuine class type, it complained (eventually) that the class called
> 'type' doesn't have an == or < operator.

I don't see how the syntax is consistent then. From what I can see 
template<pars> X where X is the code to be parametrised - or are you 
saying I declare the methods in the class definition and move the 
code to inline void TSortedList<class type>::foo() after the template 
class definition?

Either way, this was my first ever template class (yes, in all three 
years of using C++) and I copied heavily off QSortedList.h (which I 
enclosed last time) which might I point out compiles absolutely fine. 
So why my class, almost identical, does not and Qt's one does I do 
not know.

> When you fix this problem, you will end up with additional problems,
> mainly because you have moved template code into a .cpp file. The C++
> FAQ covers this issue; suggest you read that for details. (It's
> probably in the section on templates and/or containers.)

It says you can't move template code into a .cpp file :)

I think you can just put it in the header file though? I'm still not 
sure why it threw an error :(

> >[a bloody good suggestion]
> >Thank you!
> 
> No problem. BTW I consider this an idiom of C++. Part of being
> competent in using a language is knowing the syntax and semantics, but
> another critical part is knowing the idioms of the language. You're
> an expert (I assume) in certain varieties of assembler, and perhaps
> also in C. As a competent C programmer, you know the C idioms, such
> as
> 
> while (*dest++ = *src++)
> ;
> 
> This, of course, is the idiom that copies an array of things pointed
> to by 'src' into an array pointed to by 'dest', and it stops copying
> after it copies the item whose value is zero. If the arrays are
> arrays of 'char', this is equivalent to strcpy(), since it copies
> everything including the terminating '\0'. Obviously other types are
> similar.

That kind of dangerous code brought out a compiler bug in a version 
of GCC and MSVC 5 if I remember correctly. The increments weren't 
always done with the load and store when full optimisation was on. 
Solution: use comma operator.

> Other idioms in C abound, such as Duff's device:
> 
> while (n >= 0) {
> switch (n) {
> default: xyzzy;
> case 7: xyzzy;
> case 6: xyzzy;
> case 5: xyzzy;
> case 4: xyzzy;
> case 3: xyzzy;
> case 2: xyzzy;
> case 1: xyzzy;
> }
> n -= 8;
> }
> 
> If you replace 'xyzzy' with some piece of code, this applies that
> piece of code exactly 'n' times, but it is much faster than the
> equivalent:
> 
> while (n-- > 0) {
> xyzzy;
> }
> 
> Since the latter executes 'n' decrements, 'n' comparisons, and 'n'
> conditional jumps, whereas Duff's device executes only 1/8'th as many
> decrements, comparisons, or conditional jumps. Of course the key is
> that there is no 'break' statement after each 'case' -- each case
> "falls through" to the next case. The other key, obviously, is that
> the cases are listed in backwards order.

This is called loop unrolling in assembler and I thought compilers 
did it for you because modern processors run so much faster than 
system memory that the compsci measurement of execution time is often 
way way off - smaller code on modern processors goes faster than 
bigger code, even with loads of pipeline flushes from the conditional 
branches because the L1 cache is 10x system memory speed.

> The point, of course, is that this is another idiom of C, and
> competent C programmers know these sorts of things. As you become
> better and better at C++, you will learn the idioms of C++, and this
> is one of them.

Actually, using a switch() statement is bad on modern deep pipeline 
processors. It's better to use a function pointer table and calculate 
the index because then the data cache effectively maintains a branch 
history for you.

If you ask me about embedded systems, I don't doubt I'm as good as 
they get. All this high-level stuff though I must admit is beyond me 
a bit. But more on that later.

> >I have the utmost respect and admiration 
> >for any standardisation committee (with possible exception of the
> >POSIX threads committee, their poor design really screws C++ stack
> >unwinding which is unforgiveable given how recently it was designed).
> 
> Not knowing any better, I'd guess they were dealing with very subtle
> constraints of existing code or existing practice. 

Twas a completely new API AFAIK.

> Most people on
> those sorts of committees are competent, plus the entire world
> (literally) has a chance to comment on the spec before it gets
> finalized, so if there were gaping holes *that* *could* *be* *fixed*
> (e.g., without breaking oodles and oodles of existing code), I'm sure
> *someone* *somewhere* in the world would have pointed it out.

From the usenet posts I've read, people did point out POSIX thread 
cancellation did not offer C++ an opportunity to unwind the stack, 
but they ignored it and went with a setjump/longjmp solution instead. 
Now if your platform's setjmp implementation unwinds the stack - 
fantastic. If not, severe memory leakage. All they needed was the 
ability to set a function to call to perform the cancellation - under 
C++, that would be best done by throwing an exception. Still, we can 
hope it will be added in the near future.

> Like I said, I don't know the details of this particular situation,
> but I would guess that they were fully aware of the problem, that they
> investigated all the alternatives, and that they chose the "least bad"
> of the alternatives.

The above support for C++ (and other languages) costs maybe an hour 
to add and is completely portable. I can't see how it wasn't done by 
competent and fair designers. I have read rabid posts on usenet why 
you shouldn't be writing mulithreaded C++ and such bollocks - not 
sure if that's involved. The whole issues of threads gets many Unix 
people's knickers in a right twist - they seem to think they're 
"wrong", much like exceptions are "wrong" in C++ for some people. 
Weird.

> Yes, existing code puts everyone in a very difficult position, and
> often causes compromises. But that's the nature of the beast. The
> cost of breaking existing code is much, much greater than canonizing
> it. [clipped rest]

Have you noticed the world's most popular programming languages tend 
to be evolved rather than designed? ;)

> >Put it this way: when you try something which seems logical in C it
> >generally works the way you think it should. 
> 
> Really? No less a light as Dennis Ritchie bemoans the precedence of
> some of the operators, and certainly the rather bizarre use of
> 'static' has caused more than one C programmer to wonder what's going
> on. Plus the issue of order of evaluation, or aliasing, or any number
> of other things has caused lots of consternation.

Yeah, I've read his comments. There's one operator in particular - is 
it &&? - which is very suspect in precedence.

However, that said, once you get used to the way of C logic it stays 
remarkably consistent. I personally recommend sticking "static" 
before everything you want to be static and don't rely on default 
behaviour - people's confusion with static clears up remarkably 
quickly if you do that.

> But I guess I agree to this extent: C++ is larger than C, and as such
> C++ has more confusing issues. I believe that C99 is causing some of
> those same problems, however, since C99 is much bigger than its
> predecessor. The same thing will be true of C++0x: it will be bigger
> and have more compromises.

It's also a case of history. K&R C was 90% done by just a few guys. 
C++ is a collection of different enhancements over C by completely 
different people with different intentions, and then with a good 
dollop of academic theory thrown in for good measure. Hence its non-
uniformity and lack of consistency.

Note that I have not yet found an academic who thinks C is an 
excellent example of a procedural language :)

> >TQString foo;
> >foo="Hello world";
> >
> >Now TQString is a subclass of QString, and both have const char *
> >ctors. The compiler will refuse to compile the above code because
> >there are two methods of resolving it. 
> 
> I may not understand what you mean by "two methods of resolving it,"
> but I don't understand why the compiler doesn't do what you think it
> should above. If TQString has a const char* ctor, then I think that
> should promote "Hello world" to a TQString and then use TQString's
> assignment operator to change 'foo'.

I completely agree. However, MSVC wants you to put a TQString("Hello 
world") around every const char * :(

I'm running into similar problems with the << and >> operators - I've 
subclassed QDataStream with TQDataStream because QDataStream is 
default big endian and doesn't provide support for 64 bit integers. 
Every single time I use << or >> I'm an ambiguous resoluton error 
when clearly the source or destination object is a TQDataStream.

Most of my C++ problems are arising from "repairing" Trolltech's 
code. While they will fix things similarly in future Qt's as a result 
of my suggestions, it still leaves now.

> > The same sort of
> >thing applies to overloading functions - you cannot overload based on
> > return type, something I find particularly annoying.
> 
> Another C++ idiom lets you do just that. I'll have to show that one
> to you when I have more time. Ask if you're interested.

Is that like this:
bool node(TQString &dest, u32 idx)
bool node(TKNamespaceNodeRef &ref, u32 idx)
...

> >Computer history is strewn with cases of an inferior product 
> >destroying a superior product. It's hardly unique.
> 
> I agree. I guess my point is simply this: any popular language is
> going to have warts that an unpopular language will not. Take Eiffel
> for example. Way back when Eiffel was very young, Bertrand Meyer
> derided C++'s 'friend' construct, claiming it violated encapsulation. 
> Then he began to get real users who were building real systems using
> Eiffel, and suddenly he began to see how something like the 'friend'
> construct actually *improves* encapsulation. So he added it to
> Eiffel. At first the language seemed cleaner and simpler, then
> gradually it added more stuff as it became more practical.

Still, Eiffel is considered much cleaner than C++ - however, it's not 
as popular. cf. my statement above about popular languages not being 
designed.

> However if you
> pointed out any particular compromise, I could probably tell you why
> it was done and in fact could (I hope!) make you realize that
> "cleaning up" that compromise would cause more harm than good.

Ok:
1. Why didn't C++ have separated support for code reuse and subtyping 
(like Smalltalk)?
2. Why don't return types determine overload?
3. Why can't the compiler derive non-direct copy construction? eg;
class A { A(B &); } class B { B(C &}; } class C { C(const char *); }
A foo="Hello";

In C++, you must rewrite that as A foo(B(C("Hello"))); - it's not 
done for you, nor is there any way of fixing it except modifying A to 
have a copy constructor taking const char * - which isn't possible if 
you don't have the source to A or B.

> Do you really mean "functional" or "procedural" here? The Functional
> style is rather difficult to do in C++ (think Scheme). Functional
> programming means never allowing any changes to any piece of data, so
> instead of inserting something into a linked list, one creates a new
> linked list and returns the new linked list that contains the new
> item.

I mean "functional" in terms of I tell you what to do not how to do 
it. 

Also above, the new linked list isn't created, merely a potential for 
a separate new linked list is. You're right that it's "as if".

> >>Another really big error. OO is primarily a design approach. The
> >>concept of "OO programming" is very close to a misnomer, since OO
> >>programming cannot stand on its own - it needs OO *design*.
> >
> >No, I must disagree with you there: design is independent of 
> >language. 
> 
> Nope, not true at all. A design that works for Functional languages
> is horrible for Procedural languages, and vice versa. And both those
> designs are wholly inappropriate for OO languages, Logic-oriented
> languages, or Constraint-oriented languages. In short, the paradigm
> *very* much effects the design.

I would have said it affects the /implementation/ rather than the 
design. You're right that say doing a sort in Haskell is completely 
different than doing it in C - but I would call that a difference in 
implementation because (a) the algorithm used is identical and (b) 
the two solutions give identical output.

> Try your belief out sometime. Try implementing your favorite program
> in Prolog (logic-oriented) or Scheme (function-oriented) and see what
> happens. Guaranteed that if your program is nontrivial, a radically
> different design will emerge. Either that or you'll constantly be
> fighting with the paradigm and the underlying language, trying to
> force, for example, Prolog to be procedural.

No, a radically different /implementation/ will emerge. That's simply 
because implementing the design is best done one way in one language 
and differently in a different language.

> I'll go further: design isn't even independent of language *within* a
> paradigm. In other words, a design that is appropriate for Smalltalk
> is typically inappropriate for C++, even when you are trying very hard
> to use OO thinking throughout.

I'm getting a feeling that once again it's a disagreement about 
terminology rather than opinion. I would treat the word "design" as 
that in its purest sense - algorithms. Everything after that has 
increasing amounts of implementation - so, for example, the object 
structure would involve some amount of implementation detail.

> >I have never agreed with OO design as my university 
> >lecturers found out - I quite simply think it's wrong. Computers
> >don't work naturally with objects - it's an ill-fit.
> >
> >What computers do do is work with data. If you base your design
> >entirely around data, you produce far superior programs. 
> 
> In your experience, this may be true. But trust me: it's a big world
> out there, and in the *vast* majority of that world, your view is very
> dangerous.

I have applied my skills to many projects: public, private and 
personal and I have not found my data-centric approach to have failed 
yet. It has nothing to do with code maintainability nor much other 
than efficiency - but that's why I use an impure OO for 
maintainability - but if you rate superiority of a program based on 
its excellence in functioning, my approach works very well. I 
contrast with OO designed projects and quite simply, on average they 
do not perform as well.

Now regarding the TCO of the code, I would personally say my code is 
extremely maintainable using my OO-like source filing system. You, I 
would imagine, would say how can I sleep at night when performing 
such atrocities to commonly held standards? (you wouldn't be the 
first to ask this).

Of course, in all this, I am referring to C and assembler and what 
I'd call C+ because I mostly wrote C with some small C++ extras. This 
project I'm working on now is the first to use multiple inheritance 
and templates and a fair bit more.

> Be careful: you are painting yourself into a very narrow corner. You
> may end up limiting your career as a result.

Possibly, but I would doubt it. I may have some unique opinions on 
this but what the customer cares about is (a) will it work and (b) 
can we look after it well into the future. My case history strongly 
supports both of these criteria, so a priori I'm on the right path.

> >Now I will 
> >agree OO is good for organising source for improved maintainability,
> >but as a design approach I think it lacking.
> 
> You really should read "OO Design Patterns" by Gamma, et al (also
> published by Addison Wesley). Read especially chapter 2. I think
> you'll see a whole world of OO design -- and you'll see ways to use OO
> at the design level that are totally different (and, I dare say,
> totally superior) to the approach you are describing here.

Is that about a vector graphics editor called Lexi? I have written 
two vector graphic editors, the latter in OPL for a Psion Series 3 
(OPL is quite like BASIC - no objects). Interestingly, the approach 
Gamma follows is almost identical to my own - I used dynamic code 
loading to load tool modules with a fixed API thus permitting 
infinite extensibility. Encapsulation of the API plus building a 
portable framework are two things I have done many times - I wrote my 
first framework library in 1992 some four years before going 
professional.

That Gamma book amused me - it attaches lots of fancy names to real 
cabbage and thistle programming. However, his conclusion is valid - 
in modern times, most programmers wouldn't know half that book, and 
that's worrying - hence the need for such a book.

> This project ended up being around half a person-millennium (150-200
> developers over a 3 year period). I ended up training and mentoring
> them all, and we had lots and lots of design sessions. When they were
> finished, the things that used to take 9 months could be done by a
> single person in less than a day. The success-story was written up in
> Communications of the ACM -- it was the lead article in the Special
> Issue on Object-Oriented Experiences. It was also written up in IEEE
> Software and perhaps a few other places. (And, by the way, there was
> no loss of performance as a result. That was *very* hard to achieve,
> but we did it. In the end, customers gained 2x MIPS/dollar.)
> 
> The point is that these benefits came as result of OO *design*, not as
> a result of programming-level issues.

I'm sure OO design greatly improved the likely wasp's nest of 
spaghetti that existed in there previously. But I'm not seeing how OO 
design is better than any other approach from this example - there 
are many methods that could have been employed to achieve the same 
result.

> One more example: UPS (another of my clients; in fact I was there just
> last week) has new "rating" and "validation" rules that change every 6
> months. For example, if Detroit passes a law saying it's no longer
> legal to drive hazardous materials through its downtown area, the code
> needs to change to prevent any package containing hazmat from going
> through downtown Detroit. In their old system, which was built using
> your style of C++, it took 5 months out of every 6 to integrate these
> sorts of changes. Then someone created a framework using OO design
> (not just C++ programming), and as a result, they could do the same
> thing in 2 weeks.

Any good framework here, OO or not, would have solved most of their 
dynamic change problem. In fact, I'd plug in some sort of scripting 
capability so such items were easy to change.

> >An example: take your typical novice with OO. Tell them the rules and
> > look at what they design. Invariably, pure OO as designed against
> >the rules is as efficient as a one legged dog. 
> 
> The way you have learned OO, yes, it will have performance problems.
> But the way I am proposing OO should be done, either it won't have
> performance problems at all, or if it does, those problems will be
> reparable.

ie; You're bending OO to suit real-world needs, which is precisely 
what I said experienced OO people do.

> >In fact, in my opinion, OO 
> >experience is actually learning when to break pure OO and experienced
> > OO advocates do not realise that they so automatically break the
> >pure application of what they advocate.
> 
> We agree that purity is never the goal. Pure OO or pure procedural or
> pure anything else. The goal is (or *should* be) to achieve the
> business objectives. In my experience, OO *design* brings the real
> value, and not just programming-level issues.

Has it not occurred to you that it's merely a /consequence/ of OO 
rather than innate quality that it has these beneficial effects?

> I agree with everything except your last phrase. OO design is good
> for both people and computers.

Right, firstly, before I start this section, I'd like to thank you 
for your time and patience - I've noticed some of what I didn't know 
and you explained to me was already online in your FAQ, so I 
apologise for wasting your time in this regard. Furthermore, I should 
mention that if you give me permission to distribute this 
correspondence, you will not only have done me a great favour but 
also the same to others. Certainly, if it takes you as long to reply 
as me, you're investing considerable time which a busy man as 
yourself surely cannot easily spare.

I, as I have already mentioned, come from a rather unique programming 
background. We were probably most comparable to the Unix culture 
except we were more advanced and we always had a very strong free 
software tradition where we released code and source into the public 
domain - furthermore, many commercial apps came with source too. 
Hence, there was great chance for learning off others, and much of 
this recent furore about OO etc. in my humble opinion is merely fancy 
names for a collection of old techniques.

Now as I mentioned a number of times, I believe a data-centric 
approach is superior to OO because it more accurately fits the way a 
computer works. This is not to say many of the advantages of OO do 
not still hold - in fact, I daresay many OO experts actually are data-
centric too without realising it. My criticism of OO therefore is 
that it isn't /intuitively/ "correct" ie; pure OO is rarely the 
optimal solution.

I had an idea back in 1994 for advancing procedural programming to 
the next level (this was independent of OO - honestly, I barely even 
knew what it was at the time) - I effectively wanted to do what OO 
has done in knocking us onwards a notch - however, as it would be, I 
considered then and still do today that my solution is superior.

Basically, it revolves entirely around data. Responsibility for data, 
whether in memory, disc or across a network is devolved entirely to 
the kernel. One may create data streams between data in an arbitrary 
fashion - how it actually is peformed (translations etc.) is however 
the kernel sees fit. Data is strongly typed so you can't stick 
incompatible types of data together - however data can be converted 
from one type to another via convertors which are essentially 
specialised plug ins which can be installed. Often, conversion is 
implicitly performed for you although either you can choose a route 
or it can dynamically create one based on best past performances. Of 
course, converters can offer out their input in more than one format 
or indeed offer a compound document as some or all of its subdatas.

Now the next part of the picture is components - these are tiny 
programs which do one thing and one thing well to data. A good 
analogy would be "more" or "grep" in Unix - but it goes way beyond 
that because components are much like a COM object or Qt Widget in 
that you can just plonk them somewhere and they do their thing. Then, 
the theory is, to build any application, you merely create a *web* of 
simple data processing components. For example, a spell checker 
component would accept text data and check it either with the user or 
with the component managing the data - there is no concept of data 
ownership in my proposal (kernel owns everything)

This model, I believe, compares extremely well to OO. You get lots of 
code reuse, a dynamic and extremely flexible linking mechanism, a web 
rather than a hierarchy and automatic distribution across multiple 
processors (and indeed machines). It's clearly functionally biased 
because it simply sets up the data relations and the kernel works out 
the best way to actually perform the processing. You get lots of 
stuff for free eg; OLE, data recovery in case of program crash and 
indeed limited graphical programming like some of those UML editors. 
You get the advantages of dynamic linking without business' dislike 
of source exposure as with Java or VB.

Furthermore, you get automatic /data/ reuse as well as code reuse - 
data just as much as code can be distributed across multiple machines 
for performance and/or security reasons. And of course, maintainence 
costs are low because the component set you use are as individual or 
fine-grained as you like them.

Now hopefully you'll be agreeing with me that this is all good - 
however, if you're like the other experts I've proposed this to, your 
first question will be "oh but how to implement it?" because the 
balancing act between all the different requirements means severe 
inefficiency. And you'd be right - I've made two prior attempts at 
this and failed both times - and right now, I'm making my third 
attempt which I'm self-financing myself for six months. The theory 
goes, produce a technology demonstration, if it runs at all 
reasonably then obtain venture capital, start a company and two years 
later we have a product. Five years later it's more or less complete. 
If anything goes wrong, return to working on whatever pays a lot for 
a while, then try again in a few years. Either way, the spin off 
benefits of each past attempt have been enormous, so really I can't 
lose.

So, thoughts? I'm particularly interested in what you see as design 
flaws - I know MIT did research into this for a while but stopped. 
Would you agree it's a viable future? I've had Carl Sassenrath (he 
did much the OS for the Commodore Amiga) and Stuart Swales (did much 
of RISC-OS I mentioned earlier) both agree it's probably right, but 
both wondered about implementation. I should be especially interested 
in seeing what a static OO based person things - neither Carl nor 
Stuart are static code nor OO advocates hugely.

Furthermore, any advice about soliciting venture capital in Europe 
would be useful (yes, I know it's like squeezing blood from a stone 
here) - ever since the indigenous industry withered and died here, 
it's been very hard to obtain capital for blue-sky projects without 
the Americans buying them up. I'm unable to obtain a work visa to the 
US (on the banned list), so that's out - and besides, as far as I can 
see, only IBM out of the big US software companies would be 
interested as only IBM's goals would be advanced by such a project. 
Oh BTW, did I mention it runs on Win32/64, Linux and MacOS X when 
they get the new FreeBSD kernel in - and yes, all computer 
irrespective of endian automatically work in unison. I'd also like it 
to stay in Europe so it (or rather I) stays free from software 
patents.

Anyway, any comments you may like to offer would be greatly 
appreciated. You've already earned yourself an acknowledgement in the 
projects docs for helpful tips and suggestions.

Cheers,
Niall




From: "Marshall Cline" <xxxxx@xxxxxxxxx.xxx>
To: "'Niall Douglas'" <xxx@xxxxxxx.xxx>
Subject: RE: Comments on your C++ FAQ
Date: Tue, 30 Jul 2002 02:14:13 -0500

Niall Douglas wrote:
>On 28 Jul 2002 at 22:31, Marshall Cline wrote:
>
>Firstly, may I ask your permission to distribute a digest of our 
>conversation to others? I believe quite a few people could do with 
>reading it because (and this may worry you) I am considered one of 
>the better C++ people out of our class' graduates. If I didn't know, 
>I'm very sure they didn't simply because it was never taught.

Sure, no prob.


>>I spoke last time about being a prisoner of our pasts. My past 
>>includes acting as "senior technology consultant" to IBM throughout 
>>North America, which meant advising on product strategy, mentoring, 
>>and (most relevant to this situation) performing internal audits. The
>>audits included a number of important engagements with IBM's clients, 
>>and required me to perform assessments of people and technology. 
>>During these audits and assessments, I saw a lot of large projects 
>>that failed because of overengineering. Many of the technologists on 
>>these sick or dead projects had a similar perspective to what you 
>>articulated above. Their basic approach was often that overengineering
>>is better than underengineering, that it's cheaper in the long run, 
>>and perhaps cheaper in the short run, so let's overengineer just in 
>>case.
>
>I think there are two types of overengineering: controlled and 
>uncontrolled. The latter happens when the people doing the design 
>aren't really sure what they're doing. The former happens when the 
>designers take into proper account the likely extensions in the 
>future, possible client changes in specification, ramifications on 
>maintainability etc. and balance all of these against time of 
>implementation, worth to the project etc. Essentially, what I am 
>really saying, is if you spend plenty of time on *design* then your 
>project comes in on time an