Fog Creek Software
Discussion Board




C++ question

in one of my (probably futile) job interviews i was asked a the following question: If i had the choice - What feature of C++ could we get rid of ?

grumble, grumble, three days later i have an answer.
i would get rid of the private keyword. (protected is good enough).

there is such a tight coupling between a baseclass and derived class - why the hell do you need information hiding in this case?
It is quite a difficult task to anticipate how a library class is going to be used - i guess it is quite impossible task to predict how the library class is going to be used/extended;

For example with MFC hacking i frequently had problems like - this member/function has not been exported from the DLL.

Can anybody explain where private has been a good and usefull thing?

Michael Moser
Wednesday, June 04, 2003

The answer to your questions lies in these book:

Design Patterns
Effective C++
More Effective C++
C++ Programming Language
Exceptional C++


Wednesday, June 04, 2003

[[ i guess it is quite impossible task to predict how the library class is going to be used/extended ]]

I guess, that's exactly the reason why people should learn and do their homework sometimes.

Evgeny Goldin
Wednesday, June 04, 2003

Hmm... I want my private parts. I *like* encapsulation. You can usually avoid the sort of tight coupling you mention, and it's always a good idea.

My biggest beef with C++ as of right now is the worthlessness of exception specifications.... I like Java-style checked exceptions.

Maybe get rid of protected inheritance. The Meyer book states that "... and no one knows what exactly protected inheritance is supposed to mean" 8-}

But every other feature I can think of at the moment has a rational.

Mike Swieton
Wednesday, June 04, 2003

I'd take the ++ out of C++.

Cheeky Monkey
Wednesday, June 04, 2003

The two "features" I'd like to eliminate from C++ are:

1. The C macro preprocessor

The biggest problem with macros is that they make lexical changes to code in other places. So, if you look at a source file without noticing a macro that's been defined, THE CODE YOU'RE READING IS A LIE, since it'll be replaced by the macro preprocessor.

Plus, the #include macro only performs lexical inclusion of source files (rather than symbolic inclusion), which is the source of so many ugly #idndef sections. The compiler should be smart enough to know which source files have been imported and which haven't been imported.

The only "benefit" of the C preprocessor is that it provides a shortcut for the inlining of blocks of code. In my opinion, this is hardly a benefit anymore, since any compiler worth it's salt will perform some pretty agressive functional inlining of its own.

Code that has been heavily modified by the C preprocessor is difficult to step through. It's difficult to debug. I wish we could just get rid of the damn thing.

2. Header files

Goodness sakes, the whole reason for header files (in the beginning) was to provide a place where function declarations could be put, to eliminate the problem of forward-referenced function calls.

Compilers these days should be able to parse an entire source file and FIND the function declaration, forward references notwithstanding.

But these days, header files aren't just used for function declarations. People sometimes put subtle functionality in a header file (buried among hundreds of sloppy macro definitions). So, rather than the header file enforcing a separation of interface and implementation, header files often have significant implementation details, but now they're in a separate file from most of the other implementation details.

I think it's a very sloppy system. I'd like to eliminate header files entirely (function declarations should only be necessary as part of the implementation of the function).

Benji Smith
Wednesday, June 04, 2003

Benji, without header files how would you ensure that a class was declared the same in all translation units?

David Clayworth
Wednesday, June 04, 2003

I would remove the ability to put source in header files.  Put the class declaration and related structs and consts in theheader, and the source in the source file!

Happy to be working
Wednesday, June 04, 2003

It depends on how you read the question.  If you're not allowed to add new features to replace those you replace it's a very different question (and much more limited possible answers) than if you are allowed to add new features.

In defense of the preprocessor: just because a feature can be abused, doesn't mean you have to abuse it.  Sometimes it is supremely useful - and what's more I beleve it's sometimes fundamental to the C/C++ philosophy.

1. If you are building multiple versions and you really do not want any trace of the same code to show up in the binaries of each version.

Compare:
(a)
#ifdef VERSIONX
  DoSomething() ;
#else
  DoSomethingElse() ;
#endif
vs

(b) if ( versionx )
{
  DoSomething() ;
} else
{
  DoSomethingElse() ;
}

