So I’m porting the graph packages from Ptolemy II to C++ and I’ve finally gotten to the point where I can unit test some of it. I was going to start writing tests for the Graph class but when I ran the suite I got errors about some missing implementation in the graph library. A couple of those were methods I did actually miss, but one of them turns out to be the result of multiple inheritance. When you read about multiple inheritance there’s always mention of the dreaded diamond. Well, after sketching out the inheritance tree I’ve discovered I have a case of the dreaded double diamond!
This is what the Java inheritance tree looks like. However, it should be noted that all of the Analyzer classes are Java interfaces, which means it makes perfect sense in Java. However, porting this over to C++ is a bit stickier. My initial response to it was “this is silly”, so I broke the Analyzer-Strategy connection and the CachedStrategy-GraphAnalyzer connection. This prettied things up quite a bit. However, now it’s come back to bite me in the ass!
Analyzer defines a pure virtual toString() method. CachedStrategy implements a virtual toSting() method but it is not overriding Analyzer’s because it is no longer deriving from Analyzer. SelfLoopStrategy also implements a virtual toString() method and I thought that this would work not only as an overriding method for the implementation in CachedStrategy, but also as the implementation required by Analyzer. But this is not the case. As it happens, elsewhere in the code, toString() is invoked on a GraphAnalyzer and it doesn’t know where to find the implementation. This makes sense after reading about the subtleties of multiple inheritance. The question I am faced with now is whether I want put the double diamond inheritance scheme back into the C++ code. It makes me a little uneasy is all, there’s this little voice in my head saying, “there’s got to be a better way!”
If I was to use the double diamond, I’m pretty sure the way to handle it would be for Strategy and GraphAnalyzer to inherit Analyzer virtually and then for CachedStrategy and SelfLoopAnalyzer to inherit from GraphAnalyzer virtually. This would give SelfLoopStrategy the responsibility of calling the constructors for GraphAnalyzer and Analyzer. Oh multiple inheritance gurus, have I got that right? Is there a better way?