"generic" union or something

This is a discussion on "generic" union or something within the C++ Programming forums, part of the General Programming Boards category; edit: forget my previous text - it was probably just confusing here is the new version: my question is just: ...

  1. #1
    Registered User
    Join Date
    Aug 2001
    Posts
    244

    "generic" union or something

    edit: forget my previous text - it was probably just confusing
    here is the new version:

    my question is just: is this solution "safe" (so would it work with objects of arbitrary type)? or is there something i missed out.
    especially these static template functions look a bit strange in my eyes.
    (they either call the destructor or copy constructor)
    since the type gets "lost" after assignment, template functions which handle destruction and copying of the object are instanciated, and function pointers are kept to them. i wonder if that is safe... (see source code below)


    so here is the story:

    i ran into the problem that i have a container (a stack to be explicit) which needs to hold elements of multiple types. so usually i would use a union for that:

    Code:
    union MyUnion {
      char *p;
      double d;
    };
    
    std::stack< MyUnion > my_stack;
    the problem: unions cant hold elements with a constructor - so placing e.g.
    iterators into a union is not really possible.

    of course it would be possible to put a pointer to that iterator into the union.
    but since iterators are lightweight objects (only a few bytes in size) it would be pretty inefficient creating a copy of it via new and storing the pointer in the union.

    so i want to store the e.g. iterator itself as a stack element.

    my solution was creating the following class
    which allowed the following:

    Code:
    --- file: main.cpp
    #include <iostream>
    #include <stack>
    #include "union.h"
    
    int main()
    {
      typedef Union<128> MyUnion;
    
      typedef std::stack<MyUnion> MyStack;
      MyStack ms;
     
      ms.push(MyUnion(double(3.14)));
      ms.push(MyUnion(int(2)));
      ms.push(MyUnion(std::string("hello world\n")));
    
      std::cout << ((std::string&)ms.top()) << "\n";
      ms.pop();
    
      std::cout << ((int&)ms.top()) << "\n";
      ms.pop();
      
      std::cout << ((double&)ms.top()) << "\n";
      ms.pop();
    
      return 0;
    }

    output
    Code:
    [Union] assigning an object of type d
    [Union] using reference cast
    [Union] creating a copy of type d
    [Union] deleting an object of type d
    [Union] assigning an object of type i
    [Union] using reference cast
    [Union] creating a copy of type i
    [Union] deleting an object of type i
    [Union] assigning an object of type Ss
    [Union] using reference cast
    [Union] creating a copy of type Ss
    [Union] creating a copy of type Ss
    [Union] deleting an object of type Ss
    [Union] deleting an object of type Ss
    [Union] using reference cast
    hello world
    
    [Union] deleting an object of type Ss
    [Union] using reference cast
    2
    [Union] deleting an object of type i
    [Union] using reference cast
    3.14
    [Union] deleting an object of type d
    [Union] assigning an object of type i
    [Union] deleting an object of type i
    note that the attributes of MyUnion are just an array where the object is kept in (in this case: int, double, std::string) and 2 function pointers.

    accessing the object works via conversions (instead of union.member of ordinary c-unions)
    (so instead of my_union.as_string its (std::string)my_union
    but this is not much of a problem - either you must remember which is the right member - or what is the right conversion.


    here is the source code of the "union"

    Code:
    --- file: union.h
    #ifndef __UNION_H
    #define __UNION_H
    
    // Tue, Dec 6, 03:17 - alpha done!
    
    /**
      i guess this is a pretty unsafe class - but yet useful in some situations
      where the ordinary c-union fails.
    
      some rules:
      .) Union u; u = OBJECT THAT IS NOT A UNION;
         this causes u to hold a COPY of that OBJECT
      .) Union u; u = ANOTHER UNION
         this causes u to hold a COPY of the OBJECT containd in the OTHER UNION -
         NOT THE OTHER UNION ITSELF!!!
      .) before ANY access-conversions are valid, an object needs to be assigned
         to the union.
         so there needs to be a Union u = some object; statement before
         e.g. (int&)u is valid. doing so without the assignment raises
         an exception
    
      well... exceptions have been removed here...
    **/
    
    #include <typeinfo>
    
    
    template <unsigned SIZE> class Union {
     protected:
      typedef char Data[SIZE];
    
      Data data;
      void (*pfn_destroy )(Union &r_union);
      void (*pfn_copy    )(Union &r_dest, const Union &r_src);
    
     public:
      Union() :
        pfn_destroy(NULL),
        pfn_copy   (NULL)
      {
      }
    
    
      Union(const Union &r_other) :
        pfn_destroy(NULL),
        pfn_copy   (NULL)
      {
        r_other.pfn_copy(*this, r_other);
      }
    
    
      template <typename T> Union (const T &r) :
        pfn_destroy(NULL),
        pfn_copy   (NULL)
      {
        this->operator = (r);
      }
    
    
      template <typename T> static void destroy(Union &r_union)
      {
        std::cout << "[Union] deleting an object of type "
    	      << typeid(T).name() << "\n";
        reinterpret_cast<T*>(&r_union.data[0])->~T();
    
        r_union.pfn_destroy = NULL;
        r_union.pfn_copy    = NULL;
      }
    
    
      template <typename T> static void copy(Union &r_dest,
    					 const Union &r_src)
      {
        std::cout << "[Union] creating a copy of type "
    	      << typeid(T).name() << "\n";
    
    
        T*       p_dest_data = reinterpret_cast<T*>      (&r_dest.data[0]);
        const T* p_src_data  = reinterpret_cast<const T*>(&r_src.data [0]);
    
        new(p_dest_data) T(*p_src_data);
    
        r_dest.pfn_destroy = r_src.pfn_destroy;
        r_dest.pfn_copy    = r_src.pfn_copy;
      }
    
    
      Union& operator = (const Union &r_rhs)
      {
       // destroy old content
        if(NULL != pfn_destroy)
          pfn_destroy(*this);
    
    
        if(NULL != r_rhs.pfn_copy)
          r_rhs.pfn_copy(*this, r_rhs);
      }
    
    
      template <typename T> T& operator = (const T& r_rhs)
      {
        // destroy old content of data
        if(NULL != pfn_destroy)
          pfn_destroy(*this);
    
        // resize data
        if(sizeof(T) > sizeof(data)) {
          std::cout << "[Union] assignment failed: object too big\n";
          // throw some exception
        }
    
        std::cout << "[Union] assigning an object of type "
    	      << typeid(T).name() << "\n";
    
        // use placement new in order to create a new object
        // <todo> note that alignment is not considered here... </todo>
        new(&data[0]) T(r_rhs);
    
        // set up the delete function
        pfn_destroy = &destroy<T>;
    
    
        // set up the copy function
        pfn_copy = &copy<T>;
    
        return *(reinterpret_cast<T*>(&data[0]));
      }
    
    
      template <typename T> operator T* ()
      {
        std::cout << "[Union] using pointer cast\n";
        return reinterpret_cast<T*>(&data[0]);
      }
    
    
      template <typename T> operator T& ()
      {
        std::cout << "[Union] using reference cast\n";
        return reinterpret_cast<T&>(data[0]);
      }
    
    
      ~Union()
      {
        // destroy content of data
        if(NULL != pfn_destroy)
          pfn_destroy(*this);
      }
    
    };
    
    
    #endif // __UNION_H
    Last edited by Raven Arkadon; 12-05-2005 at 07:58 PM.
    signature under construction

  2. #2
    Registered User
    Join Date
    Aug 2001
    Posts
    244
    ive changed the whole text to make the problem more clear
    bison is not mentioned anymore
    signature under construction

  3. #3
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    You may wish to have a look at Boost.Variant and Boost.Any. Your solution seems similar to Boost.Any but the Boost.Any source code seems to take a slightly simpler approach.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. sizeof union question
    By noops in forum C Programming
    Replies: 13
    Last Post: 06-06-2008, 11:56 AM
  2. Replies: 16
    Last Post: 10-29-2006, 04:04 AM
  3. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM
  4. Union declaration
    By arch_sal in forum C Programming
    Replies: 1
    Last Post: 08-05-2005, 06:28 AM
  5. Sorting a Union
    By andy in forum C Programming
    Replies: 4
    Last Post: 11-21-2001, 09:12 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21