Extreme Guidelines For Cee Plus Plus Discussion

A companion discussion page for ExtremeGuidelinesForCeePlusPlus.


Michael Feathers on how much to insulate

I also have the following process issue, which could become a page in itself:

Where is the tipping point? One part of me thinks that you should start with the full treatment:

I think this is way overkill. Some classes don't need factories. Some classes don't deserve interfaces. Think about ValueObject(s) for instance. Many classes are complete in themselves.

I would suggest one class or four or more classes. If there is only a single derivation of a class, don't create an interface or class factory. Once there are at least two derivations, create the common interface, a class factory, and the derived classes.

Not just inheritance - also composition and some forms of aggregation. For this reason I don't follow this as a rule - instead I factor out interfaces and facades to limit the depth/cycle-size of dependence graphs as described on MultipleInheritanceIsNotEvil etc.

An exception: often C++ classes get leverage by overloading templated decorator classes. For example you might use such a decorator to alter memory allocation rules, or to add ref-counting, streaming or hashing. Don't put these little helpers in files of their own if they only make sense in the context of their primary class.

Another part of me says (and this is what I lean to and have done piecemeal):

Yes. Start with all methods class-inline and take 'em out of line when they start getting larger than 5 lines or when they obtain more than one exit point.

Leave accessors and two-liners class-inline. That way they self-document. Also DontUseGetAndSet.

-- MichaelFeathers

