如何将这个c ++类型列表构建到变体中?

时间:2010-01-27 22:45:32

标签: c++ templates variant typelist

下面,

how do I fix this c++ typelist template compile error?

我们使用现代c ++设计中的代码构建了一个类型列表。

现在的问题是 - 如何将其置于变体类中?

2 个答案:

答案 0 :(得分:2)

/*
 * 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 :(得分:1)

正确但更高级的方法实际上是将值存储在 holder 类型中,该类型知道如何管理它包含的实际类型。

出于学习目的,更简单的方法是将类型映射到数字(即它们在类型列表中的位置)。通过它,您可以记住您当前在变体中存储的类型。

要获得该工作版本,您可能希望使用模板化构造函数和setter以及使用该类型编号映射的访问器函数。

相当简化它看起来像这样:

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