The problem with (b) is it leads to more code in the binary = bigger program and less control to the programmer.  It also is less efficient.  The latter may not matter in a single if, but consider if the if was in a loop running lots of times.  Put simply C/C++ family is supposed to give the programmer control over this type of stuff, and removing the preprocessor removes programmer's control.

2. There are plenty of other cases where the preprocessor is useful.  Consider for example something like

#define ROW(x) { x, (x<<1)+1, (x<<2)+1, (x<<3)+1, (x<<4)+1, (x<<5)+1, (x<<6)+1, (xx<<7)+1 }

int y[8][] =
{
ROW(1)
, ROW(1)
, ROW(2)
, ROW(3)
, ROW(5)
, ROW(13)
, ROW(18)
, ROW(31)
, ROW(0)
} ;

Writing the same code without the preprocessor leads to lots more duplication (harder to edit and maintain)


If I had to choose a feature to remove, with no replacement, I'd go for private (and if I had to protected) INHERITANCE (not members)

i.e.
class X : private Y { } ;
class X : protected Y { } ;
class X : public Y { } ;

would all become
class X : public Y { } ;

This also has the side benefit of breaking relatively little existing code (compared to say removing private members or the preprocessor), if this is a consideration.

S. Tanna
Wednesday, June 04, 2003

If I had to remove one feature from C++, it would be explicit memory management.

Brad Wilson (dotnetguy.techieswithcats.com)
Wednesday, June 04, 2003

C/C++ is supposed to give the programmer control and access to low level features which may affect performane etc.

We don't need to stinkin' garbage collector :-)

S. Tanna
Wednesday, June 04, 2003

I can understand why people would want to remove features that they consider obsolete, or unnecessary (sp?).

What I don't understand is why anyone would want to remove a feature just because there's potential for abuse.

You're actually punishing those that are more competent because of the actions of the incompetent, even if the latter are the majority.

Should we apply this reasoning to everything in life? E.g., eliminate cars because they have an enormous abuse potential? I bet there are more people dying from car accidents than gun accidents.

Is my view too simplistic?
--
"Suravye ninto manshima taishite (Peace favor your sword)" (Shienaran salute)
"Life is a dream from which we all must wake before we can dream again" (Amys, Aiel Wise One)

Paulo Caetano
Wednesday, June 04, 2003

Dear Sir,

I am writing to you with regard to one of the questions posed to me during the interview I recently had with you and your associates.

You and your associates are dumb-asses for asking me such non-sense.  I want to thank you for wasting my time when I could have been at the beach or for that matter the gym.  If you want me to write code for you then give me a call.  On the other hand - If you want to ask me non-sense, please warn me before hand so as not to waste my precious time.

Sincerely,

AnyMouse

P.S.  You think I'm joking?  I'm not.

AnyMouse
Wednesday, June 04, 2003

S.Tanna:

Version b actually does /not/ lead to more code if 
1) versionx is a constant
2) it is defined in the same file
3) the compiler is sufficiently smart.

Most compilers I've seen recently eliminate branches that can never be reached.

If you add the constraint of a sufficiently intelligent linker, you can even get rid of 2) and eliminate dead branches at link time. I'm not aware of any linker that does that right now, but you can always hope....

Your example #2 is one of the few cases where you might say that macros are useful. The difference here is that your preprocessor step applies to data, not to code. And even there, and explicit transformation step might be worth the extra effort. As soon as ROW is defined in some header file buried in the usual header mess, your code becomes meaningless. Scoped Macros, anyone?

Groby
Wednesday, June 04, 2003

David Clayworth:

One solution for the problem of finding the class declaration has been demonstrated by Java - the .class file.

I don't think it's asking too much to have a language that does not require us to express the same facts twice - and C++ does exactly that. Every function, class and variable that appears in a header has to be declared again in the source. Wasteful and error prone.

CPP is a leftover from 'the good old days'. It is time it dies a peaceful death. The drawbacks outweigh the benefits by far.

- Robert

Ceterum censeo 'cpp' esse delendam

Groby
Wednesday, June 04, 2003

Groby:

It's unlikely to be defined in the same file, as most likely a versionx flag applies to many source files. 

