Question

One problem in large C++ projects can be build times. There is some class high up in your dependency tree which you would need to work on, but usually you avoid doing so because every build takes a very long time. You don't necessarily want to change its public interface, but maybe you want to change its private members (add a cache-variable, extract a private method, ...). The problem you are facing is that in C++, even private members are declared in the public header file, so your build system needs to recompile everything.

What do you do in this situation?

I have sketched two solutions which I know of, but they both have their downsides, and maybe there is a better one I have not yet thought of.

Was it helpful?

Solution

The pimpl pattern:

In your header file, only declare the public methods and a private pointer (the pimpl-pointer or delegate) to a forward declared implementation class.

In your source, declare the implementation class, forward every public method of your public class to the delegate, and construct an instance of your pimpl class in every constructor of your public class.

Plus:

  • Allows you to change the implementation of your class without having to recompile everything.
  • Inheritance works well, only the syntax becomes a little different.

Minus:

  • Lots and lots of stupid method bodies to write to do the delegation.
  • Kind of awkward to debug since you have tons of delegates to step through.
  • An extra pointer in your base class, which might be an issue if you have lots of small objects.

OTHER TIPS

John Lakos' Large Scale C++ Software Design is an excellent book that addresses the challenges involved in building large C++ projects. The problems and solutions are all grounded in reality, and certainly the above problem is discussed at length. Highly recommended.

Using inheritance:

In your header, declare the public methods as pure virtual methods and a factory.

In your source, derive an implementation class from your interface and implement it. In the implementation of the factory return an instance of the implementation.

Plus:

  • Allows you to change the implementation of your class without having to recompile everything.
  • Easy and foolproof to implement.

Minus:

  • Really awkward to define a (public) derived instance of the public base class which should inherit some of the methods of the (private) implementation of the public base.

You can use a forward declaration for class A that is referred to by pointer in another class B. You can then include class's A header file in class B's implementation file rather than its header file. That way, changes you make to class A will not affect source files that include class B's header file. Any class that wants to access class A's members will have to include class A's header file.

Refactoring and use pimpl/handle-body idiom, use pure virtual interfaces to hide implementation detail seems to be the popular answer. One should consider compile time and developer productivity when designing large systems. But what if you're working on existing large C++ system with no unit test coverage? Refactoring is usually out of the question.

What I usually do when I don't want the compiler to compile the world after I touched some common header files is to have a makefile/script to compile only the files I know need recompiling. For example, if I'm adding a non-virtual private function to a class, only the class's cpp file needs to be recompiled even when its header file is included by a hundred other files. Before I leave for the day, I kick off a clean build to rebuild the world.

None.

I see the point in using one, but I think the following arguments mitigate that point in many scenarios:

  1. Clarity comes first. If compromising clarity for runtime speed must be considered twice, what about compromising clarity for compile time speed?
  2. Private members shouldn't change so often.
  3. Usually, it doesn't take that long to rebuild everything.
  4. Faster tools will come in the future, so the compile speed problem will be automatically mitigated. Your code will not become automatically clearer.
  5. You should rebuild frequently anyway.
  6. Have you tried Incredibuild?

Of course in the end this is an economic decision. If the weight of "3" is important in your project and for some reason "6" cannot apply, then go ahead: you will win more from using these templates than you lose.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top