问题与需求:

请读者先看这篇文章,【C++模版之旅】项目中一次活用C++模板(traits)的经历。 对于此篇文章提出的问题,我给出一个新的思路。

talking is cheap,show me the code.文章结尾处,有最终版。

初版代码:

  1. class ExportData
  2. {
  3. union
  4. {
  5. string * sp;
  6. long* lp;
  7. double* dp;
  8. void* vp;
  9.  
  10. };
  11. enum my_type {SP,LP,DP} types;
  12. static unordered_map<type_index,my_type> typeMap;
  13. public:
  14.  
  15. template <typename T> ExportData(T t)
  16. {
  17. if(typeMap.find(typeid(t))==typeMap.end())
  18. assert(false);
  19. vp=new T(t);
  20. types= typeMap[typeid(T)];
  21. }
  22. template <typename T> void setData(T t)
  23. {
  24. if(typeMap.find(typeid(t))==typeMap.end())
  25. assert(false);
  26. switch(types)
  27. {
  28. case SP:
  29. delete sp;
  30. break;
  31. case DP:
  32. delete dp;
  33. break;
  34. case LP:
  35. delete lp;
  36. break;
  37. }
  38. vp=new T(t);
  39. types=typeMap[typeid(T)];
  40.  
  41. }
  42. template <typename T> void getData(T& t)
  43. {
  44. if(typeMap[typeid(T)]!=types) assert(false);
  45. t=*(static_cast<T*>(vp));
  46. }
  47.  
  48. };
  49.  
  50. unordered_map<type_index,ExportData::my_type> ExportData::typeMap
  51. {
  52. {typeid(string),ExportData::my_type::SP},
  53. {typeid(long),ExportData::my_type::LP},
  54. {typeid(double),ExportData::my_type::DP},
  55. };

重复一下,四点需求:

1. ExportData需要仅支持整型(long),浮点型(double),字符串(string)以及二进制(void*, size)4种类型的操作。(我并没有考虑二进制)
2. ExportData需要考虑结构的尺寸,尽量减少空间冗余(我使用联合体,保存各种类型数据的指针)
3. 即使对以上4种不同数据类型进行操作,还是希望在从ExportData中Get或Set真实数据时,使用的方法能统一(方法显然是统一的,因为使用的是模版)
4. 当调用者尝试使用了以上4种类型以外的数据类型时,能通过返回错误让调用方知道类型不匹配(为了方便演示,试图使用其他类型都会导致断言失败,终止运行)

如果你也讨厌代码中存在swtich,可以再次使用表驱动法。代码如下所示:

  1. class DeleteLong
  2. {
  3. public:
  4. void operator()(void *p)
  5. {
  6. delete static_cast<long*>(p);
  7. }
  8. };
  9. class DeleteString
  10. {
  11. public:
  12. void operator()(void *p)
  13. {
  14. delete static_cast<string*>(p);
  15. }
  16. };
  17. class DeleteDouble
  18. {
  19. public:
  20. void operator()(void *p)
  21. {
  22. delete static_cast<double*>(p);
  23. }
  24. };
  25.  
  26. class ExportData
  27. {
  28. union
  29. {
  30. string * sp;
  31. long* lp;
  32. double* dp;
  33. void* vp;
  34.  
  35. };
  36. enum my_type {SP,LP,DP} types;//change it to object.
  37. static unordered_map<type_index,my_type> typeMap;
  38. static vector<function<void(void*)>> deleters;
  39. public:
  40.  
  41. template <typename T> ExportData(T t)
  42. {
  43. static_assert(is_same<T,string>::value||is_same<T,double>::value||is_same<T,long>::value,"never support!");//感谢园友 崔好好提醒
  44. // if(typeMap.find(typeid(t))==typeMap.end())
  45. // assert(false);
  46. vp=new T(t);
  47. types= typeMap[typeid(T)];
  48. }
  49. template <typename T> void setData(T t)
  50. {
  51. static_assert(is_same<T,string>::value||is_same<T,double>::value||is_same<T,long>::value,"never support!");//感谢园友 崔好好提醒
  52. (deleters[types])(vp);
  53. vp=new T(t);//可以改用placement new
  54. types=typeMap[typeid(T)];
  55.  
  56. }
  57. template <typename T> void getData(T& t)
  58. {
  59.  
  60. static_assert(is_same<T,string>::value||is_same<T,double>::value||is_same<T,long>::value,"never support!");//感谢园友 崔好好提醒
  61. if(typeMap[typeid(T)]!=types) assert(false);
  62. t=*(static_cast<T*>(vp));
  63. }
  64. //这里可以改成重载,void getData(long& t){...} void getData(sting& t){....} void getData(double& t){...}调用其他类型则编译错误
  65.  
  66. };
  67.  
  68. unordered_map<type_index,ExportData::my_type> ExportData::typeMap
  69. {
  70. {typeid(string),ExportData::my_type::SP},
  71. {typeid(long),ExportData::my_type::LP},
  72. {typeid(double),ExportData::my_type::DP},
  73. };
  74. vector<function<void(void*)>> ExportData::deleters {DeleteString(),DeleteLong(),DeleteDouble(),};

