views:

288

answers:

2

Here,

http://stackoverflow.com/questions/2150618/how-do-i-fix-this-c-typelist-template-compile-error

we built a typelist, using the code from modern c++ design.

Question is now -- how do I take this and built it into a variant class?

Thanks!

+1  A: 

The proper, but more advanced, approach would be actually store the values in a holder type that knows how to manage the actual type it contains.

A simpler approach, for learning purposes, would be to map types to numbers (i.e. their position in the typelist). With that you can remember what type you are currently storing in the variant.

To get that a working version you'll probably want to have templated constructors and setters and also an accessor function that use that type-number mapping.

Quite simplified it could look something like this:

template<class TypeList>
class variant {
    unsigned type;
    void* value;
public:

    // ...

    template<class T>
    void set_to(const T& t) {
        STATIC_ASSERT(contains<TypeList, T>::value);
        // ... clean up previous value
        type  = index_of<TypeList, T>::value;
        value = static_cast<void*>(new T(t));
    }

    template<class T>
    const T& get_as() {
        STATIC_ASSERT(contains<TypeList, T>::value);
        if(type == index_of<TypeList, T>::value)
            return *static_cast<T*>(value);
        else
            throw type_error();
    }
};
Georg Fritzsche
does the set_to have to call the destructor of the previous type?
anon
it should of course `delete` the previously stored value - among other things, cleanup is missing in the example.
Georg Fritzsche
A: 
/*
 * variant_modern.hpp
 *
 *  Created on: Jun 4, 2010
 *      Author: vvenedik
 */

#ifndef _VARIANT_MODERN_HPP_
#define _VARIANT_MODERN_HPP_

struct NullType {} ;
template <class T, class U>
struct TypeList
{
    typedef T Head;
    typedef U Tail;
};

#define TYPELIST_1(T1) TypeList<T1, NullType> 
#define TYPELIST_2(T1, T2) TypeList<T1, TYPELIST_1(T2) > 
#define TYPELIST_3(T1, T2, T3) TypeList<T1, TYPELIST_2(T2, T3) > 
#define TYPELIST_4(T1, T2, T3, T4) TypeList<T1, TYPELIST_3(T2, T3, T4) > 
#define TYPELIST_5(T1, T2, T3, T4, T5) TypeList<T1, TYPELIST_4(T2, T3, T4, T5) > 
#define TYPELIST_6(T1, T2, T3, T4, T5, T6) TypeList<T1, TYPELIST_5(T2, T3, T4, T5, T6) > #define TYPELIST_7(T1, T2, T3, T4, T5, T6, T7) TypeList<T1, TYPELIST_6(T2, T3, T4, T5, T6, T7) > 
#define TYPELIST_8(T1, T2, T3, T4, T5, T6, T7, T8) TypeList<T1, TYPELIST_7(T2, T3, T4, T5, T6, T7, T8) > 
#define TYPELIST_9(T1, T2, T3, T4, T5, T6, T7, T8, T9) TypeList<T1, TYPELIST_8(T2, T3, T4, T5, T6, T7, T8, T9) >

namespace vlad
{
    namespace modern
    {

        template <class TL> struct MaxSize ;
        template <>
        struct MaxSize<NullType>
        {
            enum { value = 0 } ;
        } ;

        template <class Head, class Tail>
        struct MaxSize< TypeList<Head, Tail> >
        {
        private :
            enum { tailValue = size_t(MaxSize<Tail>::value) } ;
        public:
            enum { value = sizeof(Head) > tailValue ? sizeof(Head) : tailValue } ;
        } ;

        //TL Length
        template <class TL> struct Length ;
        template <>
        struct Length<NullType>
        {
             enum { value = 0 } ;
        } ;
        template <class Head, class Tail>
        struct Length< TypeList<Head,Tail> >
        {
            enum { value = 1 + Length<Tail>::value } ;
        } ;

        //TL IndexOf
        template <typename TL, typename T > struct IndexOf ;
        template <class T>
        struct IndexOf<NullType, T>
        {
            //if not found IndexOf == max_num_of_t
            enum { value = -1 };
        };

        template <class Tail, class T>
        struct IndexOf<TypeList<T,Tail>,T>
        {
                enum { value = 0 };
        };

        template <class Head, class Tail, class T>
        struct IndexOf<TypeList<Head,Tail>,T>
        {
          private:
                enum { nextVal = IndexOf<Tail,T>::value };
          public:
                enum { value = nextVal == -1 ? -1 : 1 + nextVal } ;
        };

        template <typename TL, unsigned int i> struct TypeAt ;

         template <class Head, class Tail>
         struct TypeAt<TypeList<Head, Tail>, 0>
         {
              typedef Head type;
         };

         template <class Head, class Tail, unsigned int i>
         struct TypeAt<TypeList<Head, Tail>, i>
         {
             typedef typename TypeAt<Tail, i-1>::type type;
         };

        template <typename  TL>
        class Variant
        {
        public :
            //compute the needed buffer size
            enum { max_size_t = MaxSize<TL>::value } ;
            enum { max_num_of_t = Length<TL>::value } ;
            //struct for alignment
            typedef struct { unsigned char buf_t[max_size_t] ; } base_t ;

            //default constructor
            template <typename T>
            explicit Variant() : _type_index(0)
            {
                new (&_variant_holder) TypeAt<TL, 0>::type() ;
            }
            virtual ~Variant() { } ; //{  _type_ptr->~T() ;  }

            template <typename T>
            explicit Variant( const T &t )
            {
                _type_index = IndexOf<TL, T>::value ;
                if ( _type_index == max_num_of_t )
                    throw std::bad_cast() ;
                new (&_variant_holder) T(t) ;
            }

            template <typename T>
            const T & get() { 
                std::size_t  type_index = IndexOf<TL, T>::value ;
                if ( type_index == max_num_of_t  || type_index != _type_index)
                    throw std::bad_cast() ;
                T * _type_ptr = reinterpret_cast<T *>(&_variant_holder) ;
                return *_type_ptr ; 
            }

        private :
            base_t _variant_holder ;
            std::size_t _type_index ;
        };

    } //namespace modern
}//namespace vlad


#endif /* _VARIANT_MODERN_HPP_ */
vladimir venediktov
Copy and paste into variant_modern.hpp and use it free of charge :-)
vladimir venediktov
BTW, no need to delete memory is not allocated on the heap, it's using placement new operator and places contents in compile time allocated base_t _variant_holder
vladimir venediktov