From: tmb@arolla.idiap.ch (Thomas M. Breuel)
Newsgroups: comp.lang.c++
Subject: Re: tagged unions, an alternative to RTTI (Re: run-time type checking)
Date: 30 Jul 92 19:28:07 GMT
	<1992Jul25.172045.24675@ucc.su.OZ.AU> <BryL9q.K5I@watcgl.waterloo.edu>
	<1992Jul28.183746.24287@ucc.su.OZ.AU>
	<TMB.92Jul29125322@arolla.idiap.ch>
Reply-To: tmb@idiap.ch
Followup-To: comp.lang.c++
Organization: IDIAP (Institut Dalle Molle d'Intelligence Artificielle
	Perceptive)
In-reply-to: tmb@arolla.idiap.ch's message of 29 Jul 92 16:53:22 GMT

I have been asked to post the following response for someone who can't
post from his site:

From: Deviasse Robert N <a228devi@cdf.toronto.edu>
Subject: Re: tagged unions, an alternative to RTTI (Re: run-time type checking)
Newsgroups: comp.lang.c++,comp.std.c++
Organization: University of Toronto Computing Disciplines Facility

Could you post the following to the net (I don't have have write access to
the net).

Tagged unions may be simulated in C++ by the method described in "The C++ 
Programming language 2nd Ed" page 168. The example is good, however, it
does not deal with the case of what to do if one of the fields in the union
has a constructor or destructor. If you have access to the class, and can
separate the struct-part (i.e. static members and functions, nonvirtual 
functions excluding the constructor and destructor) from the class part (i.e.
the constructor, destructor, and virtual functions), one can work around this 
problem by placing the struct-part in the union and re-attach the class part 
when necessary. This works, however it does not handle cases where this
separation is not possible. The method below is not much more complicated
but it should be generally applicable.

Anyway, while modifying a version of XLISP, I decided to add a Complex type
to the types available to XLISP. The only problem is that, the Complex type
I have is in a library and has a constructor, thus it cannot be included
in the union{} describing the XLISP node structure. I got around the problem
by declaring a char array in the union of size sizeof(Complex) and used
the necessary casts. It also needed to call the appropriate constructors
and destructors, It's ugly, but it works.
Thus (using you syntax):

   enum Tag {C,M,I};
   union Eg Tag {
     case C: Complex c; /* Has constructor. */
     case M: Matrix  m; /* Has constructor and destructor. */
     case I: int     i; /* Has neither. */
   };

would have to be rewritten as: 
(Please forgive any minor typos, I'm writting this from memory.)


   #define type_cheat(PointerType,pointer)   ((PointerType)(void*)(value))

   class Eg {
     public:
       enum Tag {C,M,I,UNDEFINED};
     private:
       Tag _tag;

       union {
         char _c[sizeof(Complex)];
         char _m[sizeof(Matrix)];
         int  _i;
       };

       void destroy(){
          switch(tag){
            case C: type_cheat(Complex*,&_c)->Complex::~Complex(); break;
            case M: type_cheat(Matrix*,&_m)->Matrix::~Matrix();    break;
            case I: /* nothing required to be done */ break;
          }
       }

       void enTag(Tag tag_){
         if (tag_!=_tag)
            destroy();
         _tag=tag_;
       }

       void checkTag(Tag tag_){
         if (tag_!=_tag)
            error();
       }
     public:
       Eg()  { _tag=UNDEFINED; }
       ~Eg() { destroy(); }

       /* getting member functions */
       Complex c() const { checkTag(C); return *type_cheat(Complex*,&_c); }
       Matrix  m() const { checkTag(M); return *type_cheat(Matrix*,&_m); }
       int     i() const ( checkTag(I); return _i; }

       Tag   tag() const { return _tag; }


       /* setting member functions  */
       void c(Complex c_) { enTag(C); new(type_cheat(Complex*,&_c)) Complex(c_); }
       void m(Matrix  m_) { enTag(M); new(type_cheat(Matrix*,&_m)) Matrix(m_); }
       void i(int     i_) { enTag(I); _i=i_; }
   };


Yes, it's verbose, but this is the type of contortions that the compiler
has to go through if tagged unions were added to C++ -- this is probably
the reason why tagged unions were not introduced into C++ (besides, what
is the error() function above, is it an exception, an assert(), ...)

It is possible to use downcasting to solve the above problem (instead of
using a union, one could derive a ComplexNode, MatrixNode, IntNode from
a general XLISP_Node), however, this would require a complete rewrite
of XLISP, particularly the memory manager.

I'll be very interested to know if there is a simpler solution.
Also, is the above method portable?