Как встроить этот список типов C ++ в вариант? - PullRequest
0 голосов
/ 28 января 2010

Здесь

как исправить ошибку компиляции шаблона списка типов c ++?

мы создали список типов, используя код из современного дизайна C ++.

Вопрос сейчас - как мне взять это и встроить в вариантный класс?

Ответы [ 2 ]

2 голосов
/ 24 августа 2010
/*
 * 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_ */

Вариант использования:

typedef modern::vlad::Variant<TYPELIST_2(int, std::string)> variant_t;
variant_t v (123) ;
int value = v.get<int>() ;
std::string tmp = v.get<std::string>() ; //throws exception
1 голос
/ 28 января 2010

Надлежащий, но более продвинутый подход - хранить значения в типе holder , который знает, как управлять фактическим типом, который он содержит.

Более простым подходом для целей обучения было бы сопоставление типов с числами (то есть их положение в списке типов). При этом вы можете вспомнить, какой тип вы сейчас храните в варианте.

Чтобы получить рабочую версию, вы, вероятно, захотите иметь шаблонные конструкторы и сеттеры, а также функцию доступа, которая использует это отображение номера типа.

Довольно упрощенно это может выглядеть примерно так:

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();
    }
};
...