zoukankan      html  css  js  c++  java
  • 第26课 可变参数模板(7)_any和variant类的实现

    1. any类的实现

    (1)any类:

      ①是一个特殊的,只能容纳一个元素的容器,它可以擦除类型可以将何任类型的值赋值给它

      ②使用时,需要根据实际类型将any对象转换为实际的对象

    (2)实现any的关键技术

      ①当赋值给any时,需要将值的类型擦除,即以一种通用的方式保存所有类型的数据。通常是通过继承去擦除类型基类是不含模板参数的派生类才含有所要包装对象的类型

      ②赋值时,创建派生类对象赋值给基类指针,派生类携带了数据类型,通过赋值兼容原则擦除了原始数据类型。当需要取数据时,再向下转换成派生类类型,转换失败时抛异常。

      ③由于向any赋值时,会使用原型模式创建出一个派生类对象,这里可以用unique_ptr智能指针来管理该对象的生命期。

      ④设计思路小结:Any内部维护了一个基类指针通过基类指针擦除具体类型any_cast时再通过向下转型获取实际数据。当转型失败时打印详情。

    【编程实验】any类的实现

    //Any.hpp

    #include <memory>
    #include <typeindex>
    #include <iostream>
    
    class Any  //注意Any不是一个模板类!但其成员函数可能是个模板函数。
    {
    private:
        //内部类
        struct Base;
        typedef std::unique_ptr<Base> BasePtr;
        
        struct Base  //基类不携带被包装对象的类型信息
        {
            virtual BasePtr Clone() const = 0; //设计模式之原型模式
            virtual ~Base(){/*std::cout << "virtual ~Base()" << std::endl;*/}
        };
        
        template<typename T>  //T为被包装对象的类型
        struct Derived : Base //派生类
        {
        public:
            T m_value; //被包装的对象
        public:
            template<typename U>
            Derived(U&& value) : m_value(std::forward<U>(value)){}
            
            BasePtr Clone() const
            {
                //由于返回堆对象,所以用智能指针来管理
                return BasePtr(new Derived<T>(m_value));
            }        
        };
    private:
        BasePtr m_ptr; //使用智能指针来管理被包装对象的生命期
        std::type_index m_tpIndex;
        
        BasePtr Clone() const 
        {
            return (m_ptr == nullptr) ? nullptr : m_ptr->Clone();
        }
        template<typename T>
        using type_origin = typename std::decay<T>::type;
        
    public:
        template<class U>
        bool Is() const
        {
            return m_tpIndex == std::type_index(typeid(U));
        }
        
        //注意:std::unique_ptr重载了operator bool()类型转换函数
        bool IsNull() const{return !bool(m_ptr);}
    public:
        Any(void) : m_tpIndex(std::type_index(typeid(void))){}
        Any(Any& that) : m_ptr(that.Clone()), m_tpIndex(that.m_tpIndex){}
        Any(Any&& that) : m_ptr(std::move(that.Clone())), m_tpIndex(that.m_tpIndex){}
        
        //其他类型的被包装对象value(即非Any类),则通过以下构造函数来构造。
        //一般需要先通过std::decay类型来移除value的引用和cv属性
        template<typename U, class = typename std::enable_if 
                                     <!std::is_same<type_origin<U>, Any>::value, U>::type>
        Any(U&& value): m_ptr(new Derived<type_origin<U>>(std::forward<U>(value))), 
                        m_tpIndex(std::type_index(typeid(type_origin<U>)))
        {
            //std::cout << "Any(U&& value)" << std::endl;
        }
        
        //将Any转换为实际的类型
        template<class U>
        U& AnyCast()
        {
            if(!Is<U>()){
                std::cout <<"can not cast "<<typeid(U).name() 
                          <<" to " << m_tpIndex.name() << std::endl;
                throw std::bad_cast();
            }
            
            auto derived = dynamic_cast<Derived<U>*>(m_ptr.get());
            return derived->m_value;
        }
        
        //重载赋值操作符
        Any& operator=(const Any& a)
        {
            if(m_ptr != a.m_ptr){
                m_ptr = a.Clone(); //unique_ptr指针不能直接赋值给另一个unique_ptr
                m_tpIndex = a.m_tpIndex;
            }    
            //std::cout << "Any& operator=(const Any& a)" << std::endl;
            return *this;
        }
    };

    //testAny.cpp

    #include <iostream>
    #include "Any.hpp"
    using namespace std;
    
    int main()
    {
        Any n;
        auto r = n.IsNull(); //true;
        cout << r << endl;
        
        string s1 = "hello world!";
        
        n = s1; //先将调用Any(U&&)将s1转为Any类,再赋值给n
        cout << n.AnyCast<string>() << endl;
        
        n = 100; //先将调用Any(U&&)将1转为Any类,再赋值给n
        cout << n.AnyCast<int>() << endl;    
        n.Is<int>(); //true;
        
        //n.AnyCast<string>(); //error
        
        return 0;
    }

    2. variant类的实现

    (1)variant类

      ①类似于union能代表定义的多种类型,允许赋不同类型的值给它。它的具体类型是在初始化赋值时确定的。

      ②variant用途之一是擦除类型,不同类型的值都统一成一个variant。如variant<int,char,double> vt;表示其可以代表三种类型,但具体哪一种,在初始化时确定。

    (2)实现variant的关键技术

      ①找出多种类型中size最大的那个类型,并构造一个内存对齐的缓冲区用于存放variant的值。(见IntegerMax<Types…>::value)

      ②类型检查和缓冲区中创建对象:包含检查赋值的类型是否在己定义的类型中(见Contains<T, Types>::value)以及缓冲区中创建、析构对象。

      ③通类型取值时,要判断类型是否匹配,如果不匹配,则打印详情。(见Get<T>函数)

      ④通过索引位置获取类型(IndexType函数)和通过类型获取索引位置(get<T>函数)

      ⑤使用访问者模式,定义访问variant各个类型的方法。(见visit函数)

    //function_traits.hpp:与上一节相同

    #ifndef _FUNCTION_TRAITS_H_
    #define _FUNCTION_TRAITS_H_
    
    #include <functional>
    #include <tuple>
    
    //普通函数
    //函数指针
    //function/lambda
    //成员函数
    //函数对象
    
    template<typename T>
    struct function_traits;   //前向声明
    
    //普通函数
    template<typename Ret, typename... Args>
    struct function_traits<Ret(Args...)>
    {
    public:
        enum {arity = sizeof...(Args)};//arity : 参数的数量
    
        //函数别名
        typedef Ret function_type(Args...); //<==> using function_type = Ret(Args...);
        
        typedef Ret return_type; //返回值类型
        using stl_function_type = std::function<function_type>;
        typedef Ret(*pointer)(Args...);
        
        //获取可变参数模板中第I个位置的参数类型。
        template<size_t I, class = typename std::enable_if<(I<arity)>::type>
        using args = typename std::tuple_element<I, std::tuple<Args...>>;
    };
    
    //函数指针
    template<typename Ret, typename... Args>
    struct function_traits<Ret(*)(Args...)> : function_traits<Ret(Args...)>{};
    
    //std::function
    template<typename Ret, typename... Args>
    struct function_traits<std::function<Ret(Args...)>> : function_traits<Ret(Args...)>{};
    
    //成员函数
    #define FUNCTION_TRAITS(...)   
    template <typename ReturnType, typename ClassType, typename... Args>  
    struct function_traits<ReturnType(ClassType::*)(Args...) __VA_ARGS__> : function_traits<ReturnType(Args...)>{};
    
    FUNCTION_TRAITS()
    FUNCTION_TRAITS(const)   //const成员函数
    FUNCTION_TRAITS(volatile)
    FUNCTION_TRAITS(const volatile)
    
    //函数对象
    template<typename Callable>
    struct function_traits :  function_traits<decltype(&Callable::operator())>{};
    
    //将lambda转为std::function
    template<typename Function>
    typename function_traits<Function>::stl_function_type
    to_function(const Function& lambda)
    {
        return static_cast<typename function_traits<Function>::stl_function_type>(std::forward<Function>(lambda));
    }
    
    template<typename Function>
    typename function_traits<Function>::stl_function_type
    to_function(Function&& lambda)
    {
        return static_cast<typename function_traits<Function>::stl_function_type>(lambda);
    }
    
    //将lambda转为函数指针,如
    template<typename Function>
    typename function_traits<Function>::pointer
    to_function(const Function& lambda)
    {
        //    typedef int(*FUN)(int);
        //    auto f = FUN([](int x){return x + 10;});
        //  cout << f(10) << endl;  //20
        return static_cast<typename function_traits<Function>::pointer>(lambda);
    }
    
    #endif  //_FUNCTION_TRAITS_H_
    View Code

    //Variant.hpp

    #include <typeindex>
    #include <iostream>
    #include "function_traits.hpp"
    
    /** 获取最大的整数 **/
    template<size_t arg, size_t...rest>  //eg.IntegerMax<8, 7, 9, 10, 5, 6, 3, 4>::value == 10
    struct IntegerMax : std::integral_constant<size_t, (arg > IntegerMax<rest...>::value) ?
                                                        arg : IntegerMax<rest...>::value >
    {};                                                   
    
    template<size_t arg>  //递归终止
    struct IntegerMax<arg> : std::integral_constant<size_t, arg>
    {    
    };
    
    /** 获取最大的align **/
    template<typename... Args>  //eg. MaxAlign<char, int, double>::value == 8
    struct MaxAlign : std::integral_constant<int, IntegerMax<std::alignment_of<Args>::value...>::value>
    {};
    
    /** 是否包含某个类型 **/ 
    template<typename T, typename...List>  //eg. Contains<float, char,double,int>::value == 0
    struct Contains;
    
    template<typename T, typename Head, typename... Rest>
    struct Contains<T, Head, Rest...> :
    std::conditional<std::is_same<T, Head>::value, 
                             std::true_type, 
                             Contains<T, Rest...> >::type
    {};
    
    template<typename T>
    struct Contains<T> : std::false_type{};
    
    /**获取T在List中的索引位置**/
    //在展开参数包的过程中看是否匹配到特化的IndexOf<T, T, Rest...>,
    //如果匹配上则终止递归将之前的value累加起来得到目标类型的索引位置,
    //否则将value加1,如果所有的类型中都没有对应的类型则返回-1;
    template<typename T, typename... List> 
    struct IndexOf;                //IndexOf<int, double, short, char, int, float>::value == 3
    
    template<typename T, typename Head, typename... Rest>
    struct IndexOf<T, Head, Rest...>  //T和Head不同
    {
    private:
        enum {temp = IndexOf<T, Rest...>::value };    
    public:    
        //enum {value = (temp == -1) ? -1 : temp + 1};    
        //enum{value = (IndexOf<T, Rest...>::value == -1) ?  -1 : IndexOf<T, Rest...>::value + 1 };
        enum{value = IndexOf<T, Rest...>::value + 1 };
    };
    
    template<typename T, typename... Rest>
    struct IndexOf<T, T, Rest...>   //T和Head相同
    {
        enum{value = 0};
    };
    
    template<typename T>
    struct IndexOf<T>
    {
        enum{value = -1};
    };
    
    /**获取指定位置的类型**/
    template<int index, typename... Types>  //eg. At<1, int, double, char>::type == double
    struct At;
    
    template<int index, typename First, typename... Types>
    struct At<index, First, Types...>
    {
        using type = typename At<index-1, Types...>::type;
    };
    
    template<typename T, typename... Types>
    struct At<0, T, Types...>
    {
        using type = T;
    };
    
    //Variant类
    template<typename... Types>
    class Variant
    {
        enum
        {
            data_size = IntegerMax<sizeof(Types)...>::value, //类型的最大值
            align_size = MaxAlign<Types...>::value    //类型最大内存对齐值
        };
        using data_t = typename std::aligned_storage<data_size, align_size>::type;
        
    private:
        data_t m_data;
        std::type_index m_typeIndex;//类型ID
        
    private:
    
        //拷贝
        void copy(const std::type_index& old_t, const void* old_v, void *new_v)
        {
            //使用lambda表达式来展开Types...以便根据当前m_typeIndex来判断是否进行拷贝
            [&]{std::initializer_list<int>{(copy0<Types>(old_t, old_v, new_v),0)...};}();
        }
        
        template<typename T>
        void copy0(const std::type_index& old_t, const void* old_v, void *new_v)
        {
            if(old_t == std::type_index(typeid(T)))
                new (new_v) T (*reinterpret_cast<const T*>(old_v)); //placement new
        }
        
        //移动
        void move(const std::type_index& old_t, void* old_v, void *new_v)
        {
            //使用lambda表达式来展开Types...以便根据当前m_typeIndex来判断是否进行移动
            [&]{std::initializer_list<int>{(move0<Types>(old_t, old_v, new_v),0)...};}();
        }
        
        template<typename T>
        void move0(const std::type_index& old_t, void* old_v, void *new_v)
        {
            if(old_t == std::type_index(typeid(T)))
                new (new_v) T (std::move(*reinterpret_cast<T*>(old_v))); //placement new
        }
        
        //销毁
        void destroy(const std::type_index& index, void * buf)
        {
            [&]{std::initializer_list<int>{(destroy0<Types>(index, buf),0)...};}();
        }
        
        template<typename T>
        void destroy0(const std::type_index& index, void * buf)
        {
            if(index == std::type_index(typeid(T)))
                reinterpret_cast<T*>(buf)->~T();
        }
        
    public:
        template<int index>
        using IndexType = typename At<index, Types...>::type;
        
        Variant(void) : m_typeIndex(typeid(void)){}
        
        Variant(const Variant<Types...>& old) : m_typeIndex(old.m_typeIndex)
        {
            copy(old.m_typeIndex, &old.m_data, &m_data);
        }
        
        Variant(Variant<Types...>&& old) : m_typeIndex(old.m_typeIndex)
        {
            move(old.m_typeIndex, &old.m_data, &m_data);
        }
        
        Variant& operator=(const Variant& old)
        {
            copy(old.m_typeIndex, &old.m_data, &m_data);
            m_typeIndex = old.m_typeIndex;
            return *this;
        }
        
        Variant& operator=(Variant&& old)
        {
            move(old.m_typeIndex, &old.m_data, &m_data);
            m_typeIndex = old.m_typeIndex;
            return *this;
        }
        
        //当Variant被Types...中某一类型的对象初始化时,调用该构造函数
        //1. enable_if确保T只能是Types...中的类型,此外,enable_if无第2个参数,则默认为void
        template<class T, class = typename std::enable_if<
                                  Contains<typename std::decay<T>::type, Types...>::value>::type>
        Variant(T&& value) : m_typeIndex(typeid(void))
        {
            destroy(m_typeIndex, &m_data);
            
            typedef typename std::decay<T>::type U;
            new(&m_data) U(std::forward<T>(value));
            m_typeIndex = std::type_index(typeid(U));
        }
        
        ~Variant()
        {
            destroy(m_typeIndex, &m_data);
        }
        
    public:
        template<typename T>
        bool is() const //判断Variant值的类型是否为T
        {
            return (m_typeIndex == std::type_index(typeid(T)));
        }
        
        bool Empty()const
        {
            return m_typeIndex == std::type_index(typeid(void));
        }
        
        std::type_index type() const //获取类型ID
        {
            return m_typeIndex;
        }
        
        //获取元素的值
        template<typename T>
        typename std::decay<T>::type& get()
        {
            using U = typename std::decay<T>::type;
            if(!is<U>()){
                std::cout << typeid(U).name() << " is not defined. "
                << "current type is " << m_typeIndex.name() << std::endl;
                throw std::bad_cast{};
            }
            return *(U*)(&m_data);
        }
        
        //获取类型的位置索引
        template<typename T>
        int indexOf()
        {
            return IndexOf<T, Types...>::value;
        }
        
        //访问元素(访问者模式)
        template<typename F>
        void visit(F&& f)
        {
            //求出F函数的第1个参数的类型(注意由于args也是个模板,需加template)
            using T = typename function_traits<F>::template args<0>::type; 
    
            if (is<T>())
                f(get<T>()); //获取元素的值,并传入f函数中。
        }
        
        template<typename F, typename... Rest>
        void visit(F&& f, Rest&&... rest)
        {
            using T = typename function_traits<F>::template args<0>::type;
            if(is<T>()){
                visit(std::forward<F>(f));
            }else{
                visit(std::forward<Rest>(rest)...);
            }
        }    
    };

    //testVariant.cpp

    #include "Variant.hpp"
    #include <iostream>
    
    using namespace std;
    
    int main()
    {
        cout << IntegerMax<8, 7, 9, 10, 5, 6, 3, 4>::value << endl;
        
        cout << MaxAlign<char, int, double>::value << endl;
        cout << sizeof(double) << endl;
        
        cout << Contains<float, char, double, int>::value << endl;
        cout << IndexOf<float, double, short, char, int>::value << endl;
        
        using T = At<1, int, double, char>::type;
        cout << typeid(T).name() << endl; //输出double
        
        typedef Variant<int, double, string, char> V;
        V v1 = 10;
        
        //获取索引位置为1的类型
        cout << typeid(V::IndexType<1>).name() << endl; //double
        
        //获取类型的索引位置
        cout << v1.indexOf<string>() << endl; //2
        
        cout << v1.get<int>() << endl; //10
        
        //是否为空
        cout << v1.Empty() << endl; //0
        cout << v1.type().name() << endl; //int
        
        V v2 = v1; //初始化,也可以在这里测试赋值操作
        cout << v2.get<int>() << endl; //10
        
        //通过一组lambda访问variant
        v1.visit([&](double i){cout << i << endl;},
                 [&](short i){cout << i << endl;},
                 [&](int i){cout << i << endl;},     //10
                 [&](string i){cout << i << endl;}); 
    
        return 0;
    }
  • 相关阅读:
    css中的选择器
    HTML5总结
    table中的一些另类标签
    form表单
    Laravel常用命令行中文版
    浅谈Session与Cookie的区别与联系
    Wechat微信公众平台开发
    js中的正则表达式入门
    畅谈Redis和Memcached的区别
    php的ob缓存详解
  • 原文地址:https://www.cnblogs.com/5iedu/p/7843171.html
Copyright © 2011-2022 走看看