【问题标题】:How to build this c++ typelist into a variant?如何将此 c++ 类型列表构建为变体?
【发布时间】:2010-01-27 22:45:31
【问题描述】:

这里,

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

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

现在的问题是——我如何将它构建到一个变体类中?

【问题讨论】:

    标签: c++ templates variant typelist


    【解决方案1】:
    /*
     * 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
    

    【讨论】:

    • 复制并粘贴到 variant_modern.hpp 并免费使用 :-)
    • 顺便说一句,不需要删除堆上没有分配的内存,它使用placement new 运算符并将内容放在编译时分配的base_t _variant_holder
    【解决方案2】:

    正确但更高级的方法实际上是将值存储在 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();
        }
    };
    

    【讨论】:

    • set_to 是否必须调用前一种类型的析构函数?
    • 它当然应该 delete 以前存储的值 - 除其他外,示例中缺少清理。
    猜你喜欢
    • 1970-01-01
    • 2015-05-19
    • 1970-01-01
    • 2017-07-26
    • 1970-01-01
    • 2019-11-14
    • 1970-01-01
    • 1970-01-01
    • 2021-04-05
    相关资源
    最近更新 更多