这里是测试代码:

  1. int main()
  2. {
  3.  
  4. long i=;
  5. long j=;
  6. string s="Hello";
  7. string ss;
  8. ExportData p(i);
  9. p.setData(++i);
  10. p.getData(j);
  11. p.setData(s);
  12. p.getData(ss);
  13. cout<<j<<endl;
  14. cout<<ss<<endl;
  15. return ;
  16. }

这是一个精简版,使用重载:

  1. class ExportData
  2. {
  3. union
  4. {
  5. string * sp;
  6. long* lp;
  7. double* dp;
  8. };
  9. public:
  10. ExportData(long t)
  11. {
  12. lp=new long(t);
  13. }
  14. ExportData(double t)
  15. {
  16. dp=new double(t);
  17. }
  18. ExportData(string t)
  19. {
  20. sp=new string(t);
  21. }
  22. void setData(long t)
  23. {
  24. *lp=t;
  25. }
  26. void setData(double t)
  27. {
  28. *dp=t;
  29. }
  30. void setData(string t)
  31. {
  32. *sp=t;
  33. }
  34. void getData(long& t)
  35. {
  36. t=*lp;
  37. }
  38. void getData(double& t)
  39. {
  40. t=*dp;
  41. }
  42. void getData(string& t)
  43. {
  44. t=*sp;
  45. }
  46. //1.析构函数需要解决内存泄露问题 2.如当前指针指向double,setData函数传入string,会发生内存错误。
  47. };

这个版本存在两个严重的问题,1.析构函数需要解决内存泄露问题 2.如当前指针指向double,setData函数传入string,会发生内存错误。

