【C++模版之旅】項目中一次活用C++模板(traits)的經歷 -新註解

 

問題與需求:

請讀者先看這篇文章,【C++模版之旅】項目中一次活用C++模板(traits)的經歷。 對於此篇文章提出的問題,我給出一個新的思路。函數

talking is cheap,show me the code.文章結尾處,有最終版。性能

第一版代碼:

class ExportData
{
    union
    {
        string * sp;
        long*  lp;
        double* dp;
        void* vp;
 
    };
    enum my_type {SP,LP,DP} types;
    static unordered_map<type_index,my_type> typeMap;
public:
 
    template <typename T> ExportData(T t)
    {
        if(typeMap.find(typeid(t))==typeMap.end())
            assert(false);
        vp=new T(t);
        types= typeMap[typeid(T)];
    }
    template <typename T> void setData(T t)
    {
        if(typeMap.find(typeid(t))==typeMap.end())
            assert(false);
        switch(types)
        {
        case SP:
            delete sp;
            break;
        case DP:
            delete dp;
            break;
        case LP:
            delete lp;
            break;
        }
        vp=new T(t);
        types=typeMap[typeid(T)];
 
    }
    template <typename T> void getData(T& t)
    {
        if(typeMap[typeid(T)]!=types) assert(false);
        t=*(static_cast<T*>(vp));
    }
 
};
 
unordered_map<type_index,ExportData::my_type> ExportData::typeMap
{
    {typeid(string),ExportData::my_type::SP},
    {typeid(long),ExportData::my_type::LP},
    {typeid(double),ExportData::my_type::DP},
};

 

重複一下,四點需求:

 

