Topic: What did I mean (Incestuous templates)?


Author: girod@dshp01.trs.ntc.nokia.com (Marc Girod)
Date: 08 Feb 1995 08:00:46 GMT
Raw View
>>>>> "Burger" == Burger / John Adriaan (ISE) <u865151@student.canberra.edu.au> writes:
In article <1995Feb5.165248.27747@csc.canberra.edu.au> u865151@student.canberra.edu.au (Burger / John Adriaan (ISE)) writes:

Burger> Note that to get it to compile on some systems, I needed to use:

Burger> class A;
Burger> class A : public Node<A> { ... };

So did I, on HP CC.

Burger> Like I said, as I was designing it it all made sense, but on
Burger> review I've succeeded in confusing myself - have I invented
Burger> the derived self-class?

No, that's hopefully ok.
This technique is mentioned in 'The Design and Evolution of C++', by
Bjarne Stroustrup (   15.8 Composition Techniques).

[ Sorry: the comments below do not suit anymore to the charter of this
group, please follow-up in comp.lang.c++ ]

I am using it heavily myself. To me, it allows you to introduce into a
class some generic features without releasing type tightness.
What I mean is that using plain inheritance usually is an alternative,
but a poorer one, leading to looser typing in two ways.

Suppose we make a Node class as a base for both A and B.

- Within Node, we loose the specificity of the actual class being A or
  B. This is not much since we cannot access (even from the template)
  any functionality they would not have in common. Something we might
  use however is friendship, to restrict access to the Node interface.
  Instead of making it public, using friendship, we could dedicate it
  to some NodeUser, and using the template form, more specifically to
  NodeUser<A> or NodeUser<B>, so that a NodeUser<A> could not use a
  Node<B>.
- Within A (and the same within B), we grant to our users, through the
  inheritance from Node, the right to handle an A* as a Node* (an A&
  as a Node&). Again, this is usually ok, apart when it comes to
  storing 'A's in a Container<Node> (or a database), from which the
  exact type is definitely lost. You will only retrieve from there a
  Node* and you'll need some kind of RTTI to get back your initial A*.
  Using the template alternative, the same cast does not bring this
  unwanted side-effect: Node<A> is no less specific than A.

As a matter of fact, I believe that this pattern is extremely powerful
and generic, and can show the path to techniques to support covariance
in type-safe ways.
--
+-----------------------------------------------------------------------------+
| Marc Girod - Nokia Telecommunications       Phone: +358-0-511 27703         |
| TL4E - P.O. Box 12                            Fax: +358-0-511 27432         |
| SF-02611 Espoo 61 - Finland              Internet: marc.girod@ntc.nokia.com |
|    X.400: C=FI, A=Elisa, P=Nokia Telecom, UNIT=TRS, SUR=Girod, GIV=Marc     |
+-----------------------------------------------------------------------------+




Author: u865151@student.canberra.edu.au (Burger / John Adriaan (ISE))
Date: Sun, 5 Feb 95 16:52:48 GMT
Raw View
This probably sounds stupid, but I wrote this with all the good
intentions in the world, but on reviewing it I realised it was pretty
incestuous and confusing, and as such I wonder if there is a "better way"!

What I wanted was a group of (unrelated) classes, all on their own lists,
without having to write the list code many times - naturally I used templates.
I knew there would only be one list per class, and also knew I wanted each
instance to self-insert into the list, and self-extract (also, access to
head, next member, etc.)

OK, on to the code... (I'm typing this from memory, so please take
intent, not compilation. This is stripped from what it really looks like
to just the bare minimum).

----------------------- START OF CODE --------------------------
template <class T>
class Node {

public:

   Node() : next(head), prev(0) {
      if (head) head->prev = this;
      head = this;
   } // Node

   ~Node() {
      if (prev) prev->next = next;
      if (next) next->prev = prev;
      if (head==this) head = next;
   } // ~Node

protected: // I actually used access members rather than protected data...

   T *next;

   T *prev;

   static T *head;

}; // Node

template <class T> Node<T>::head = 0; // This is instantiated for each class!

class A : public Node<A> { // Incestuousness here
}; // A

class B : public Node<B> { // And here!
}; // B
----------------------- END OF CODE -------------------------

Note that to get it to compile on some systems, I needed to use:

class A;
class A : public Node<A> { ... };

Like I said, as I was designing it it all made sense, but on review I've
succeeded in confusing myself - have I invented the derived self-class?

John Burger