我觉得第二个错误,没办法在编译期阻止用户编译,因为在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. class DeleteLong
  30. {
  31. public:
  32. void operator()(void *p)
  33. {
  34. delete static_cast<long*>(p);
  35. }
  36. };
  37. class DeleteString
  38. {
  39. public:
  40. void operator()(void *p)
  41. {
  42. delete static_cast<string*>(p);
  43. }
  44. };
  45. class DeleteDouble
  46. {
  47. public:
  48. void operator()(void *p)
  49. {
  50. delete static_cast<double*>(p);
  51. }
  52. };
  53. class DeleteA
  54. {
  55. public:
  56. void operator()(void *p)
  57. {
  58. delete static_cast<A*>(p);
  59. }
  60. };
  61.  
  62. class ExportData
  63. {
  64.  
  65. void* vp;
  66. enum my_type {SP,LP,DP,AP} types;
  67. static unordered_map<type_index,my_type> typeMap;
  68. static vector<function<void(void*)>> deleters;
  69. public:
  70.  
  71. template <typename T> ExportData(const T& t)
  72. {
  73.  
  74. static_assert(is_same<typename TypeTraits<T>::TYPE,T>::value,"not support!");
  75. vp=new T(t);
  76. types= typeMap[typeid(T)];
  77. }
  78. template <typename T> void setData(const T& t)
  79. {
  80. static_assert(is_same<typename TypeTraits<T>::TYPE,T>::value,"not support!");
  81. assert(types==typeMap[typeid(T)]);
  82. *(static_cast<T*>(vp))=t;
  83. }
  84. template <typename T> void getData(T& t)
  85. {
  86. static_assert(is_same<typename TypeTraits<T>::TYPE,T>::value,"not support!");
  87. assert(types==typeMap[typeid(T)]);
  88. t=*(static_cast<T*>(vp));
  89. }
  90.  
  91. ~ExportData()
  92. {
  93.  
  94. (deleters[types])(vp);
  95. }
  96.  
  97. };
  98.  
  99. unordered_map<type_index,ExportData::my_type> ExportData::typeMap
  100. {
  101. {typeid(string),ExportData::my_type::SP},
  102. {typeid(long),ExportData::my_type::LP},
  103. {typeid(double),ExportData::my_type::DP},
  104. {typeid(A),ExportData::my_type::AP}
  105. };
  106. 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...>>{};
  1. struct A{~A(){cout<<"delete A..."<<endl;}};
  2. class DeleteLong
  3. {
  4. public:
  5. void operator()(void *p)
  6. {
  7. delete static_cast<long*>(p);
  8. }
  9. };
  10. class DeleteString
  11. {
  12. public:
  13. void operator()(void *p)
  14. {
  15. delete static_cast<string*>(p);
  16. }
  17. };
  18. class DeleteDouble
  19. {
  20. public:
  21. void operator()(void *p)
  22. {
  23. delete static_cast<double*>(p);
  24. }
  25. };
  26. class DeleteA
  27. {
  28. public:
  29. void operator()(void *p)
  30. {
  31. delete static_cast<A*>(p);
  32. }
  33. };
  34.  
  35. class ExportData
  36. {
  37.  
  38. void* vp;
  39. enum my_type {SP,LP,DP,AP} types;
  40. typedef Typelist<string,long,double,A> list;
  41. static unordered_map<type_index,my_type> typeMap;
  42. static vector<function<void(void*)>> deleters;
  43. public:
  44.  
  45. template <typename T> ExportData(const T& t)
  46. {
  47.  
  48. static_assert(is_contained<T,list>::value,"not supprot");
  49. vp=new T(t);
  50. types= typeMap[typeid(T)];
  51. }
  52. template <typename T> void setData(const T& t)
  53. {
  54. static_assert(is_contained<T,list>::value,"not supprot");
  55. assert(types==typeMap[typeid(T)]);
  56. *(static_cast<T*>(vp))=t;
  57. }
  58. template <typename T> void getData(T& t)
  59. {
  60. static_assert(is_contained<T,list>::value,"not supprot");
  61. assert(types==typeMap[typeid(T)]);
  62. t=*(static_cast<T*>(vp));
  63. }
  64.  
  65. ~ExportData()
  66. {
  67.  
  68. (deleters[types])(vp);
  69. }
  70.  
  71. };
  72.  
  73. unordered_map<type_index,ExportData::my_type> ExportData::typeMap
  74. {
  75. {typeid(string),ExportData::my_type::SP},
  76. {typeid(long),ExportData::my_type::LP},
  77. {typeid(double),ExportData::my_type::DP},
  78. {typeid(A),ExportData::my_type::AP}
  79. };
  80. vector<function<void(void*)>> ExportData::deleters {DeleteString(),DeleteLong(),DeleteDouble(),DeleteA()};