In any case, a super smart linker and optimizer is not part of the C/C++ is not mandated by the standards.  So unless these features become mandatory in all compilters, then I'd rather stick with things the way they are, and not have to worry if each individual compiler/linker is sufficiently smart. 

I might also add, I do not consider this #ifdef to be unreadable,  why remove a GENERAL feature just because it might be abused in some circumstances

The reason it is GENERAL, is it covers many other cases of conditional code generation which needs to occur at compile time, not runtime, too. What about something like:-

#ifdef PLATFORMX
typedef short INT16 ;
typedef int INT32 ;
typedef long INT64 ;
#endif
#ifdef PLATFORMY
typedef short INT16 ;
typedef long INT32 ;
typedef __int64 INT16 ;
#endif

I'm sure that you might argue, oh the type's sizes should be preset (like Java).  However the whole point of C/C++ is to allow me to be close to the underlying architecture of the machine, which is why the type's sizes are left open.


As for the 2nd example. Yes there are others ways to do it (like fill in the table at runtime).  But in this case the preprocessor allows me to code it the way I would want it.

Yes it would be a readability problem if ROW was defined on the other side of the source... but the answer is simple - just don't do it. 

The only way to make a language which does not allow people to write horrible programs, is to remove all language features.  Any feature can be potentially abused.  At some point you have to trust the programmer.  C/C++ is all about giving the programmer trust and control to do what he or she wants.

By the way, think about example 2 type problems carefully, for code, not data, you can easily come up with more exotic examples (in the simple case given you could just use a function to generate the elements as they are all the same algorithm).

S. Tanna
Wednesday, June 04, 2003

In response to the original poster, private member variables are essential for proper encapsulation, *especially* for a library.  If the members are protected instead of private, you can not be sure of any invariants you set on them, because a subclass could change them in any way it pleases, or even provide public access to them via accessors.

Yes, I know in C++ private is not truely strict and there are ways to get around it, but I consider that a different issue.  Doing something to bypass private access security is obviously unsupported, whereas there is no reason to assume that accessing a protected member would be a bad thing.

I'm not sure what particular feature of C++ I would get rid of.  I have always been annoyed at some of the "features" caused by basing the language on C instead of starting from scratch.  Some examples have been mentioned here already (C preproccessor, header files which result in duplication of code AND more importantly exposure of implementation details).  But these are not things that you could just remove from the language, since they are fundamental to it.  IMO I would fault the general design philosophy of the language more than any particular feature.

Mike McNertney
Wednesday, June 04, 2003

> would fault the general design philosophy of the language more than any particular feature.

That's why you have a choice of languages. Pick one with the philosophy you like for your problem domain.

S. Tanna
Wednesday, June 04, 2003

My first gut response was "multiple class inheritance", but I'm afraid then that someone would graft on a different abomination which could do the necessary job of interface inheritance.

Failing that, I'd have to go with the guy who said to remove .H files.  I feel like I'm writing COBOL.  IDENTIFICATION DIVISION, ENVIRONMENT DIVISION, DATA DIVISION, OH- LETS- DEFINE- EVERYTHING - BEFORE-WE- USE- IT- BECAUSE- A- TWO- PASS- COMPILER- IS- TOO- MUCH- FOR- US DIVSION.