Don't be afraid to move classes around to make your libs smaller. Doing template closures on large libs consumes much more time than you'd expect. Keep all your lib directories at the same level - don't build deep package hierarchies - so that it's easier to move classes around. I strongly recommend the use of an automated build environment like Makeit (http://www.dscpl.com.au) instead of the godawful by-hand settings in a crap tool like VS6. Many C++ developers have been spoiled rotten by IDEs ... when the IDEs don't do what you need, they still resist anything that isn't built-in. Screw 'em - make an auto-build environment and tell 'em to configure their IDEs to invoke it. Then it's their problem. -- PeterMerel

After you have your header files and modules well organized (approximately one class per header file, restrained inclusion of unnecessary header files from within headers), and you have a working compile-time dependency system (i.e. "make depend"), and you have your dynamically-linked shared libraries in place, stop worrying about compile-time overhead. Sure, it's not an interpreter, but that's half the point. The debugger is your interpreter, and the place to try quick inner-loop repetitions. A reasonable debugger will reload only the class libraries that have changed since the last run. Careful layering of code into ever-more specific class libraries might be key. I work with .5 MLOC of code divided into 50 libraries (spread over four vertically stacked source trees), and I'm very comfortable practicing my own variant of ExtremeProgramming. And at times it has scaled to teams of 3 or even 4 people :-). -- ScottJohnston

Only do what you need to when you need to do it. That's the XP way and it works for C++. The "tipping point" is different for different classes and often for different methods. Assume YouArentGonnaNeedIt and refactor as you go. This much of XP, at least, I have applied successfully to my own work. -- PhilGoodwin


Discussion about a starting point for future refactoring

Peter, responding to all your comments at once. I tend to agree, and I know that what you suggest is very good advice; stuff I've done. The thing that I'm trying to figure out is whether C++ programming has to be so inconsistent. What if I pull all functions into the .cpp files? Would I feel more comfortable changing things that would have been inlines? What if I avoided value passing and stack objects at all costs? Everything goes off the heap and you only have to forward declare in the header. When performance suffers, you pool objects or move back to values. I admit that this sounds crazy, even to me, but it is one extreme and I wrote it up to be controversial. The other, is to take it incremental, like the second bullet list.

What I'd like to see, is a way in C++ to say: "do it this way, all the time, and we will measure and move in less simple directions as we need to." The rub is that the initial condition needs to allow good migration to the more specialized forms. I hope that the second bullet list moves in that direction. We need to be able to develop criteria for refactoring the physical design as well as the logical design. It probably requires experimentation.

The very fact that C++ offers all these options and forces us to decide among them, makes me wonder about AlternativesToCeePlusPlus. But when I shake myself out of that, I wonder whether we can make set of patterns which allow us to deal with physical refactoring comfortably, migrating a code base back and forth across a continuum of well understood forms that allow us to be very open to logical refactoring at times, and very closed for speedy compilation at others. Sometimes an airplane needs its gear dropped; other times it needs it retracted.

-- MichaelFeathers (has been programming C++ for years and is re-examining a lot)

I understand your concern here Michael only too well. In fact it's this in chief that leads me to despair for C++ on AllPanaceasBecomePoison. You see I don't think it can properly be done. The best I can recommend is to stick to StlStyle - I believe that provides us with an unquestionably core dialect from which we need vary only very occasionally. But this isn't a complete answer.

The StlStyle page is somewhat weak. Can you add a bit more elaboration please? -- PhilGoodwin


Establishing dialect standards

One thing that's probably more important in C++ than any other language that might be amenable to XP is to establish dialect standards even before project standards. A good basis for this imho is the Ellemtel standard, now simplified and morphed into IndustrialStrengthCeePlusPlus. Taking this and STL as a basis provides you with a good starting point.

Such a point ought to be very useful to someone looking for ExtremeGuidelinesForCeePlusPlus. Not so useful for an old C++ hand who probably needs to define a standard himself. But apart from we small band of brothers how many of them are there in the world? -- PeterMerel

Yeah, I have many doubts also, but I'd like to give it a crack. If there turns out that there is no way of simplifying this decision process, and moving back and forth between nimble and rapidly compiled code, then Ron has all the more ammunition.

-- MichaelFeathers


Michael Feathers -- Starting Points for Reducing Compilation Cost

Starting from base principles.. the simplest thing that could possibly work.. and some of Phil and Peter's ideas, I've come up with a set of proposals for XP in C++. They err on the side of reducing compilation cost, because we all know that if you don't have much of it, it is easier to increase it later (for secondary goals of course). :-).

Please let me know where these initial conditions could cause a moderate sized project to go through grief:

Upsides: Compilation costs are as low as possible, and interface invariant refactorings are painless.

Downsides: Upfront work. Overuse of heap could push errors, but this can be ameliorated. Overuse of heap could cause performance to suffer, but with compilation costs down, you can move to values or pools more easily.

-- MichaelFeathers

Compilation Cost Comments

Mike, apologies if I seem harsh below - this might work for you, but I could never work this way.

These are far from the "simplest thing". They're things you're maybe gonna need. So that's two XP strikes against 'em. Also I think you're losing the fact that every class declaration is already an interface - effectively you're repeating that interface for every class, which violates OAOO - 3 XP strikes. Plus this way you're going to use a vtable for every class - that's far from acceptable to most C++ bods. Plus for at least 90% of classes I don't want a factory; I just use new plus ref-counting instead. So I feel you're going against the GrainOfTheLanguage with these three.

I understand why you want to do these if you do the above - and certainly I want to pass and return the most generic types that make sense.

I know why you want this, Mike, but I have to say it's a bad move. Bad, bad, bad move. If you're going to ever want ConstCorrectness - and you will - then you want to apply it from the get-go. Propagating consts after the fact is one of the least fun things you can do in C++. Plus, of course, if you're going StlStyle, then you're going to need ConstCorrectness in many spots or the compiler will kill you.

Actually, adding ConstCorrectness after the fact to a well-designed program is blazing fast, mostly limited by compilation and typing speed.


That's a lot of work just to maintain a redundant declaration. If I used the declaration of the class as its interface, then I never have to do this work. Plus if I blow away the derived class then do I have to take away the declaration again?

I guess you mean everything is passed by ref?

I don't understand why mixins should trouble XP.

Okay, this one I agree with. Also minimize the use of static members and singletons as much as possible. -- PM


I added a paragraph about mixins that directly contradicts the preceding paragraph on mixins - please feel free to delete it to restore consistency, but if you do, please also rename the page to ARandomAssortmentOfCplusplusPreferences. -- AndersMunch


Reference Counting

Ref-counting is mentioned a couple of places above, but never as the main point. Now that I think about it, that seems like a major oversight. In my opinion memory management deserves at least one bullet point. For example:

-- CurtisBartley

''ReferenceCounting is one of a dozen or so idioms that no C++ programmer should be without. But there are many projects where it isn't needed at all. there are bunches of resource management techniques and the correct one needs to be chosen for the given situation. On nice thing about C++ is that it doesn't pretend that memory management is the only resource management problem there is. I really miss having destructors that get called at well-determined times in Java for instance.

There are two questions here: Should you use ref-counting, and should you use smart pointers. I'd argue that there is a real benefit to using smart pointers whether you're going to ref-count or not: They're more exception friendly and they're more STL friendly. And if you're going to go to the trouble of using smart pointers, you might as well make them ref-count. Another way to look at is this: If you don't need smart pointers in your project, what is the cost if you use them anyway? (I'd argue it's negligible). -- CB

--- Limiting The Number of Language Features

In the spirit of DoTheSimplestThingThatCouldPossiblyWork, it might be interesting to get a book on C++ language circa 1989, and stick to the oldest language constructs until you really understand what you need of the new ones. There are a small number of semantic differences since then, but the language is for the most part backward compatible, and newer compilers will warn you when an old construct has been deprecated. You can live without features like exceptions, templates, dynamic casting, and even multiple-inheritance - all things that addressed perceived needs in the language, but also seem to have impacted its ease of use and degree of programmer satisfaction.

On the other hand, I would guess the C++ Standard Template Library gives you no choice but to immerse yourself in the latest language features. Unless you can find an alternate collection of common re-usable objects. NIHCL had Smalltalk appeal. I rely on the UnidrawFramework for all my lists, hash-tables, trees, ref-counting, etc. -- ScottJohnston

The newer language features really help... a lot. Ignore them at your (and those who follow) peril!! STL is a huge win. The NIHCL experiment proved that Smalltalk style inheritance doesn't work in C++. In fact, STL proves that inheritance isn't needed at all for reuse, templates work extremely well. In one library I did, the use of exceptions and autoptr halved the amount of code I had to write (I refactored about half way through). The biggest problem that I see with C++ is that it's very complex and many programmers don't bother to learn most of the features of the language... features that can actually simplify development and result in cleaner more usable code. The worst part about C++ for me is inheriting something from one of these guys/gals. I cannot say I've always liked C++, but I do (finally) respect it. -- lef


See RefactoringCppToReduceDependencies


Design Up Front??

Why did DesignUpFront creep into the ExtremeGuidelinesForCeePlusPlus? Maybe it is not DesignUpFront, but BuildInfrastructureBeforeApplications?. I haven't found this necessarily to be true, and I've found that most infrastructure bootstrappers relied on parallel development of at least sample applications to guide them (and took an evolutionary approach to creating their infrastructures as well). Isn't the reason people desired a static C++ infrastructure because they were not familiar with the balance of ExtremeGuidelinesForCeePlusPlus? For another take on evolving C++ base classes in parallel with derived classes, see [1]. -- ScottJohnston

I echo your concern Scott. I'd prefer to keep compilation costs down by whatever means possible and see if infrastructure motion becomes an issue, but I'm open to the possibility that it may not be possible. What I do know is that I haven't been ruthless enough about cutting down CostOfChange in C++ to see what the outer limits are. I'd like to try development in ExtremeFormsForCppCode to see just how low the CostOfChange can be. -- MichaelFeathers

Whether you call it DesignUpFront or BuildInfrastructureBeforeApplications?, there's clearly a substantial amount of YouAreGonnaNeedIt operating here. That may be OK; all these suggestions weren't just pulled out of peoples' hats - they're based on experience. In the Smalltalk world, YouArentGonnaNeedIt may apply close to 100% of the time, whereas in the C++ world, it may only apply 90% of the time. Lots of practical experience suggests that that 10% can cause you a great deal of pain that you could have avoided. I think the idea of using sample applications to guide infrastructure implementation is an excellent one, too. I look at this as not just testing your code, but also testing your design. In an Extreme process one way to approach this would be to choose the initial UserStories based on what's best for bootstrapping the infrastructure, and only allowing the users to prioritize in later iterations. -- CurtisBartley

I agree some amount of pre-existing infrastructure is needed for any successful project. And arriving at an initial collection of infrastructure takes more diligence in C++ than other object-oriented languages. But I see no reason why that infrastructure has to be static over the course of the project. Stop building infrastructure seems to suggest this. But then the first line says If you're doing XP you probably don't need this so maybe my point is moot.

There is a deeper issue here, about XP and common infrastructure that doesn't necessarily arise in the Smalltalk world. I've uncovered two viewpoints on this in the last year. Extremists say to ignore the design of the infrastructure (or application framework or reusable software) and focus on the immediate use case. Designists advocate a static and unchanging infrastructure. Both seem to unnecessarily constrain the programmer's way. -- ScottJohnston

Yes, I'd like to offer a middle ground between the two. Which is to not build infrastructure until you need to, but to then do a fairly thorough job of it. For instance, if you find that you need a class to translate between different string formats then predict which string formats you'll need to handle and deal with them all right from the start. The reason for this is that you'll likely be using this class all over the project and you don't want to have to recompile everything in the world just because you want to add some incremental improvement to your string translation class. It's a matter of balancing the risk of having to recompile and retest some amount of code against the risk of writing code that you'll end up never needing. There isn't a fixed rule that will make these decisions for you - you'll have to pay attention and think. -- PhilGoodwin

I agree there is value to a little forward thinking when laying down base classes, and that it is a highly subjective endeavour. And I'm willing to balance the risk of recompilation/retesting against having unused methods, because I see neither of them as risk factors worth worrying about. I get satisfaction from recompiling and retesting code, having once again verified the quality of the system, and perhaps removed a few more problems that went unnoticed at an earlier date. Unused methods have little impact on project success either, and can serve as reminders at a later date of the original base-class implementors intent. See also: RefactorSlack. -- ScottJohnston


C++ isn't XP Unfriendly in the first place

Some is made here, and more is made on a couple of other pages, about special techniques for running XP in C++. MichaelHill is currently running XP in C++ across a modest-sized project with four developers (200 classes, maybe 20 kloc or so). They are experiencing zero need for special C++ constructions. They have a fairly tight set of coding and naming conventions, but then, that's called for by XP already. We need a more thorough discussion of the fundamental problems that are not solved via (I hate to say this) 'normal' XP methods.

In particular, a distinction should be made between large, moderate, and small projects. Much of what appears on the source page seems potentially relevant to large-scale projects, but to a twenty-thousand line project it seems like a truly massive assertion of YouAreGonnaNeedIt. Perhaps it would be a good idea to rank our suggestions according to project size starting with smaller projects and adding more suggestions for larger projects on the assumption that they will still need the stuff from smaller projects. It's likely that the stuff from the smaller projects will be useful to all projects, not just XP projects.

CurtisBartley has worked on a couple of C++ projects on the order of 10 or 20 kloc of code and no more than a couple of people and didn't need to resort to anything special in order to get good turn-around times during development.

It is somewhat ironic that smalltalk XPers talk about not creating infrastructure when they are the biggest users of premade infrastrucure: the smalltalk library and environment. A large percentage of the "infrastructure" we make in C++ is just to get to the level of the smalltalk environment.

On larger projects it is far more efficient to standardize on an approach to things like: OS calls, GUI calls, task encapsulations, debug and log libraries, data structure libraries, messaging infrastructures, makefiles, build system, bug tracking system, source code control system, memory management libraries, etc. All this stuff is infrastructure and it would be madness to let each developer develop her own version of each.

''Don't take my comments on infrastructure to mean there should be none, or that its development shouldn't be centralized, organized, well-thought out. And don't assume because I still think infrastructure should remain mutable over the course of the project that I'm drawing my conclusions based on Smalltalk. I've been working with nothing but large scale C++ frameworks for the past ten years.

It's not that there should be no infrastructure or that it shouldn't be centralized, organized and well thought out. It's just that it must remain mutable over the course of the project. XP may be the first written-down methodology that gives permission to evolve and refactor where ever you need to, when ever you need to. Teams thrive on their ability to change things at all levels as new requirements arise. It is hard to imagine how you arrive at a healthy hierarchy of object-oriented code without doing this. Although, there is usually a distinction in practice between those who evolve base classes and those who evolve application specific classes. At least on C++ projects.

Contributors: MichaelHill CurtisBartley ToddHoff ScottJohnston PhilGoodwin

My experience, working on C++ projects with up to 200kloc, and up to 10 developers (I'm currently the sole developer on a project with over 100kloc of C++) is that turn-around time and infrastructure are not big issues. If you need find a need for infrastructure, you build it, and refactor the existing code to use it. Admittedly, it can be a pain if the current code is poorly factored, but then that's the payback for not keeping your code tidy, and you can still take SmallSteps, and RefactorLowHangingFruit first. -- AnthonyWilliams


Use LawOfDemeter

The things that lead to slow recompiles are things which lead to bad design anyway. Normal good design, with minimal inter-module dependencies, should be enough in most circumstances.

For instance avoid:

x = a.b().c().d().e().x();

because it requires the calling code to #include b.h, c.h, d.h and e.h. Instead put an x() function in class A, and isolate the dependencies in a.cpp.

But this is good design anyway, because the path through the data structure is a fragile thing.

Contributors: DaveHarris, PhilGoodwin


Don't use the dot operator

I often use the pimpl idiom, either to make a ReferenceObject or as compilation firewall. Since the dot operator can't be overloaded, this means either forwarding all method invocations, like so:

 class Cow_impl {
 public:
...
void moo() const;
void chew(Grass&);
 };

class Cow { SmartPtr?<Cow_impl> impl; public: ... void moo() const {impl->moo();} void chew(Grass& grass) {impl->chew(grass);} };

or overloading the arrow operator -> and changing all invocations of cow.moo() in your code to cow->moo().

Neither option is all that fun after you've done it a couple of hundred times. Therefore, you should overload the arrow operator to begin with for all user defined types and never use the dot operator for member access.

Note: This is ugly. For every user defined type you will almost always have two extra methods:

 class MyClass {
 public:
   MyClass* operator -> ();
   const MyClass* operator -> () const;
   ...
 };

There is something about this that doesn't look right and that's enough for me to say that it shouldn't always be done.

It smells like a template mixin waiting to happen -- DanilSuits

 // WARNING: uncompiled code
 template< typename T >
 class ArrowOp? {
 public:
   T* operator -> ();
   const T* operator -> () const;
 };
 class MyClass : public ArrowOp?<MyClass> {
   ...
 };


Start with all methods class-inline

I assume that this refers to classes which are isolated in an implementation file - if we are inlining in the header, that's likely to force increased compilation times -- DanilSuits

Disagree. Putting methods inline increases coupling. If one keeps methods separate from the header, one can use forward references rather than #includes. As soon as one tries to perform an operation on another class, the forward reference is no longer sufficient. -- WayneMack


Refactored this page. Made it smaller and converted some thread mode stuff to multi contributor. I would like to know more about WikiRefactoring from VolunteerHousekeepers. -- PhilGoodwin


CategoryCpp CategoryExtremeProgramming


EditText of this page (last edited April 9, 2008) or FindPage with title or text search