【C++模版之旅】项目中一次活用C++模板(traits)的经历 -新注解的更多相关文章

  1. 【C++模版之旅】项目中一次活用C++模板(traits)的经历

    曾经曾在一个项目中碰到过一个挺简单的问题,但一时又不能用普通常规的方法去非常好的解决,最后通过C++模板的活用,通过traits相对照较巧妙的攻克了这个问题.本文主要想重现问题发生,若干解决方式的比較 ...

  2. 在使用vue+webpack模版创建的项目中使用font-awesome

    前言:最近使用vue+webpack进行一个小项目的开发,按照官方模版文档完成项目初始化后打算引入ont-awesome字体图标库进行使用,引入过程中遇到一些问题并解决,现记录如下. 一开始进展很顺利 ...

  3. 项目中使用的spring 注解说明

    以前在项目中spring 的依赖注入使用 xml 配置,现在使用 注解(Annotation) 来实现配置. 1声明bean 1.1实例 有类: public class MyBean{ //do s ...

  4. 控制反转和spring在项目中可以带来的好处

    Spring实例化Bean的三种方式分别是: 1,xml配置使用bean的类构造器 <bean id="personService" class="cn.servi ...

  5. 采用EntLib5.0(Unity+Interception+Caching)实现项目中可用的Caching机制

    看了园子里很多介绍Caching的文章,多数都只介绍基本机制,对于Cache更新和依赖部分,更是只简单的实现ICacheItemRefreshAction接口,这在实际项目中是远远不够的.实际项目中, ...

  6. Android项目中如何用好构建神器Gradle?(转)

    最近在忙团队并行开发的事情,主要是将各个团队的代码分库,一方面可以降低耦合,为后面模块插件化做铺垫,另一方面采用二进制编译,可以加快编译速度.分库遇到了一些问题,很多都要通过Gradle脚本解决,所以 ...

  7. 如何在cocos2d项目中enable ARC

    如何在cocos2d项目中enable ARC 基本思想就是不支持ARC的代码用和支持ARC的分开,通过xcode中设置编译选项,让支持和不支持ARC的代码共存. cocos2d是ios app开发中 ...

  8. 关于如何正确地在android项目中添加第三方jar包

    在android项目中添加第三方jar包虽然不是一个很复杂的问题,但是确实给很多开发者带来了不小的困扰.我自己就曾经碰到过calss not found exception.error inflati ...

  9. 项目中如何使用babel6详解

    由于浏览器的版本和兼容性问题,很多es6,es7的新的方法都不能使用,等到可以使用的时候,可能已经过去了很多年.Babel可以把es6,es7的新代码编译成兼容绝大多数的主流浏览器的代码. 本篇文章主 ...

随机推荐

  1. 二十一、IntelliJ IDEA 控制台输出中文乱码问题的解决方法

    首先,找到 IntelliJ IDEA 的安装目录,进入bin目录下,定位到idea.vmoptions文件,如下图所示: 双击打开idea.vmoptions文件,如下图所示: 然后,在其中追加-D ...

  2. SQLserver高级编程

    1.数据库设计 数据库设计的重要性: 减少冗余,提高性能.易维护 数据库设计的步骤: 1.收集信息.标识对象.标识属性.标识关系(一对一.一对多.多对一.多对多) E-R图: 属性:定义实体的性质.实 ...

  3. Knowledge Point 20180305 详解精度问题

    1.1 精度与基本数据类型运算的深度解析 我们在探讨Java基本数据类型时多次提到过精度的问题,那么计算机中的精度究竟是什么样的,为什么我们有时候的计算和我们预期的不同呢?下面我们通过精度来了解: 1 ...

  4. vue+element-ui中的图片获取与上传

    vue+element-ui中的图片获取与上传 工作上接触了一下图片的处理,图片的格式是文件流, 记录如下. 请求图片 请求图片的时候,带上{ responseType: 'blob' }, 否则图片 ...

  5. 集合Gk表示这样一堆数字,该集合内的数字有k个1

    问题描述 集合Gk表示这样一堆数字,该集合内的数字有k个1.比如,G1 = { 1, 10, 100, 1000, ...} G2 = {11, 110, 1110 }, ... , Gk { ... ...

  6. markdown常用命令(持续整理更新...)

    编写使用的工具 VS Code 拥有丰富插件支持的代码编辑器,当然也支持markdown MdEditor一款在线编辑markdown网站 1.标题 示例: # 一级标题 ## 二级标题 ### 三级 ...

  7. iOS 清理Xcode项目中没有使用到的图片资源和类文件

    接手到一个旧的项目,但是发现里面有太多的无用资源,包括升级app后,一些无用的图片资源并没有被删掉,导致app在打包成ipa包以后,文件变大.手边这个项目IM要更换成环信的IM,之前的一些旧的SDK, ...

  8. hql返回数值

    public int getCountUser() throws ParseException { Session hSession = sessionFactory.getCurrentSessio ...

  9. php的基础知识(一)

    php(超文本预处理器)定义: php(外文名:php:Hertext Preprocessor,中文名:“超文本预处理器”)是一种通用的开源脚本语言.语法吸收了C语言.Java和Perl的特点,利于 ...

  10. day 20 约束 异常处理 MD5

    1.类的约束(重点): 写一个父类.  父类中的某个方法要抛出一个异常  NotImplementError # 项目经理 class Base:     # 对子类进行了约束. 必须重写该方法    ...