问题与需求:

请读者先看这篇文章,【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=;
long j=;
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 ;
}

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

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.代码更加的短小紧凑。(代码量减少)
2.ExportData对象使用起来更容易。
3.ExportData对象仅有两个数据,一个是指针联合体,一个是枚举值。(性能更优)
4.我在作者提出4点需求基础上添加了一个额外功能,ExportData可以动态的改变持有数据的类型。(功能更强)
5. 类中所有方法如果不使用模版而是使用重载,虽然会导致代码量大增,但好处是我们可以在编译期提示用户ExportData不支持某些类型,也能提高一点运行速度。要不要这么做,可具体问题具体分析。
6.因为使用模版,所以可扩展性强,当增加支持类型时,只需改动少量代码。(可扩展性更好)

最后,这段代码是示例代码,也许经不起推敲,那么引用原文作者的话,“我想肯定还有更好的解决方法,比如可以尝试在编译时就提示类型不支持而不是在运行时通过返回错误来提示。如果有更好的解决方案,欢迎一起讨论。”,ME TOO。

 
typetraits版:
 struct A{~A(){cout<<"delete A..."<<endl;}};
template<typename T>
struct TypeTraits
{
typedef void TYPE;
};
template<>
struct TypeTraits<std::string>
{
typedef std::string TYPE;
};
template<>
struct TypeTraits<long>
{
typedef long TYPE;
};
template<>
struct TypeTraits<A>
{
typedef A TYPE;
};
template<>
struct TypeTraits<double>
{ typedef double TYPE;
}; 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;
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_same<typename TypeTraits<T>::TYPE,T>::value,"not support!");
vp=new T(t);
types= typeMap[typeid(T)];
}
template <typename T> void setData(const T& t)
{
static_assert(is_same<typename TypeTraits<T>::TYPE,T>::value,"not support!");
assert(types==typeMap[typeid(T)]);
*(static_cast<T*>(vp))=t;
}
template <typename T> void getData(T& t)
{
static_assert(is_same<typename TypeTraits<T>::TYPE,T>::value,"not support!");
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()};

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版本

 template<typename... _Elements> struct Typelist;
template<typename T,typename U> struct is_contained;
template<typename T,typename... Tail> struct is_contained<T,Typelist<T,Tail...>>:public true_type{};
template<typename T> struct is_contained<T, Typelist<>>:public false_type{};
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()};

【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. 处理java多线程时线程安全问题 - ThreadLocal和Synchronized

    多线程在自动化测试中用的不多,也就是说我们用单线程可以完成大部分的自动化测试脚本. 主要有两个原因,首先是因为自动化测试首要考虑的是脚本的稳定性,所以一般会牺牲效率以保证脚本稳定,其次是由于局限于我们 ...

  2. win8安装wampserver报403错误解决方法

    看着别人开始体验win8了,前几天我也安装了win8系统,总体来说还不错,但是今天安装完Wampserver后,浏览器输入localhost,竟然报了403错误,我以为我安装出错了,后来研究了半天,发 ...

  3. 教你使用 Reflexil 反编译.NET 转

    转自:http://www.wxzzz.com/711.html http://sebastien.lebreton.free.fr/reflexil/ http://www.aneasystone. ...

  4. Razor

    什么是Razor 1.Razor是一种将基于服务器的代码添加到网页中的标记语法 2.Razor具有传统ASP.NET标记功能 3.Razor是一种服务器端的标记语法 4.Razor同时支持C#和VB ...

  5. 爬虫之Beautifulsoup及xpath

    1.BeautifulSoup (以 Python 风格的方式来对 HTML 或 XML 进行迭代,搜索和修改) 1.1 介绍 Beautiful Soup提供一些简单的.python式的函数用来处理 ...

  6. 图形解析理解 css3 之倾斜属性skew()

    1.作用 改变元素在页面中的形状2.语法 属性:transform 函数: 1.skew(xdeg) 向横向倾斜指定度数 x取值为正,X轴不动,y轴逆时针倾斜一定角度 x取值为负,X轴不动,y轴顺时针 ...

  7. Jewels and Stones

    题目如下 You're given strings J representing the types of stones that are jewels, and S representing the ...

  8. 竞赛题解 - [CF 1080D]Olya and magical square

    Olya and magical square - 竞赛题解 借鉴了一下神犇tly的博客QwQ(还是打一下广告) 终于弄懂了 Codeforces 传送门 『题目』(直接上翻译了) 给一个边长为 \( ...

  9. youku客户端

    文件结构 config import os IP_PORT = ('127.0.0.1',8080) BASE_DIR = os.path.dirname(os.path.dirname(__file ...

  10. 基于OMAPL138的字符驱动_GPIO驱动AD9833(三)之中断申请IRQ

    基于OMAPL138的字符驱动_GPIO驱动AD9833(三)之中断申请IRQ 0. 导语 学习进入到了下一个阶段,还是以AD9833为例,这次学习是向设备申请中断,实现触发,在未来很多场景,比如做用 ...