Topic: Template base classes and ANSI C++


Author: dalroth@my-deja.com
Date: Wed, 24 Jan 2001 14:57:13 GMT
Raw View
I'm using an interesting technique in my current application to
implement type safe maps for many subclasses without resorting to using
macros.

My requirements when I came up with this technique were as follows:
MAPs must be type-safe (no macros), implementation code must be
reusable over a wide range of maps (i.e. templates or macros), easy to
debug (no macros), memory efficient (maps must either be global, or
static), and finally thread safe (maps must be const).

After racking my brain for a while, I came up with a nifty little
technique that so far has worked quite well for me.  Here's some very
simple code to illustrate what I'm doing.  This first batch of code is
g++ AND VC++6.0 friendly.

(g++ test.cpp or cl /GR test.cpp)

01: #include <iostream.h>
02:
03: template<class T>
04: struct map_t
05: {
06:  void (T::*handler)();
07: };
08:
09: class interface
10: {
11: public:
12:  virtual void execute() = 0;
13: };
14:
15: template<class T>
16: class implementation: public interface
17: {
18: protected:
19:  static const map_t<T> map[];
20:
21: public:
22:  virtual void execute();
23: };
24:
25: class child;
26: class child: public implementation<child>
27: {
28: public:
29:  void Handler1() { cout << "Handler1" << endl; };
30:  void Handler2() { cout << "Handler2" << endl; };
31:  void Handler3() { cout << "Handler3" << endl; };
32: };
33:
34: template<class T>
35: void implementation<T>::execute()
36: {
37:  T *me;
38:  for (int i=0; map[i].handler; i++)
39:  {
40:    me = dynamic_cast<T *>(this);
41:    (me->*map[i].handler)();
42:  }
43: };
44:
45: const map_t<child> implementation<child>::map[] = {
46:  &child::Handler1,
47:  &child::Handler2,
48:  &child::Handler3,
49:  0
50: };
51:
52: int main(int argc, char **argv)
53: {
54:  child c;
55:  c.execute();
56:  return 0;
57: };

Ok, well here's something a LITTLE different.  This ONLY works with
Visual C++ 6.0 as far as I can tell (cl /GR test.cpp):

01: #include <iostream.h>
02:
03: template<class T>
04: struct map_t
05: {
06:  void (T::*handler)();
07: };
08:
09: class interface
10: {
11: public:
12:  virtual void execute() = 0;
13: };
14:
15: template<class T>
16: class implementation: public interface
17: {
18: protected:
19:  static const map_t<T> map[];
20:
21: public:
22:  virtual void execute();
23: };
24:
25: class child;
26: class child: public implementation<child>
27: {
28: protected:
29:  void Handler1() { cout << "Handler1" << endl; };
30:  void Handler2() { cout << "Handler2" << endl; };
31:  void Handler3() { cout << "Handler3" << endl; };
32: };
33:
34: template<class T>
35: void implementation<T>::execute()
36: {
37:  T *me;
38:  for (int i=0; map[i].handler; i++)
39:  {
40:    me = dynamic_cast<T *>(this);
41:    (me->*map[i].handler)();
42:  }
43: };
44:
45: const map_t<child> child::map[] = {
46:  &child::Handler1,
47:  &child::Handler2,
48:  &child::Handler3,
49:  0
50: };
51:
52: int main(int argc, char **argv)
53: {
54:  child c;
55:  c.execute();
56:  return 0;
57: };

You really have to look to see the difference, so I'll point them out.

Line #28:
  sample #1: public:
  sample #2: protected:
Line #45:
  sample #1: const map_t<child> implementation<child>::map[] = {
  sample #2: const map_t<child> child::map[] = {

That's it.  But those two difference make ALL the difference in the
world.  If I swap line #45 in sample 2, I have to also switch line #28
otherwise the test program won't be able to access the protected
members.

Now, I can understand why I would have to declare the static map in
terms of the base class (since it's a part of the base class), but why
can I define it in terms of the child class when using VC++?  Which is
correct according to the ANSI C++ standard?

The problem is, one of my child classes is actually a template itself
(template class: inherits from template class).  The thing is, the
template child is itself!  Yes, that's right, here is an example of
what I'm talking about (class child is actually no longer a part of
this code with these changes):

  template<class T>
  class implementation: public interface
  {
    protected:
    static const map_t<implementation> map[];

And, keeping in mind this ONLY works with VC++, here would be the new
map definition:

  template<class T>
  const map_t< implementation<T> > implementation<T> :: map[] = {
  etc. etc.

This works fine using MSVC, but g++ doesn't like it.  Why do this?
Well, the code is already written and debugged using maps, so if I
want to add some new functionality in I just add a new map entry and a
new handler and I'm done.  I can work around it, but I find it an
intellectually stimulating challenge to solve it this way.

(the actuall implementation of this is a combination of both
techniques, each serving a diferent purpose).

Anyway, I can't quite seem to get the syntax right with g++.  The
killer is defining the map in terms of the templated class.

VC++ being more expressive than g++?  I must be drinking too much
lately...


Sent via Deja.com
http://www.deja.com/

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]