Alyosha`
Wednesday, June 04, 2003

Paulo, I agree with you.  If someone doesn't like the existence of certain features in C++ because the features are too difficult for them to understand, perhaps they'd be happier with a language like Java.  Please don't limit *my* access to features that I understand and can use.

Regarding getting rid of headers, how would you release your libraries to other developers?  How would they import your declarations into their source file?  How would you handle externalizing things that are internal by default such as class, struct, and const?  The nice thing about headers is their self-documenting nature.  I'd rather not have to run a source file (that could have errors in it) through a doc generator just to see a list of functions that the author intended to export.

Regarding macros, in my experience with C++, I can't think of a single instance where macros caused problems for me.  For all of the horror stories about macros changing the meaning of pieces of code, I've never seen it result in a bug in production code.  On the other hand, I've often seen bugs result from code duplication that could've been reduced by using macros.  The typical sources of problems with macros (for example, needing parenthesis around macro arguments when used in the macro) go away with a little experience and are usually caught by the compiler if they slip through.  Macros are certainly a last resort but when needed they do things that are impossible to do otherwise in a manner that isn't tedious, error prone, and repetitive. 

There's a nice breakdown of the uses for macros in D&E and a discussion of the alternatives that C++ provides (for example, a templated max function versus a max macro).  There are a few things you can do with macros that simply can't be done with other facilities of the language.  Until the language is updated to account for these things, macros are essential. 

SomeBody
Wednesday, June 04, 2003

I *like* header files because I can seperate out the portions of code that the user of my classes should see.

I'd say that one thing that would improve the language is to add a non-preprocessor include.

w.h.
Wednesday, June 04, 2003

Somebody, remember that the question was "What would you remove from /C++/, if you /could/".

I do not think any of the suggestions here stand a snowballs chance in hell to actually make it into C++, but it's nice to look at a language and find out what's good or bad about it.

As for the "use something else if you don't like it" part, I wish I could do that - unfortunately there's a whole infrastructure that needs to change if I wanted to do that. For my private stuff, I use Ruby, Java, Objective C and C++ - depending on what's appropriate. So I definitely find enough fault with C++ to sometimes switch :)

Groby
Wednesday, June 04, 2003

The original question was kinda silly, I admit. I actually appreciate the overall design for C++. C++ the language is just about as good as it can be given the design constraints the language creators were working with.

Every feature of the language is in there for a reason. That reason may just be "compatibility with C", but that's an important reason too.

As far as the original poster's "get rid of private" response, I'd have to say I disagree 100%. I'd get rid of protected, and keep private. Protected is effectly public - you have to document it, you can't change the semantics without breaking derived code, etc. So what does does limiting access to only derived classes actually give you? Might as well make them public and let everyone play.

As far as my own personal answer: I'd dump the locale stuff from the standard library. It's rediculously overcomplicated, hard to understand, and doesn't do the job it was intended for (easy internationalization). Get rid of it and start over.

Chris Tavares
Wednesday, June 04, 2003

Locale stuff is a great answer.  I'm not worthy to use the same computer as anyone who fully understands that stuff.

My personal answer (before someone suggested locales) was to get rid of the rule that the virtual call mechanism is turned off in constructors.  Yes, I understand why it has to be that way, but that is my pie-in-the-sky answer.  Offhand, I can't think of any good, detailed examples, but I know there have been times that I've ended up doing a two-stage "Construct then call Init()" because of that rule.

One-Armed Bandit
Wednesday, June 04, 2003

>As far as the original poster's "get rid of private"
>response, I'd have to say I disagree 100%. I'd get rid of
>protected, and keep private. Protected is effectly public -
>you have to document it, you can't change the semantics >without breaking derived code, etc. So what does does >limiting access to only derived classes actually give you?
>Might as well make them public and let everyone play.

for the user of the class (the one that instantiates a object of a class and calls some public method)
it is neat to encapsulate an interface and to leave the implementation part out.

For derived classes this restriction is meaningless, the derived and base class have a much stronger relationship, so i have a problem, encapsulation doesn't seem to help;

in other words, the derived class HAS to know about the implementation of the base class, whereas the client that instantiates the object and calls some method does not.

for the answer of 'keeping invariants' - the derived class can always get to the private data members in some ugly manner the thing is different with functions.

Now the uglyist thing i did in C++ was when i had
to cut and past the code of a private method from a base class, only because i needed the method and there was no other way of geting it done.

I thought that this should not have been the design goal of C++ - to make people cut and past code. isnt' it?

Michael Moser
Wednesday, June 04, 2003

Private methods may be one thing, but private data is another. Personally I feel both are essential, otherwise you are always tied to your implementation because someone may subclass it and use those members.  The whole point of encapsulation is so you can change implementations and people don't care about it.  When I write a class, I decide what can be accessed.  If that means you can't get at some functionality that I implemented and you want to use, oh well.  I probably did it that way so I can completely change or get rid of that functionality if I want to.

Your point about subclasses always being able to do something to get to private members is irrelevant.  So can any other code.  If you have the header for a class (or even if you know, the layout enough to find your variable) it is not difficult for any code to access private members of a class.  Private is there for the convenience of the programmer, not as a security measure.  If you break the semantics of the language to access private members, it's not my problem when it comes back to haunt you.  However if you break my class by subclassing it and using protected members that you shouldn't have access to in the first place, that *is* my problem because it is a perfectly valid (and expected) behavior for protected members to be access by subclasses.

Think about it this way.  If private doesn't exist, what is the point of even having protected?  Because if I want to access something directly, all I have to do is make a subclass that does it.  And since C++ has multiple inheritance I can do this with any number of base classes in your library.  You might as well just make everything public in that case.

If you agree that encapsulation is important, I can't see how you could do away with private.

Mike McNertney
Wednesday, June 04, 2003

You have the private / protected thing backwards.

Stroustrup himself says that he never should have added protected, apparently it was done to help out an early library implementor, but he now regrets it.

Also, most people seem to have come to the conclusion that exception specifiers aren't useful.

punter
Wednesday, June 04, 2003

> Now the uglyist thing i did in C++ was when i had
to cut and past the code of a private method from a base class, only because i needed the method and there was no other way of geting it done.

Well that actually suggests the base class was defined wrong.  They should have anticipated a derived class might want this functionality and made that member protected.

This is wondering on to a different topic, but given all programmers get stuff wrong (as exhibited by bugs in all non trivial programs), I'd suggest many classes put private where they went protected and vice-versa.  Whether it's better to err in one direction or the other probably depends on the problem/class... but it is why some folks tend towards use of protected instead of private so often.

S. Tanna
Wednesday, June 04, 2003

Without a doubt, the first to go should be header files.  No, they shouldn't be necessary.  Yes mandated .mak files might be necessary... they're a better idea than IDE hidden alternatives.

The second to go should be sneaky upcasts.

The third should be weird operator overloading rules.

John Aitken
Wednesday, June 04, 2003

I hate this idea of relying on a smart compiler.  It really just adds another huge layer of complexity and returns you to programming in 'speculative assembler'.  If the compiler can really reduce your code to some exactly equivalent more efficient alternative, then a good language should allow you to precisely define that equivalent concisely yourself.

OTOH I love the idea of relying on a very smart linker... because header files really are (or could be made) completely redundant logically.

John Aitken
Wednesday, June 04, 2003

>>
Stroustrup himself says that he never should have added protected, apparently it was done to help out an early library implementor, but he now regrets it.
<<

Do you have a citation for this?  I've read his books and a few interviews with him and never saw a mention of this.  All I can remember of him mentioning protected is describing its use in designing interfaces for derived classes. 

In fact, he answers the question "Are there any features you'd like to remove from C++?" in his FAQ with "Not really".

SomeBody
Wednesday, June 04, 2003

It's in D&E, if memory serves.

More about protected members than functions.

punter
Wednesday, June 04, 2003

Ah, I see what you mean.  I assumed you meant "protected" in general.  He's somewhat unclear in that section because he suggests that adding "protected" violated his "better judgement" but makes it clear that he's all for protected member functions.  It seems natural to me that protected data would be a bad idea for the same reason that public data is a bad idea, but necessary for the sake of symmetry.

SomeBody
Thursday, June 05, 2003

I'm discussing the header thing with Duncan one night and he says "It's the main thing that really bugs me about C++ - having to type all that stuff twice".

I reply "Oh, I don't bother anymore. I just put all the functionality in the header file, let the compiler sort the mess out. I only write code in the C file if it would cause a circular reference to have it in the H file."

He looks at me.

I go on "Makes refactoring easier, makes editing the code easier, makes searching the code easier... of course I can only do this at home because no workplace will buy me computers that are fast enough to cope with the increased rebuild times. Home projects have got a lot more productive lately though..."

But yes. This is the 21st century. Why for the love of everything digital can't we have a 2 pass compiler?

Katie Lucas
Thursday, June 05, 2003

Or if we can't have a two-pass compiler, can we at least let the computer do the work?

I stumbled across this:

http://www.hwaci.com/sw/mkhdr/index.html

while looking at the sqlite library. It's a neat approach to the problem - put everything in your .cpp file, and let the tool generate the headers automatically.

Unfortunately it doesn't handle templates. Oh well.

Chris Tavares
Friday, June 06, 2003

*  Recent Topics

*  Fog Creek Home