Written by Bo Thorsen
2015/02/28
The object orientation guys have pretty much won the battle. So much, that when something doesn’t fit in a neat OO inheritance tree, it can’t be done. I recently worked on a commercial project where I saw the following code in production (simplified here, of course):
class A {
void init() {
// A lot of code;
};
class B { void init() ... /* Almost just like A */ }
class C { void init() ... /* Almost just like A */ }
The init methods were almost completely identical, but since A, B and C are unrelated classes, there is no simple solution to this problem, right? No, wrong.
There are at least three options here:
- Do the code duplication as they had done.
- Add a base class called Initializable and implement the init() there.
- Use template functions.
The company had used 1. as their solution, which is obviously bad.
Some OO developers would go for 2. as it fits the code. Maybe with the addition of a Template Method pattern (not to be confused with C++ templates). A true OO designer would immediately stop this idea, which might be exactly what had happened at my customer. Inheritance must be used to describe real inheritance, not to solve code issues. And actually, the code in the three init() methods did have a few variations, so private inheritance (with “using init;”) wouldn’t have been enough. But the point here is, just adding something to the inheritance tree for code sharing is in my opinion a horrible idea. Unfortunately, in many languages it’s the only option you have.
C++ templates to the rescue!
In C++, inheritance isn’t the only way to avoid code duplication, templates can do it as well. And for completely unrelated objects. The trick here is to write a set of template functions that each do parts of the init() method above, and let the A::init() and the other two call these functions:
template static inline void f1(T* t) {
// Do some of the work from the init() methods like this:
t->doSomething();
}
template static inline void f2(T* t) {
// Do some more of the work from the init() methods
}
...
void A::init() {
f1(this);
f2(this);
f3(this);
}
void B::init() { // No f1() needed in this class
f2(this);
f3(this);
}
This is a simple solution, that avoids all the unnecessary code duplication without breaking conceptual concepts like inheritance.
With a half decent compiler and not too long f1() and f2() methods, the assempler code generated by this C++ is actually exactly the same as in the code duplication. Many people have heard that templates lead to code bloat, which in some cases can be true, and in others is just wrong. In this case, the code bloat is identical to the original solution, as the compiler just inserts the template code in the place of the calls.
To seasoned C++ developers, this isn’t even a trick, it’s just a normal tool to solve a set of problems. But I keep seeing code that doesn’t use template functions to solve these problems. Remember this solution, and try to come up with other situations where it can be applied.