1. ExportData須要僅支持整型(long),浮點型(double),字符串(string)以及二進制(void*, size)4種類型的操做。(我並無考慮二進制
2. ExportData須要考慮結構的尺寸,儘可能減小空間冗餘(我使用聯合體,保存各類類型數據的指針
3. 即便對以上4種不一樣數據類型進行操做,仍是但願在從ExportData中Get或Set真實數據時,使用的方法能統一(方法顯然是統一的,由於使用的是模版
4. 當調用者嘗試使用了以上4種類型之外的數據類型時,能經過返回錯誤讓調用方知道類型不匹配爲了方便演示,試圖使用其餘類型都會致使斷言失敗,終止運行

若是你也討厭代碼中存在swtich,能夠再次使用表驅動法。代碼以下所示:
class DeleteLong
{
public:
    void operator()(void *p)
    {
        delete static_cast<long*>(p);
    }
};
class DeleteString
{
public:
    void operator()(void *p)
    {
        delete static_cast<string*>(p);
    }
};
class DeleteDouble
{
public:
    void operator()(void *p)
    {
        delete static_cast<double*>(p);
    }
};

class ExportData
{
    union
    {
        string * sp;
        long*  lp;
        double* dp;
        void* vp;

    };
    enum my_type {SP,LP,DP} types;//change it to object.
    static unordered_map<type_index,my_type> typeMap;
    static vector<function<void(void*)>> deleters;
public:

    template <typename T> ExportData(T t)
    {
        static_assert(is_same<T,string>::value||is_same<T,double>::value||is_same<T,long>::value,"never support!");//感謝園友 崔好好提醒
        // if(typeMap.find(typeid(t))==typeMap.end())
        //     assert(false);
        vp=new T(t);
        types= typeMap[typeid(T)];
    }
    template <typename T> void setData(T t)
    {
        static_assert(is_same<T,string>::value||is_same<T,double>::value||is_same<T,long>::value,"never support!");//感謝園友 崔好好提醒
        (deleters[types])(vp);
        vp=new T(t);//能夠改用placement new
        types=typeMap[typeid(T)];

    }
    template <typename T> void getData(T& t)
    {

        static_assert(is_same<T,string>::value||is_same<T,double>::value||is_same<T,long>::value,"never support!");//感謝園友 崔好好提醒
        if(typeMap[typeid(T)]!=types) assert(false);
        t=*(static_cast<T*>(vp));
    }
    //這裏能夠改爲重載,void getData(long& t){...} void getData(sting& t){....} void getData(double& t){...}調用其餘類型則編譯錯誤

};

unordered_map<type_index,ExportData::my_type> ExportData::typeMap
{
    {typeid(string),ExportData::my_type::SP},
    {typeid(long),ExportData::my_type::LP},
    {typeid(double),ExportData::my_type::DP},
};
vector<function<void(void*)>> ExportData::deleters {DeleteString(),DeleteLong(),DeleteDouble(),};

 


 這裏是測試代碼:測試

int main()
{
   
    long i=5;
    long j=0;
    string s="Hello";
    string ss;
    ExportData p(i);
    p.setData(++i);
    p.getData(j);
    p.setData(s);
    p.getData(ss);
    cout<<j<<endl;
    cout<<ss<<endl;
    return 0;
}

 

 

這是一個精簡版,使用重載:spa

class ExportData
{
    union
    {
        string * sp;
        long*  lp;
        double* dp;
    };
public:
    ExportData(long t)
    {
        lp=new long(t);
    }
    ExportData(double t)
    {
        dp=new double(t);
    }
    ExportData(string t)
    {
        sp=new string(t);
    }
    void    setData(long t)
    {
        *lp=t;
    }
    void   setData(double t)
    {
        *dp=t;
    }
    void setData(string t)
    {
        *sp=t;
    }
    void getData(long& t)
    {
        t=*lp;
    }
    void getData(double& t)
    {
        t=*dp;
    }
    void getData(string& t)
    {
        t=*sp;
    }
    //1.析構函數須要解決內存泄露問題 2.如當前指針指向double,setData函數傳入string,會發生內存錯誤。
};

這個版本存在兩個嚴重的問題,1.析構函數須要解決內存泄露問題 2.如當前指針指向double,setData函數傳入string,會發生內存錯誤。 .net

我以爲第二個錯誤,沒辦法在編譯期阻止用戶編譯,由於在setData的時候,沒法在編譯期知道哪一個指針有效。指針

這段代碼額外的優勢:

1.代碼更加的短小緊湊。(代碼量減小)
2.ExportData對象使用起來更容易
3.ExportData對象僅有兩個數據,一個是指針聯合體,一個是枚舉值。(性能更優)
4.我在做者提出4點需求基礎上添加了一個額外功能,ExportData能夠動態的改變持有數據的類型。(功能更強)
5. 類中全部方法若是不使用模版而是使用重載,雖然會致使代碼量大增,但好處是咱們能夠在編譯期提示用戶ExportData不支持某些類型,也能提升一點運行速度。要不要這麼作,可具體問題具體分析。
6.由於使用模版,因此可擴展性強,當增長支持類型時,只需改動少許代碼。(可擴展性更好)

最後,這段代碼是示例代碼,也許經不起推敲,那麼引用原文做者的話,「我想確定還有更好的解決方法,好比能夠嘗試在編譯時就提示類型不支持而不是在運行時經過返回錯誤來提示。若是有更好的解決方案,歡迎一塊兒討論。」,ME TOO
 
typetraits版:
  1 struct A{~A(){cout<<"delete A..."<<endl;}};
  2 template<typename T>
  3 struct TypeTraits
  4 {
  5        typedef void TYPE;
  6 };
  7 template<>
  8 struct TypeTraits<std::string>
  9 {
 10        typedef std::string TYPE;
 11 };
 12 template<>
 13 struct TypeTraits<long>
 14 {
 15        typedef long TYPE;
 16 };
 17 template<>
 18 struct TypeTraits<A>
 19 {
 20        typedef A TYPE;
 21 };
 22 template<>
 23 struct TypeTraits<double>
 24 {
 25 
 26        typedef double TYPE;
 27 };
 28 
 29 
 30 class DeleteLong
 31 {
 32 public:
 33     void operator()(void *p)
 34     {
 35         delete static_cast<long*>(p);
 36     }
 37 };
 38 class DeleteString
 39 {
 40 public:
 41     void operator()(void *p)
 42     {
 43         delete static_cast<string*>(p);
 44     }
 45 };
 46 class DeleteDouble
 47 {
 48 public:
 49     void operator()(void *p)
 50     {
 51         delete static_cast<double*>(p);
 52     }
 53 };
 54 class DeleteA
 55 {
 56 public:
 57     void operator()(void *p)
 58     {
 59         delete static_cast<A*>(p);
 60     }
 61 };
 62 
 63 class ExportData
 64 {
 65 
 66     void* vp;
 67     enum my_type {SP,LP,DP,AP} types;
 68     static unordered_map<type_index,my_type> typeMap;
 69     static vector<function<void(void*)>> deleters;
 70 public:
 71 
 72     template <typename T> ExportData(const T& t)
 73     {
 74 
 75         static_assert(is_same<typename TypeTraits<T>::TYPE,T>::value,"not support!");
 76         vp=new T(t);
 77         types= typeMap[typeid(T)];
 78     }
 79     template <typename T> void setData(const T& t)
 80     {
 81         static_assert(is_same<typename TypeTraits<T>::TYPE,T>::value,"not support!");
 82         assert(types==typeMap[typeid(T)]);
 83         *(static_cast<T*>(vp))=t;
 84     }
 85     template <typename T> void getData(T& t)
 86     {
 87         static_assert(is_same<typename TypeTraits<T>::TYPE,T>::value,"not support!");
 88         assert(types==typeMap[typeid(T)]);
 89         t=*(static_cast<T*>(vp));
 90     }
 91 
 92     ~ExportData()
 93     {
 94 
 95        (deleters[types])(vp);
 96     }
 97 
 98 };
 99 
100 unordered_map<type_index,ExportData::my_type> ExportData::typeMap
101 {
102     {typeid(string),ExportData::my_type::SP},
103     {typeid(long),ExportData::my_type::LP},
104     {typeid(double),ExportData::my_type::DP},
105     {typeid(A),ExportData::my_type::AP}
106 };
107 vector<function<void(void*)>> ExportData::deleters {DeleteString(),DeleteLong(),DeleteDouble(),DeleteA()};

1.刪除ExportData對象持有的數據,最好使用標準delete刪除數據,謹記使用內存擦除方法沒法清除用戶自定義類型(當數據持有指針時)code

2.static_assert(is_same<typename TypeTraits<T>::TYPE,T>::value,"not support!");靜態斷言,當用戶使用不支持類型時,當即阻止用戶編譯。對象

3.assert(types==typeMap[typeid(T)]);運行時斷言,當運行時發現類型異常,當即退出程序。blog

4.void*指針承擔擦除類型的重任內存

 typelist版本

1 template<typename... _Elements> struct Typelist;
2 template<typename T,typename U> struct is_contained;
3 template<typename T,typename... Tail> struct is_contained<T,Typelist<T,Tail...>>:public true_type{};
4 template<typename T> struct is_contained<T, Typelist<>>:public false_type{};
5 template<typename T,typename Head,typename... Tail> struct is_contained<T,Typelist<Head,Tail...>>:public is_contained<T,Typelist<Tail...>>{};

 

struct A{~A(){cout<<"delete A..."<<endl;}};
class DeleteLong
{
public:
    void operator()(void *p)
    {
        delete static_cast<long*>(p);
    }
};
class DeleteString
{
public:
    void operator()(void *p)
    {
        delete static_cast<string*>(p);
    }
};
class DeleteDouble
{
public:
    void operator()(void *p)
    {
        delete static_cast<double*>(p);
    }
};
class DeleteA
{
public:
    void operator()(void *p)
    {
        delete static_cast<A*>(p);
    }
};

class ExportData
{

    void* vp;
    enum my_type {SP,LP,DP,AP} types;
    typedef Typelist<string,long,double,A> list;
    static unordered_map<type_index,my_type> typeMap;
    static vector<function<void(void*)>> deleters;
public:

    template <typename T> ExportData(const T& t)
    {

        static_assert(is_contained<T,list>::value,"not supprot");
        vp=new T(t);
        types= typeMap[typeid(T)];
    }
    template <typename T> void setData(const T& t)
    {
         static_assert(is_contained<T,list>::value,"not supprot");
        assert(types==typeMap[typeid(T)]);
        *(static_cast<T*>(vp))=t;
    }
    template <typename T> void getData(T& t)
    {
         static_assert(is_contained<T,list>::value,"not supprot");
        assert(types==typeMap[typeid(T)]);
        t=*(static_cast<T*>(vp));
    }

    ~ExportData()
    {

       (deleters[types])(vp);
    }

};

unordered_map<type_index,ExportData::my_type> ExportData::typeMap
{
    {typeid(string),ExportData::my_type::SP},
    {typeid(long),ExportData::my_type::LP},
    {typeid(double),ExportData::my_type::DP},
    {typeid(A),ExportData::my_type::AP}
};
vector<function<void(void*)>> ExportData::deleters {DeleteString(),DeleteLong(),DeleteDouble(),DeleteA()};
相關文章
相關標籤/搜索