请读者先看这篇文章,【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}, };
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; }
这是一个精简版,使用重载:
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,会发生内存错误。
我觉得第二个错误,没办法在编译期阻止用户编译,因为在setData的时候,无法在编译期知道哪个指针有效。
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删除数据,谨记使用内存擦除方法无法清除用户自定义类型(当数据持有指针时)
2.static_assert(is_same<typename TypeTraits<T>::TYPE,T>::value,"not support!");静态断言,当用户使用不支持类型时,立即阻止用户编译。
3.assert(types==typeMap[typeid(T)]);运行时断言,当运行时发现类型异常,立即退出程序。
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()};