GCC begins move to C++
From: | Mark Mitchell <mark-AT-codesourcery.com> | |
To: | GCC <gcc-AT-gcc.gnu.org> | |
Subject: | Using C++ in GCC is OK | |
Date: | Sun, 30 May 2010 17:26:16 -0700 |
I am pleased to report that the GCC Steering Committee and the FSF have approved the use of C++ in GCC itself. Of course, there's no reason for us to use C++ features just because we can. The goal is a better compiler for users, not a C++ code base for its own sake. Before we start to actually use C++, we need to determine a set of coding standards that will apply to use of C++ within GCC. At first, I believe that we should keep the set of C++ features permitted small, in part so that GCC developers not familiar with C++ are not rapidly overwhelmed by a major change in the implementation language for the compiler itself. We can always use more of C++ later if it seems appropriate to do so, then. For example, I think it goes without question that at this point we are limiting ourselves to C++98 (plus "long long" so that we have a 64-bit integer type); C++0x features should not be used. Using multiple inheritance, templates (other than when using the C++ standard library, e.g. std::list<X>), or exceptions also seems overly aggressive to me. We should use features that are relatively easy for C programmers to understand and relatively hard for new C++ programmers to misuse. (For example, I think constructors and destructors are pretty easy and hard to misuse.) Because C++ is a big language, I think we should try to enumerate what is OK, rather than what is not OK. But, at the same time, I don't think we should try to get overly legalistic about exactly what is in and what is out. We need information guidelines, not an ISO standard. Is there anyone who would like to volunteer to develop the C++ coding standards? I think that this could be done as a Wiki page. (If nobody volunteers, I will volunteer myself.) Whoever ends up doing this, I would urge the rest of us not to spend too much time in the C++ coding-standards bikeshed; we're not going to win or lose too much because we do or do not permit default parameters. -- Mark Mitchell CodeSourcery [email protected] (650) 331-3385 x713
Posted May 31, 2010 17:42 UTC (Mon)
by gregj (guest, #16266)
[Link] (22 responses)
Posted May 31, 2010 19:22 UTC (Mon)
by Ed_L. (guest, #24287)
[Link] (21 responses)
You do want to keep it simple though, and
I'm not certain I'd agree with Mark's contention its hard to misuse C++ constructors and destructors. He may have difficulty, but I've always seemed to manage just fine...
Posted May 31, 2010 19:28 UTC (Mon)
by johill (subscriber, #25196)
[Link]
Posted May 31, 2010 20:44 UTC (Mon)
by stijn (subscriber, #570)
[Link] (19 responses)
Posted May 31, 2010 21:27 UTC (Mon)
by stevenb (guest, #11536)
[Link] (5 responses)
Inheritance? Likely.
But that is what the main debate will be about: What code style and constructs should be used, and what should be avoided. I guess it will take some time to come to some kind of agreement...
Posted Jun 1, 2010 0:50 UTC (Tue)
by Ed_L. (guest, #24287)
[Link]
Bergeson's Second Law of Circuit Design:
Posted Jun 1, 2010 4:58 UTC (Tue)
by jgg (subscriber, #55211)
[Link] (2 responses)
When people start talking about limiting complex C++ features for whatever reason (too easy to abuse! too complex! etc) I generally point out the C++ standard library string class as a pretty stand out example of why this is a bad idea.
The class has an easy user interface, makes use of a great amount of C++ magic and is *dramatically* better than the C equivalence.
A good example for gcc might be the uint64_t type - to support certain build situations uint64_t is used sparingly. But you can make a completely transparent and code compatible uint64_t using a C++ class and a wack of operator overloading. How is this stuff like this not a good idea??
I was reading the thread and IMHO, people are focusing on the wrong thing. The best way to use C++ is to continue to micro optimize your fast/common cases but use fancy C++-ness to 'correctness' optimize the boring uncommon, infrequent, error, and unlikely cases.
Posted Jun 1, 2010 9:50 UTC (Tue)
by mjthayer (guest, #39183)
[Link]
I'm certainly no C++ guru, but I always feel that complex C++ features are a very slippery slope. Specifically, that you will always find situations in which any given feature makes a great deal of sense, but that you will also find many many more in which it seems to make sense but actually does more harm than good, in wasted development time, tricky bugs and hard-to-follow code. As long as they are only used by people qualified to write standard libraries and their ilk, all is well, but when common mortals (like me!) start to use them things can degenerate very fast.
Posted Jun 5, 2010 15:48 UTC (Sat)
by vonbrand (guest, #4458)
[Link]
The problem with stuff like C++'s strings or the proposed
You need magicians to write the magic code, and disciplined programmers who know that they shouldn't try their hand at magic... the last part is next to hopeless.
Posted Jun 1, 2010 18:49 UTC (Tue)
by sustrik (guest, #62161)
[Link]
Posted Jun 1, 2010 21:54 UTC (Tue)
by man_ls (guest, #15091)
[Link] (12 responses)
It is possible to do something like behavior encapsulation in C, with structures of pointers, but they are cumbersome and require too much plumbing. Maybe this is what you think is easily done in C.
Inheritance is the icing on the cake, since it allows specific behavior depending on the kind of data. I guess you could do something like it with C unions, but it would quickly lead to madness.
Posted Jun 1, 2010 23:54 UTC (Tue)
by anselm (subscriber, #2796)
[Link] (5 responses)
C++ started out as a glorified pre-processor for C, so it was originally about trying to contain the madness one would otherwise have to deal with when programming the same sort of thing in plain C.
This also proves that nothing the original C++ did (which includes most of the object orientation, inheritance, ...) was in fact impossible to do in plain C, since the C++ code had to be translated to plain C for the C compiler -- it was just messy as hell. I'm not a serious C++ programmer but I'm told that present-day C++ contains stuff that can no longer be feasibly translated to C.
Posted Jun 2, 2010 10:36 UTC (Wed)
by ncm (guest, #165)
[Link] (4 responses)
It appears that C++ was the first language first implemented in this way, but most languages that came after were done the same way, for the same reasons.
Posted Jun 2, 2010 11:18 UTC (Wed)
by anselm (subscriber, #2796)
[Link] (2 responses)
Whatever. The point is that the OOP things C++ does aren't impossible to do in plain C (see AT&T's 1980s-vintage cfront); they're on the whole just too much of a hassle to write down and keep track of in plain C, which doesn't mean that people won't try anyway (for interesting subsets of the functionality), nor does it mean that if they do they won't succeed.
Posted Jun 2, 2010 17:48 UTC (Wed)
by cry_regarder (subscriber, #50545)
[Link] (1 responses)
Posted Jun 2, 2010 22:26 UTC (Wed)
by anselm (subscriber, #2796)
[Link]
You can probably do functional or generic programming in assembly language, only (similar to the C++ vs. plain C situation) it is enough of a hassle that, on the whole, people don't bother these days because more convenient methods are available.
Posted Jun 3, 2010 10:17 UTC (Thu)
by HelloWorld (guest, #56129)
[Link]
Posted Jun 2, 2010 9:38 UTC (Wed)
by stijn (subscriber, #570)
[Link] (5 responses)
It depends on your expectations I think. I'll say that defining your
structures and the code that manages them close together is entirely possible in C, and in fact the prefered way. Not in the way that C++ does it, but taken on its own the statement applies to C. I am refering to bog-standard C by the way, nothing that tries to stuff each and every structure full of method pointers and/or uses a shower of preprocessing magic.
Posted Jun 2, 2010 10:07 UTC (Wed)
by dark (guest, #8483)
[Link] (4 responses)
Posted Jun 2, 2010 13:24 UTC (Wed)
by dgm (subscriber, #49227)
[Link]
Posted Jun 2, 2010 14:32 UTC (Wed)
by mjthayer (guest, #39183)
[Link]
Or you define a virtual parent class with just the public interface in a header file and the actual implementation (including its non-public members) in the implementation file. There is a negligeable price in efficiency of course, which should nearly always be worth paying.
Posted Jun 2, 2010 18:05 UTC (Wed)
by jwakely (subscriber, #60262)
[Link]
Posted Jun 2, 2010 19:37 UTC (Wed)
by man_ls (guest, #15091)
[Link]
It has always bothered me that I needed to add something in two places to get an accessible function. With a little perspective header files look like a way to make the preprocessor happier at the cost of developer time. Why C++ didn't fix this little detail is beyond me.
Posted May 31, 2010 19:35 UTC (Mon)
by ikm (guest, #493)
[Link] (12 responses)
Posted May 31, 2010 21:55 UTC (Mon)
by Lovechild (guest, #3592)
[Link] (11 responses)
I believe it might be a sign of the apocalypse, but what the hell, it sounds like a fun ride. Roll the movie.
Posted May 31, 2010 23:17 UTC (Mon)
by HelloWorld (guest, #56129)
[Link] (10 responses)
Posted Jun 1, 2010 3:27 UTC (Tue)
by jrn (subscriber, #64214)
[Link] (5 responses)
One of the merits of idiomatic C is that it is easy to guess roughly what is going to happen on the machine due to your code. I cannot imagine an experienced C programmer using
for (i = 0; i < strlen(line); i++)
except to prove a point, for exactly this reason.
Especially amusing was this:
> Unless countless very respected computer scientists and programmers around the world are completely wrong, we just cannot deny that non-abstract, low-level code tends to be quite unmaintainable. This is the more true the larger the program is.
> Torvalds not only admits here that C, by its very nature, leads you to create non-abstract, low-level code, that C gives you the mentality required to create such non-abstract code, but he actually claims that it's a good thing that this is so.
How does one explain the Linux kernel? Of course it includes abstraction where appropriate, just like most reasonably-sized C programs do.
Posted Jun 2, 2010 18:35 UTC (Wed)
by Cyberax (✭ supporter ✭, #52523)
[Link] (4 responses)
Nope, it's a great example. Because C programmers routinely use examples which no sane C++ developer will ever write to prove that C++ is BAD BAD BAD. Why shouldn't C++ developers do the same?
"How does one explain the Linux kernel? Of course it includes abstraction where appropriate, just like most reasonably-sized C programs do."
Linux is good because it's not actually quite that big. Most of code is in the drivers. And its infrastructure code if fairly compact and has quite clean abstractions.
Posted Jun 2, 2010 18:45 UTC (Wed)
by ikm (guest, #493)
[Link] (1 responses)
Maybe because they don't have a problem with C?
Posted Jun 2, 2010 18:47 UTC (Wed)
by Cyberax (✭ supporter ✭, #52523)
[Link]
Posted Jun 4, 2010 9:53 UTC (Fri)
by jrn (subscriber, #64214)
[Link] (1 responses)
Good point. I get annoyed when C programmers do it, too. :)
Posted Jun 4, 2010 10:06 UTC (Fri)
by dlang (guest, #313)
[Link]
Posted Jun 1, 2010 3:54 UTC (Tue)
by jamesh (guest, #1159)
[Link] (1 responses)
For git, a minor contributor was proposing changes that would negatively affect the core developers' ability to maintain the code base (since C++ is not one of their areas of expertise). In the gcc case, it is the core developers themselves who are suggesting the change and believe it will make maintenance easier.
Posted Jun 1, 2010 6:30 UTC (Tue)
by adobriyan (subscriber, #30858)
[Link]
Proposing? I don't think so.
> When I first looked at Git source code two things struck me as odd:
Posted Jun 1, 2010 9:08 UTC (Tue)
by ctg (guest, #3459)
[Link] (1 responses)
1) Real Programmers use C
While I have some sympathy with the statements, I think it is largely a question of taste.
Posted Jun 1, 2010 9:22 UTC (Tue)
by ikm (guest, #493)
[Link]
Note the word 'tend'. This problem is not with the language, but with the monkey coders. How many C++ programmers actually understand how virtual functions are implemented, for instance?
With GCC, the answer would actually be "all of them" :)
The Linus' solution is just not to use C++ and do all the dirty work the compiler would've done for you yourself. I like the GCC's one -- to limit the allowed subset and to be careful and thoughtful -- MUCH better.
Posted Jun 1, 2010 3:22 UTC (Tue)
by jrn (subscriber, #64214)
[Link] (43 responses)
Posted Jun 1, 2010 8:33 UTC (Tue)
by zorro (subscriber, #45643)
[Link] (42 responses)
Posted Jun 1, 2010 10:50 UTC (Tue)
by nix (subscriber, #2304)
[Link] (3 responses)
Posted Jun 1, 2010 11:52 UTC (Tue)
by Cyberax (✭ supporter ✭, #52523)
[Link]
Posted Jun 1, 2010 17:22 UTC (Tue)
by jwakely (subscriber, #60262)
[Link] (1 responses)
Posted Jun 2, 2010 11:38 UTC (Wed)
by nix (subscriber, #2304)
[Link]
Posted Jun 1, 2010 11:57 UTC (Tue)
by mjthayer (guest, #39183)
[Link] (19 responses)
This is an area where I still have a lot to learn, but my answer at this stage is that exceptions allow you to move error handling away from the place where the error occurs. The idea is that handling it somewhere else may make more sense, for instance because stretches of code littered with error handling can be harder to read. If methods called in a stretch of code throw exceptions instead of using return values to signal more-or-less fatal errors then you can enclose the entire stretch in a "try" block and deal with all the exceptions that might occur in a single "catch" block after the "try" block.
The main problem here is that the further away you get from the source of the error, the harder it becomes to deal with it properly - you have to distinguish what all the different exceptions you might catch actually mean and know what to do with each, and as you move your exception handling further away from the source, you end up potentially catching exceptions from more and more different places in one catch block, and being further and further away from the bits you need to clean up. Which in theory should be helped because of destructors getting called along the exception path, but this requires a lot of dilligence (especially on a project with lots of developers) to assure.
On the other hand, the chances of a random more-or-less-fatal error being handled correctly are pretty low in almost any project under most circumstances due to the amount of testing needed to achieve this.
Posted Jun 1, 2010 13:02 UTC (Tue)
by avik (guest, #704)
[Link] (18 responses)
Posted Jun 1, 2010 13:56 UTC (Tue)
by rev (guest, #15082)
[Link]
Posted Jun 1, 2010 16:10 UTC (Tue)
by mjthayer (guest, #39183)
[Link] (8 responses)
Things tend to get a bit messier though when you have to call non-exception-aware code, which in practice happens as soon as you start dealing with external APIs. One can write class wrappers around them of course, but that is only moving the issue slightly (it is easier in Java, where you get those wrappers as part of the environment!) Not to mention if you want to ensure that your object can survive an exception and remain in a sane state, although I admit that wanting that may be a sign that you are doing something wrong.
Posted Jun 1, 2010 16:23 UTC (Tue)
by avik (guest, #704)
[Link] (7 responses)
Posted Jun 1, 2010 16:30 UTC (Tue)
by mjthayer (guest, #39183)
[Link] (4 responses)
A random example of what seems tricky to me: you are creating some container class and have a method to store several elements into the container. An exception occurs half way through. It would be nice to roll back to the state you were in before the method was called, which is certainly doable, but not quite trivial either.
Posted Jun 1, 2010 16:45 UTC (Tue)
by avik (guest, #704)
[Link] (2 responses)
Posted Jun 1, 2010 18:45 UTC (Tue)
by mjthayer (guest, #39183)
[Link] (1 responses)
Certainly. I think I have even implemented this before, but for me it is not trivial in the sense that I would need to run tests on it to be confident that it worked as intended.
If one takes the point of view that exceptions should only be used for non-recoverable errors of course then this ceases to be relevant, and it is enough to ensure that the object can be cleanly destroyed, so no need to ensure a consistant state otherwise. I suspect that if the STL designers had taken this point of view, C++ would be used rather differently today.
Posted Jun 2, 2010 9:57 UTC (Wed)
by jwakely (subscriber, #60262)
[Link]
That point of view is unsuitable, impractical or unnecessary in a lot of code, especially in standard library components. The reasons why have been known for more than ten years,
> I suspect that if the STL designers had taken this point of view, C++ would be used rather differently today.
Yes, it would be used less.
Posted Jun 2, 2010 14:50 UTC (Wed)
by endecotp (guest, #36428)
[Link]
If your container is a linked list like std::list, you do it something like this (pseudo-code):
list tmp_list;
list::splice cannot normally throw, so to addition of the new items to the main list is atomic. If an exception occurs during the loop, the temporary list is destroyed during unwinding and the main list is never touched. splice() just involves a handful of pointer swaps so there is no efficiency issue with this code.
If your container is more like a std::vector it's a bit more verbose; I would write something like this (pseduo-code):
size_t old_size = main_list.size();
You could wrap that up into something more transactional-looking if you wanted (pseudo-code):
{
where transaction's ctor saves the old size and the dtor calls resize() unless commit() has been called.
> not quite trivial either
No it's not quite trivial, but I would hope that anyone who has got their "C++ programmer's license" would be able to do it.
Phil.
Posted Jun 2, 2010 16:40 UTC (Wed)
by HelloWorld (guest, #56129)
[Link] (1 responses)
Posted Jun 2, 2010 17:25 UTC (Wed)
by avik (guest, #704)
[Link]
Posted Jun 2, 2010 11:03 UTC (Wed)
by ldo (guest, #40946)
[Link] (7 responses)
avik wrote: Exceptions allow you to avoid this error cleanup and propagation code completely. Actually, it can be done more elegantly without exceptions. Heres a rewrite of your example: No gotos, and much easier to verify that everything will always be cleaned up no matter what path is taken, dont you think?
Posted Jun 2, 2010 11:35 UTC (Wed)
by avik (guest, #704)
[Link] (1 responses)
Posted Jun 2, 2010 13:01 UTC (Wed)
by mpr22 (subscriber, #60784)
[Link]
Posted Jun 2, 2010 12:11 UTC (Wed)
by farnz (subscriber, #17727)
[Link]
Actually, I much prefer avik's example to yours. As a competent C++ programmer who also knows C, I recognise the exception safety guarantee of auto_ptr, and I can guess that spinlock_t::guard provides the strong exception safety guarantee (an exception thrown that causes it to be destroyed returns it to the original state). I also know that the release method on auto_ptr returns the contained pointer and stops auto_ptr from deleting it.
As a result of this general C++ knowledge, and the assumption that the programmer hasn't been writing C in C++ (or otherwise behaved like a crack-addled monkey), I can see very quickly in avik's C++ example that nothing can leak. I can also see clearly what the code is meant to do.
In your version, I spend most of my mental energy figuring out what the code is meant to do - it looks like it's mostly error handling code, and the meat of the function is lost in swathes of boilerplate; as I'm normally concentrating on what the code should normally do, not what it does when there's an exceptional condition, I find the sheer quantity of effort I have to put in to read your code wasteful.
Of course, this is not to say that exceptions and C++ are perfect - whoever implements auto_ptr and spinlock_t::guard has to give me a suitable level of exception safety (no-throw or strong guarantees, possibly basic guarantee, to use the jargon). However, I personally find that it's not that hard to meet the needed guarantees for exception safety, and the resulting increase in code clarity is well worth the effort.
Posted Jun 2, 2010 12:54 UTC (Wed)
by gowen (guest, #23914)
[Link]
Posted Jun 2, 2010 15:51 UTC (Wed)
by gek (guest, #18143)
[Link] (1 responses)
Posted Jun 2, 2010 19:15 UTC (Wed)
by mjthayer (guest, #39183)
[Link]
Somehow I feel that this must also be an answer to the original question of "what is wrong with exceptions" (in C++). That they are sufficiently non-obvious to use that almost twenty years after they were first introduced electrons are still being spend explaining how to use them safely, and people still feel the need to debate on lwn.net whether or not they are a good thing. I can't put it down entirely to people being stuck in the past.
Posted Jun 3, 2010 1:43 UTC (Thu)
by jamesh (guest, #1159)
[Link]
While it is possible to write spaghetti code with gotos, they're also the most concise and readable way of implementing the equivalent of a "try {} finally {}" block in C. So there is no real reason to avoid their use in error handling of this type.
Posted Jun 1, 2010 15:53 UTC (Tue)
by dgm (subscriber, #49227)
[Link] (17 responses)
The basic problem with exceptions is that they can pop up anywhere, changing control flow. Doing this in an existing code base, and doing it right, is really _hard_, not unlikely trying to make existing code thread safe.
Think for instance:
this is a pretty innocent statement in C. Enter C++, where it can involve several constructors, destructors, method calls and overloaded operator invocations, all potentially throwing different kinds of exceptions, so you really should be doing:
Exception safe code doesn't happen by chance. You have to plan for it, be careful and program defensively, always asking yourself "what if an exception is thrown just here?". It doesn't matter that It cannot happen today, because code evolves and can very well happen tomorrow.
If you try to add exceptions as an afterthought the probability that you will miss several critical places is high.
Posted Jun 1, 2010 16:11 UTC (Tue)
by rev (guest, #15082)
[Link] (11 responses)
It's just that exceptions automate and hide what you would otherwise write explicitly: return error codes to a point in the call tree where the error is handled. They are a language construct that expresses exactly what you would otherwise have to tediously code.
In more recent langauges exception than C++ have the benefit of having to handle an exception: failing to do so is a compile error. Whereas with classic error handling it is easy to forget to deal with an error situation.
Posted Jun 1, 2010 16:36 UTC (Tue)
by dgm (subscriber, #49227)
[Link] (10 responses)
return codes don't change control flow. That's the reason avik had to introduce consider for instance:
This code does not leak memory in C, but can leak in C++ with exceptions.
Remember we are talking about a _large_ amount of C code that was not written with exceptions in mind.
Posted Jun 1, 2010 17:03 UTC (Tue)
by avik (guest, #704)
[Link] (2 responses)
1. Pick a function
If correctly applied, an entire code base can be converted with very little explicit try/catch blocks remaining.
Posted Jun 1, 2010 17:23 UTC (Tue)
by dgm (subscriber, #49227)
[Link] (1 responses)
Posted Jun 1, 2010 18:01 UTC (Tue)
by avik (guest, #704)
[Link]
Posted Jun 2, 2010 11:13 UTC (Wed)
by gowen (guest, #23914)
[Link]
Posted Jun 2, 2010 18:01 UTC (Wed)
by Cyberax (✭ supporter ✭, #52523)
[Link] (3 responses)
Is it better than a thrown exception?
Posted Jun 3, 2010 21:07 UTC (Thu)
by khim (subscriber, #9252)
[Link]
This is exactly why you CAN'T write such code in gcc. malloc is forbidden there at include file level. Instead you'll use xmalloc - it can not ever return NULL, so there are no need to check the error code.
Posted Jun 4, 2010 9:46 UTC (Fri)
by jrn (subscriber, #64214)
[Link] (1 responses)
On Linux, this is true but mostly irrelevant. Most systems overcommit (it is hard to find a saner thing to do if the user wants large programs to be able to fork() but does not provide the swap to back them) and malloc almost never fails.
Posted Jun 7, 2010 9:47 UTC (Mon)
by nye (guest, #51576)
[Link]
Posted Jun 3, 2010 5:44 UTC (Thu)
by njs (guest, #40338)
[Link] (1 responses)
You're assuming that g-written-in-C cannot fail, while g-written-in-C++ can, and then saying that that makes C better... which I guess is true given your assumptions, but if you have a method to write error-free code in C then you should share *that*, it'd be a much more compelling argument!
Posted Jun 3, 2010 16:41 UTC (Thu)
by dgm (subscriber, #49227)
[Link]
What I assume is that g-written-in-C cannot change the flow of control. That assumption doesn't hold in C++ with exceptions. That doesn't make C better, just different. Different enough, in fact, that converting C code straight to C++ is not that easy.
By the way:
@gowen: it's just an example.
Posted Jun 2, 2010 1:31 UTC (Wed)
by dvdeug (subscriber, #10998)
[Link] (4 responses)
Posted Jun 2, 2010 13:53 UTC (Wed)
by dgm (subscriber, #49227)
[Link] (3 responses)
You would be surprised. I recommend you to read this http://www.gotw.ca/gotw/020.htm
In this concrete example:
1. c() can indeed throw an exception in the code, but can also propagate uncaught exceptions from any code called within, possibly including constructors.
AFAIK, the only thing that cannot throw is "b".
Posted Jun 2, 2010 14:37 UTC (Wed)
by mjthayer (guest, #39183)
[Link]
I think the OP was talking about code directly converted from C, so those things should not apply.
Posted Jun 3, 2010 0:24 UTC (Thu)
by dvdeug (subscriber, #10998)
[Link] (1 responses)
a = copy_foo (add_foo (b, c()));
in C. If C++ would have thrown an exception, then a will have an error code in it (and a will have to support error values, and that value will have to be checked just like you have to catch an exception), a will have trash in it (and you'll have to check it--if you can) or the whole thing will crash, like if copy_foo assumes that add_foo can't return errors or trash.
Posted Jun 3, 2010 16:31 UTC (Thu)
by dgm (subscriber, #49227)
[Link]
That's basically the problem. Adding exceptions to an established code base changes basic assumptions programmers would probably have made (nobody in his right mind would lose time writing exception-safe code if your language has no exceptions!).
Posted Jun 1, 2010 11:50 UTC (Tue)
by rev (guest, #15082)
[Link] (17 responses)
It is very easy to abuse C++'s OO features. The most common mistake is using inheritance where it not appropriate. Particularly when the relationship between two concepts is 1:1, programmers coming from C tend to use inheritance where composition is in order.
For instance, a HTTP connection has a TCP connection. For C programmers it is tempting to make a class representing a HTTP connection inherit from a class representing a TCP connection.
How can you see that this is wrong?
Well, suppose one day you need a HTTP connection over some other transport mechanism. Unfortunately your HTTP connection is intimately tied to a TCP connection (being a subclass it is a special kind of TCP connection after all). You cannot have a HTTP connection over some other transport mechanism without rewriting your HTTP connection class. Suppose you want to make your classes suitable for IPv6. You not only need to modify your TCP connection class but also your HTTP connection class.
Rather, you should supply the HTTP connection class (through the ctor) with an object representing a transport layer connection. (I.e. use composition). It has methods to read and write data, but whether it is a connection over TCP over IPv4, IPv6, or over some other transport layer, you don't care about. (Well, you also are likely to need to find a common interface for setting connection parameters dynamically, BTW. Finding commonalities amongst simmilar concepts is a common meme in OO.)
I did not make the above example up. It comes actually from a C++ library from the GNU project. The name escapes me. Sorry to say it was terribly designed. To get at the headers of a HTTP request, one had to subclass a HTTP connection class. There was no class representing a HTTP request to which one could simply say `getHeaders()'. It had knowledge about IPv6 in several layers of the class hierarchy.
Another common mistake is inherit to data rather than behavior. Having a class 'ellipse' inherit from a class 'circle' is a classic example. Rather `circle' and `ellipse' should both inherit from a class `shape' having a pure virtual method `draw()' that is implemented differently in its subclasses.
Needless to say the above abuse gets worse with every layer of subclassing one uses.
Some guiding principles:
Posted Jun 1, 2010 16:34 UTC (Tue)
by Ed_L. (guest, #24287)
[Link] (5 responses)
The Guide *will* be interesting.
Posted Jun 1, 2010 17:04 UTC (Tue)
by Ed_L. (guest, #24287)
[Link] (2 responses)
C_Bar *pfoo;
will work just fine. But I *like* auto objects....
And yes, if used an Init() method may return a status flag rather than throw an exception.
Posted Jun 1, 2010 17:13 UTC (Tue)
by Ed_L. (guest, #24287)
[Link]
pfoo = new C_Bar();
(Its what I get for writing code fragments without a compiler...)
Posted Jun 2, 2010 18:27 UTC (Wed)
by Cyberax (✭ supporter ✭, #52523)
[Link]
std::auto_ptr<C_BAR> bar(new C_Bar());
...or something like it. It's exception- and leak-proof.
In one of my projects we had a 'no naked new' rule. So the result of EACH 'new' must be wrapped in a smart pointer (there were only a few carefully audited places where this policy was relaxed). Worked wonders.
Posted Jun 1, 2010 17:49 UTC (Tue)
by jwakely (subscriber, #60262)
[Link]
This (not uncommon) approach amuses me. Handling errors in constructors was one of the motivations for adding exceptions to C++
> because that requires a try block around the entire scope of the object.
Only if you need to catch the exception in the same scope as the object. Usually I don't need to do that and let the exception exit the current scope (with destructors doing whatever cleanup is needed)
As has been suggested, adding exceptions to a large codebase that wasn't written with them in mind can cause problems. New code can be made exception-safe from the start and so there is less reason to not use exceptions. Writing exception-safe code is *not* difficult, it's just different.
Posted Jun 2, 2010 19:55 UTC (Wed)
by rev (guest, #15082)
[Link]
I've become a little touchy on the issue having seen so many C programmers struggle and failing with OO and seen so many programmers when asked about code quality jump right into detail issues like naming conventions and where to place the opening brace. Significant issues but the overall issues are way more fundamental.
See my post elsewhere for a comment on the guidelines.
Posted Jun 1, 2010 22:45 UTC (Tue)
by Tobu (subscriber, #24111)
[Link] (1 responses)
Good guidelines. I especially agree with the ones about keeping variables private not making things mutable lightly.
I'd like to amend the rule about multiple inheritance though; besides an optional base class, it is perfectly fine to inherit from a number of mixins. For example, any class that deals with a non-copyable system resource (such as a socket) should add boost::noncopyable (or the local equivalent) to its parents. And I recall friends can help making data more private to the world at large, and should be allowed to help write things like output operators that are closely related to the class.
Posted Jun 2, 2010 20:18 UTC (Wed)
by rev (guest, #15082)
[Link]
Surely. These are guidelines. And my list is not complete I might add. I like to formulate them a bit strict. Firstly to make them clear. Secondly to steer programmers with little OO experience away from the abuse their instinct seems to drives them to. You are allowed to break the rules when you understand them a kind of being the motto.
There will always be situations where we have to break the rules. Rules cannot deal 100% with all the details a quirks of reality.
And, of course, there are always situations where the nature of the underlying problem is such that deviation from the rules is the required solution, such as building a class hierarchy, or using friends.
Posted Jun 2, 2010 18:50 UTC (Wed)
by ikm (guest, #493)
[Link] (7 responses)
And why is that, exactly?
Posted Jun 2, 2010 19:48 UTC (Wed)
by rev (guest, #15082)
[Link] (6 responses)
Posted Jun 2, 2010 19:54 UTC (Wed)
by ikm (guest, #493)
[Link] (5 responses)
Yes, that's exactly what I'm doing. What's wrong with it, exactly?
Posted Jun 2, 2010 20:09 UTC (Wed)
by ikm (guest, #493)
[Link] (2 responses)
What I want to say is, protected/private inheritance makes sense. A classic example is when you want to make an active object:
class Foo: QThread
You don't want start() and stop() to be exposed to the outside world, and yet you want it to be a thread (by being able to override the actual thread function).
Lastly, stating that something in the language is plain "meaningless" is implying that the people who agreed to add it to the standard are morons. This rarely makes sense.
Posted Jun 2, 2010 21:21 UTC (Wed)
by rev (guest, #15082)
[Link] (1 responses)
Your Foo class in your example is now a QThread except that it isn't: the outside world cannot call any of the methods that make it a QThread.
That you find yourself forced to use protected inheritance in your example is due to a modelling flaw in the QThread class. Not unlike the situation in which I found myself forced to subclass a HTTPConnection only to be able to get to the headers of a HTTP request. The QThread class should have something like a Worker member having the run() method. You implement a Worker (ie subclass it and implement the virtual run() function). You have a Qthread instance var, feed it your worker implementation, and call start() on your QThread.
A useful way, BTW, to find out whether you have woven different concepts into a single class, that thus should be decomposed into different classes is this:
Picture all the instance variables of your class and it superclass on one side. Likewise, picture all functions on the other side. Now draw a line between a function and a variable when said function uses that variable, If the resulting class now tends to consists of several unconnected graphs you have reason to consider decomposing your classes: the belong to the same hierarchy but appear to be unrelated.
Applied to the QThread example: the designers of the QThread should have asked themselves the question: is it desirable that an implementation of the run() function have access to the protected instance variables? The answer is no. Because QThread deals with the machinery of threading while the run() functions deals with matters of the user's problem domain, like reading messages from a queue. The stuff in the run() function is therefore another beast that needs to sit in its own class.
I find your 'moron' remark rather odd. Firstly, no language is perfect, no language designer is. Secondly, C++ was conceived at a time when understanding of OO was, by today's standards, rather limited.
Posted Jun 2, 2010 21:34 UTC (Wed)
by ikm (guest, #493)
[Link]
Maybe to the world, but not to him. To him, he is a werewolf. At night, when there is a full moon, he transforms into an unholy beast and hunts basic_strings. The world doesn't have to know.
Posted Jun 2, 2010 20:38 UTC (Wed)
by rev (guest, #15082)
[Link]
Please see my HTTP connection example in my post http://lwn.net/Articles/390256/ to see why this is, in general bad.
Posted Jun 2, 2010 20:57 UTC (Wed)
by rev (guest, #15082)
[Link]
To recapitulate:
When you write an HAS-A as an IS-A you really tied two concepts into one which are not.
Suppose you have class D: C
Suppose later you need another flavor of C, say B, or you have to rewrite D You will have to rewrite D and its relationship.
Now write:
class D {
You one day need a B. Introduce an abstract base class A for both B and C:
class D {
Now you have the advantage that you can easily change 'a' to be a B or a C.
BTW, there's people that take this advantage to the next level. They recognized that A has become a dependency of D. And rather than hard-wiring this dependency in your code created tools that allow you to specify in a configuration file whether 'a' should be an B or a C. Some day another kind of A is needed? Write a class E, deploy it, modify the config file and there you go.
Posted Jun 2, 2010 23:41 UTC (Wed)
by daglwn (guest, #65432)
[Link]
Most of these are good. A few comments and additional suggestions follow. This seems too strict to me. There are reasons to inherit beyond overriding virtual functions. The Curiously Recurring Template Pattern is a prime example (replacing dynamic polymorphism with static polymorphism). I think I know what you mean, but we have to be careful with how we define "class" or "object." A class does not necessarily have all of its functionality in member functions (see below). If I understand your meaning, this is good in every language. Factoring out common behavior is always important. If I've missed your point, please follow up. It's good to learn from others. I would go even stronger than that. Rather than make the variables protected, make the accessors (possibly getters/setters) protected. That way you minimize impacts of data member changes. This also seems overly restrictive. In general, yes, one doesn't need private or protected inheritance, but they have their uses, especially when composing behaviors from several classes (aha! see the next point :) ). This is by far my biggest pet peeve of most "good C++ practice" lists. MI is incredibly useful. What one usually doesn't want to do is create a diamond hierarchy where base classes have data members. The problem with data members in diamond hierarchies is the ambiguity of initialization. The most-derived class needs to take care of initializing base class data members, which breaks encapsulation. But MI is very, very useful for imparting properties and/or varying behaviors on a common class (template). Mixins are wonderful things. MI gets used all over the place when template metaprogramming is involved (c.f. Modern C++ Design, boost, etc.). I would add these guidelines: Example:
Example:
And my number one guideline:
Posted Jun 1, 2010 19:35 UTC (Tue)
by coriordan (guest, #7544)
[Link] (1 responses)
And if minimal C++ is better for gcc, other projects will consider it... Posted Jun 3, 2010 10:44 UTC (Thu)
by sylware (guest, #35259)
[Link] (1 responses)
A proper C++ compiler is a monster compared to a proper C compiler, and that without any optimization framework. I dislike my software being dependent on a piece of software that is that hard to rewrite. "A kernel is already enough" sort of illustrate my opinion. I know many other components have faulted (from my point of view) the same way, but I feel better to remove them than add more. Till gcc does not need a C++ compiler to compile its C compiler, I'll stay quiet. If the steering comity decided othewise and it does happen, I will start to depart from gcc or try to make gcc C++ parts needed for the C optimizing compiler optional. In the case I depart from it, I'll feel the pain of losing the optimization part of gcc, but I'll try my best to depart from it. There are not many alternatives, since I do contribute GNU GPL only code... Additionnaly, as I percieve it (I was a C++ coder), C++ helps too much (way more than C) coders to create brain damaged object orientish designs that ends up kludge/bloat. The mental dicipline in order to avoid such deviance can perfectly be applied to C too, then better be C which is easier to write a compiler for. This is a part of my personal perception of that specific issue which explains for a bigger part my choices and my behavior: for all software use cases, I'm looking to reduce as much as possible size/complexity of the software stack. I really dislike the feature freakness and "over abstraction" many coders are falling for (I try to avoid it myself, it's hard). Down to the C++ issue, actually I do feel the same way for all langages. C is the less worse choice in my opinion.
Posted Jun 3, 2010 13:50 UTC (Thu)
by mpr22 (subscriber, #60784)
[Link]
Posted Jun 3, 2010 17:20 UTC (Thu)
by cdmiller (guest, #2813)
[Link] (1 responses)
Posted Jun 3, 2010 21:09 UTC (Thu)
by daglwn (guest, #65432)
[Link]
The traditional UNIX paradigm of "everything is a file" is a fine example of OO design. What language it's implemented in is of little importance.
To me the important considerations for language choice do not include how powerful it is (they're all Turing complete in the end) but rather how well it maps to the problem domain and how many tools exist to help me find and fix problems.
Posted Jun 4, 2010 15:05 UTC (Fri)
by brinkmd (guest, #45122)
[Link] (2 responses)
It's a rebuttal of the C++ FAQ, and is a much more honest and useful explanation of the pitfalls of C++ than any other resource I found. If you ever thought while programming C++: "I can't be the first to try to do that, why is this so hard?", then the C++ FQA comes to the rescue.
Posted Jun 4, 2010 15:40 UTC (Fri)
by daglwn (guest, #65432)
[Link] (1 responses)
Yes, there are some issues with C++. Many are even being addressed in C++0x. But this "FQA" demonstrates a fundamental misunderstanding of the language, its design and its purpose. It's worse than useless. It will actually steer people in the wrong direction. Not just away from C++ (there are many cases where C++ is not appropriate) but away from sound technical analysis (maybe I don't need garbage collection) and into blind faith in highly dynamic systems.
Posted Jun 7, 2010 10:01 UTC (Mon)
by etienne (guest, #25256)
[Link]
GCC begins move to C++
Possibly. But in the sense that, as a long-time C++ programmer, were I to choose to volunteer my time between a C project and a (very) roughly equivalent C++ project, all else being roughly equal, I'd choose C++. Its not that I can't code in C, or can't maintain and moderately enhance existing C code, its just that I've been thinking OO for so long that for any new project and most additions to old, the first thing I think about (after defining purpose and goals) is how to encapsulate data and methods. Anymore restricting myself to C just seems too unnecessarily hard.
GCC begins move to C++
For example, I think constructors and destructors are pretty easy and hard to misuse.
GCC begins move to C++
I had to read that twice too, especially in the context, but I think he's agreeing with you :)
GCC begins move to C++
GCC begins move to C++
Templates? Only for basic data structures from e.g. STL.
Operator overloading? Unlikely.
I'm not involved with GCC and its been many years since I've even looked at their code. Still, its not inconceivable they might find some utility in typename templates. But its a slippery slope. GCC enlists some of the most practised C coders in the world, They obviously want to keep their C++ guidelines simple enough not to alienate productive members who simply Refuse To Go There.
GCC begins move to C++
"Keep it simple. If you cannot explain how your circuit works to a retarded chimpanzee, you will probably have difficulty explaining why, in fact, it does not work at all."
GCC begins move to C++
GCC begins move to C++
> The class has an easy user interface, makes use of a great amount of C++ magic and is *dramatically* better than the C equivalence.
GCC begins move to C++
uint64_t
use of "magic" is that as long as the magic doesn't leak out (i.e., the result is usable by mere mortals) everything is fine. As soon as the would-be magician looks into the book, caos as in the Disney classic Magician's Apprentice ensues.
GCC begins move to C++
GCC begins move to C++
Data encapsulation is done quite easily in C.
Of course data encapsulation is easy in C, but not data and methods encapsulation. That is the whole point of objects: define your structures and the code that manages them close together. In turn, each piece of data inside your structures may have associated methods to manipulate it.
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
C++ was never at any point in its history any sort of pre-processor for C.
It depends on whether you consider "C with Classes" to be part of the history of C++.
Stroustrup writes in hos book "Design and Evolution of C++" (page 27):
In October1979 I had a running preprocessor, called Cpre, which added Simula-like classes to C
However, that thing was replaced by Cfront not much later.
GCC begins move to C++
Of course data encapsulation is easy in C, but not data and methods encapsulation. That is the whole point of objects: define your structures and the code that manages them close together.
That's a good point, and you've put your finger on something that has been bothering me about C++. You see, C is actually better at this than C++. In C, I can declare a structure and its methods in a header file, and then define them close together in an implementation file. But when making a C++ class, I have to define the structure and the methods in separate places: the structure fields (= class members) in a header file and the methods in an implementation file. Unless I just put everything in the header file, but that has its drawbacks.
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
There are also other alternatives in C++, with their own pros and cons.
In this respect Java is definitely better than both: you define attributes and methods on the same file, and you don't need separate declarations.
Java advantage
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
http://warp.povusers.org/OpenLetters/ResponseToTorvalds.html
GCC begins move to C++
GCC begins move to C++
for (i = 0; i < strlen(line); i++)
except to prove a point, for exactly this reason."
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
> 1. Pure C as opposed to C++. No idea why. Please don't talk about
> portability, it's BS.
> 2. Brute-force, direct string manipulation. It's both verbose and
> error-prone. This makes it hard to follow high-level code logic.
GCC begins move to C++
2) C++ is about architecture, as much as code, and the architecture tends to be rigid. With C, you can ignore/change/adapt architectures within a project/timeframe.
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
Most error handling code does not actually handle the error, it merely undoes the effects of the code prior to the error and then propagates said error to the caller. Exceptions allow you to avoid this error cleanup and propagation code completely.
Example:
GCC begins move to C++
int f(struct foo *f)
{
int r;
struct bar *b;
b = kmalloc(GFP_KERNEL, sizeof(*b));
if (!b)
return -ENOMEM;
spin_lock(&f->lock);
r = g(f);
if (r)
goto out_free;
f->bar = b;
spin_unlock(&f->lock);
return 0;
out_free:
spin_unlock(&f->lock);
kfree(b);
return r;
}
becomes:
void f(foo *f)
{
auto_ptr<bar> b(new(GFP_KERNEL) bar);
spinlock_t::guard lock_guard(f->lock);
g(f);
f->bar = b.release();
}
Unlocking and freeing on error are automatic, and the function body size is greatly reduced.
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
Things tend to get a bit messier though when you have to call non-exception-aware code, which in practice happens as soon as you start dealing with external APIs. One can write class wrappers around them of course, but that is only moving the issue slightly
It's exactly the right solution to the problem. If, for example, you deal with file descriptors, you write a wrapper class that calls close() in its destructor.
Not to mention if you want to ensure that your object can survive an exception and remain in a sane state, although I admit that wanting that may be a sign that you are doing something wrong.
It's perfectly reasonable and doable to roll back to a sane state. It's pretty easy if you use RAII extensively.
GCC begins move to C++
Good example. You could implement this as follows (pseudocode):
GCC begins move to C++
void container::splice(container& other)
{
container::undo u(*this);
for (i in other)
u.append(this->append(i));
u.clear();
}
class container::undo {
public:
undo(container& c) : _c(c) {}
~undo() {
for (i in undo_list backwards)
_c.remove(i);
};
void clear() { undo_list.clear(); }
private:
container& _c;
list undo_list;
};
so, while adding items you also log them in an undo list, which is used to undo all that's been done on an exception.
GCC begins move to C++
GCC begins move to C++
http://www.boost.org/community/exception_safety.html
GCC begins move to C++
> store several elements into the container. An exception occurs
> half way through. It would be nice to roll back to the state
> you were in before the method was called
for (.....) {
tmp_list.push_back(....);
}
main_list.splice(main_list.end(),tmp_list);
try {
for (......) {
main_list.push_back(.....);
}
}
catch (...) {
main_list.resize(old_size);
throw;
}
transaction t(main_list);
for (......) {
main_list.push_back(....);
}
t.commit();
}
GCC begins move to C++
It's exactly the right solution to the problem. If, for example, you deal with file descriptors, you write a wrapper class that calls close() in its destructor.
The close system call can fail, therefore its return value must be checked. Now what do we do if we called close() from a destructor and it failed? We cannot throw an exception, because if another exception is flying, this cause the enviroment to call terminate(). So we can't really do anything except ignoring the error, which is also not acceptable. The consequence is that you must not close file descriptors in destructors.
GCC begins move to C++
Better Than Exceptions
int f
(
struct foo * f
)
{
int r = 0;
struct bar * b = NULL;
do /*once*/
{
b = kmalloc(GFP_KERNEL, sizeof *b);
if (b == NULL)
{
r = -ENOMEM;
break;
} /*if*/
spin_lock(&f->lock);
do /*once*/
{
r = g(f);
if (r != 0)
break;
f->bar = b;
b = NULL; /* so I don't free it yet */
}
while (false);
spin_unlock(&f->lock);
}
while (false);
kfree(b);
return r;
} /*f*/
Better Than Exceptions
I find it readable but gratuitously verbose... but then, I have recently been up to my armpits in the Nethack source code stripping out its support for pre-ANSI compilers and steam-driven System III / Version 7 Unices.
Better Than Exceptions
Better Than Exceptions
If you think code with a series of
Better Than Exceptions
do {
if(foo) break;
}
constructs can be described as structed with "no gotos" then you have massively failed to understand what's bad about goto's. Give me the
if(error) goto cleanup_and_exit;
.
.
return SUCCESS;
cleanup_and_exit:
free(ptr);
return -E_NO_CLUE_ABOUT_STRUCTURED_PROGRAMMING;
idiom any day.
This example reminds me of the "obvious solution" which leads you to the tar pit .
Better Than Exceptions
Better Than Exceptions
Better Than Exceptions
GCC begins move to C++
a = b + c();
try {
a = b + c();
} catch (exception * e) {
// STL exception
manage_standard_exception(e);
} catch (LibException * e) {
// unstandard exceptions thrown by a third party library
manage_lib_exception(e);
} catch (...) {
// catch everything else...
manage_unknown_exception();
}
GCC begins move to C++
GCC begins move to C++
auto_ptr<>
and RAII besides exceptions in his example above.
void f(void)
{
T * p = malloc (sizeof (T));
g(p);
free(p);
}
GCC begins move to C++
2. Modify its callers to expect exceptions from it (adding try/catch if necessary)
3. Convert it to throw exceptions instead of returning error codes
4. Modify it to expect exceptions from its callees (removing try/catch if necessary)
5. Repeat
GCC begins move to C++
GCC begins move to C++
How about...GCC begins move to C++
void f(void)
{
T p;
g(&p);
}
GCC begins move to C++
You are right of course...
malloc() failure
malloc() failure
GCC begins move to C++
GCC begins move to C++
@Cyberax: maybe g() can handle the NULL case gracefully, as it should if properly written.
GCC begins move to C++
GCC begins move to C++
2. + can be an overloaded operator, and thus can also throw.
3. = can need to create a temporary object, whose constructor can also throw.
GCC begins move to C++
> 1. c() can indeed throw an exception in the code, but can also propagate uncaught exceptions from any code called within, possibly including constructors.
> 2. + can be an overloaded operator, and thus can also throw.
> 3. = can need to create a temporary object, whose constructor can also throw.
GCC begins move to C++
GCC begins move to C++
For programmers going from C to C++ the most important issue is dealing with OO.
The coding standards are the least of your worries!
Sorry for the sermon. It's just that it gets to my nerves that when programmers talk about writing maintainable code they jump to the relatively minor issues showing to have a blind spot for the issues that really matter.
For the most part I agree, which is probably why Mark suggested limiting or forbidding inheritance in the first GCCC++ Guideline draft. (But I doubt it will fly). Composition vs. Inheritance is a basic question to all OO design, frequently aliased as "ISA vs. HASA". Prsumably, GCC already has some decent OO hacks to guide any OO "newbies" which in this particular context takes on a whole 'nother meaning as I 'spect the hardline GCC C hacks have eschewed OO because they are already aware of most of its pitfalls and simply don't want to deal with them. I expect some lively debate.
The coding standards are the least of your worries!
And related, all properties that are required to bring an object into a meaningful state should be supplied through the ctor.
Yabut here you run up against the "do you allow exceptions and where?" question,as well as your local definition of "meaningful state." Personally, I use exceptions. Except I try to avoid throwing from a constructor, because that requires a try block around the entire scope of the object. Rather, if an object needs to perform some exceptional operation in order to be initialized (e.g. allocating memory) I usually write a separate throwable Init() method that sets a private is_init flag to let all other methods know whether the object is ready to use. But (a) this isn't always possible, (b) I haven't investigated whether auto_ptr might be used to limit a constructor try block, and (c) YMMV anyway.
The coding standards are the least of your worries!
Yes, I meant the try block must surround the scope of any auto object whose constructor might throw. Certainly
try{
pfoo = new(C_Bar);
}catch(...){};
The coding standards are the least of your worries!
The coding standards are the least of your worries!
The coding standards are the least of your worries!
The coding standards are the least of your worries!
The coding standards are the least of your worries!
The coding standards are the least of your worries!
The coding standards are the least of your worries!
The coding standards are the least of your worries!
The coding standards are the least of your worries!
The coding standards are the least of your worries!
{
...
private:
virtual void run();
}
The coding standards are the least of your worries!
The coding standards are the least of your worries!
The coding standards are the least of your worries!
The coding standards are the least of your worries!
private:
C c;
}
private:
A a;
}
The coding standards are the least of your worries!
Some guiding principles:
class Base {
private:
virtual void doItImpl(void) = 0;
public:
void doIt(void) {
debug("Calling doIt!");
doItImpl();
}
};
class Foo : Base {
private:
void doItImpl(void) {
doSomething();
}
};
template<typename Key, typename Value>
struct MapGenerator {
typedef std::map<Key, Value> type;
};
template<typename Value>
struct MapGenerator<int, Value> {
typedef MyFancyEfficientIntMapper<Value> type;
};
typedef MapGenerator<double, std::string>::type DoubleStringMap;
typedef MapGenerator<int, std::string>::type IntStringMap;
C+
GCC begins move to C++
C inflicts syntactic salt on me every time I want to do arithmetic on ordered pairs of integers; C++ doesn't :)
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
GCC begins move to C++
http://yosefk.com/c++fqa/faq.html
For me, I thank brinkmd for this FQA link, worth a read.