Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Curious circular inheritance with mix-ins in C++

What is a good way to unscramble the circular inheritance here?

class Node {
   // ...
public:
   list<Node*> neighbors() { /* ... */ }
   void update() { }
}

template<class NodeType>
class HasImportance : public virtual NodeType {
   double m_importance = 0.0;
public:
   void receive_importance(double imp) { /* ... */ }
   void give_importance() {
      for (auto neighbor : this->neighbors())
         neighbor->receive_importance(m_importance /* ... */);
   }
};

class TrafficLight : public HasImportance<TrafficLight>, virtual Node {
public:
   list<TrafficLight*> neighbors() { ... }
   void update() { give_importance(); /* ... */ }
};

It fails (gcc 4.7.0) because TrafficLight is an incomplete type when HasImportance tries to inherit from it.

The real problem is that HasImportance needs to know the type returned by neighbors(). If HasImportance inherits from Node, then it thinks neighbors() returns a list of Node*, not TrafficLight*, and consequently doesn't know that it can call receive_importance() on the items. Similar problem if HasImportance doesn't inherit at all.

BTW, what I'm trying to do is make a few mix-ins to help define a variety of different kinds of graphs easily and to unit-test each mix-in separately. For example, I should be able to define the node class for a graph of traffic lights by just writing something like class TrafficLight : public HasImportance, HasState<3>, virtual Node { }.

I've come up with three ways to solve this, but all seem ugly. (1) static_cast<NodeType*>. (2) TrafficLight passes its this to HasImportance in its constructor. This way, HasImportance doesn't need to inherit at all; it just stores a pointer to (ahem) itself, and the template parameter provides the type of the pointer. (3) Make Node a class template, like this:

template<class NodeType>
class Node {
public:
   list<NodeType*> neighbors() { /* ... */ }
}

class TrafficLight : public HasImportance<Node<TrafficLight>> { /* ... */ }

That compiles and it doesn't introduce a gratuitous copy of this, but it seems…a little too curious.

Is there a code smell here? Should I approach these graphs in a completely different way?

like image 546
Ben Kovitz Avatar asked Jul 03 '12 12:07

Ben Kovitz


1 Answers

(3) but a bit differently.

template <class NodeType>
class Node { ... };

template<class NodeType>
class HasImportance : public virtual Node<NodeType> { ... };

class TrafficLight : public HasImportance<TrafficLight> { ... };

Looks entirely straightforward to me, not more curious than the CRTP itself.

like image 67
n. 1.8e9-where's-my-share m. Avatar answered Sep 23 '22 08:09

n. 1.8e9-where's-my-share m.