【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)
    {
        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);
        (deleters[types])(vp);
        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));
    }    //这里可以改成重载,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;
}

这段代码额外的优点:

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

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

时间: 2024-11-07 12:26:06

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

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

曾经曾在一个项目中碰到过一个挺简单的问题,但一时又不能用普通常规的方法去非常好的解决,最后通过C++模板的活用,通过traits相对照较巧妙的攻克了这个问题.本文主要想重现问题发生,若干解决方式的比較,以及最后怎样去解决的过程,或许终于的方案也并非最好的方案,但至少个人认为从发现到思考到解决到改善,这是一个对帮助个人成长非常不错的过程,所以凭记忆想把它记录下来,分享给大家. 先描写叙述下问题,项目中有这样一个接口类会暴露给外部使用,接口类定义例如以下(类方法名称以及描写叙述该问题无关的内容会有所

项目中使用的spring 注解说明

以前在项目中spring 的依赖注入使用 xml 配置,现在使用 注解(Annotation) 来实现配置. 1声明bean 1.1实例 有类: public class MyBean{ //do something } xml 方式: <bean id="myBean"class="com.bean.MyBean"/> 注解方式: @Component("myBean") public class MyBean { //do som

项目中引入IconFont

1.登录iconFont官网 2.选中需要的icon,添加入库 3.添加至项目 4.添加到自己的项目中,点击更新代码,并复制此代码,并单击下载至本地 5.解压后只复制iconfont.css,放入自己的项目中并正确引入 . 6.我放到styles文件夹中了 7.在index.scss中引入了 . 8.打开 iconfont.css,把下面的复制的代码替换 iconfont.css中的 @font-face,就可以在项目中引用图标字体了. 9.如果想添加新的图标,只需更新 @font-face代码

项目中如何使用babel6详解

由于浏览器的版本和兼容性问题,很多es6,es7的新的方法都不能使用,等到可以使用的时候,可能已经过去了很多年.Babel可以把es6,es7的新代码编译成兼容绝大多数的主流浏览器的代码. 本篇文章主要介绍在项目中如何安装配置和使用babel. 1.在项目下初始化 package.json $ npm init 2.在项目中安装babel $ npm install babel-cli --save-dev 3.安装babel插件 $ npm install babel-preset-xxxxx

导入开源库到基于Android Studio构建的项目中

前两天,谷歌发布了Android Studio 1.0的正式版,也有更多的人开始迁移到Android Studio进行开发.然而,网上很多的开源库,控件等还是以前的基于Eclipse进行开发,很多人不知道怎么导入到自己的基于Android Studio项目中来,微博上也有人私信我,让我来写写,正好今天回来的比较早,就写写吧.主要介绍一下常见的一些导包的场景. 前言 --project //项目目录 | build.gradle //项目的gradle配置文件 | settings.gradle

关于项目中的DAL数据接入层架构设计

摘要:项目中对关系型数据库的接入再寻常不过,也有海量的ORM工具可供选择,一个一般性的DAL数据接入层的结构却大同小异,这里就分享一下使用Hibernate.Spring.Hessian这三大工具对DAL层的具体实现方法,也是对之前使用的一个总结. 关键词:Hibernate, Spring, Hessian, DAL, 数据接入层, 架构设计 注意:以下配置或代码运行在Hibernate4.2.5,Spring3.2.4,Hessian4.0.37,Tomcat7.0.47环境下 一.Mode

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

1.下载第三方jar包 2.在android项目下创建一个libs目录(名称并不固定,你完全可以取其他名称) 3.在eclipse中右键点击libs目录,依次选择Import -> General -> File System,选中jar包所在目录, 然后选中这个目录下的jar包 注:到这一步为止你就成功地把jar包添加到项目中(但是还没有被android的虚拟机识别,因此如果这时你使用jar包中的类,编译都无法通过) 4.右键点击项目名,依次选择Build Path -> Config

我们为什么要把Dagger2,MVP以及Rxjava引入项目中?

1Why? 毫无疑问在Android开发圈中这三个技术是经常被提及的,如此多的文章和开源项目在介绍他们,使用他们,开发者也或多或少的被带动起来在自己的项目中使用他们,但是使用他们之前我们知道为什么要使用他们,他们能给我们带来什么好处吗,还是只是跟随潮流 其实我们大多数项目中是使用不到他们的,或者说对这些技术的需求不是很大,为什么这么说呢? 大多数的开发者其实都是在开发功能模块比较少的小项目,对于这些项目来说,其实使用这些技术带来的好处相对于在开发时的所付出的时间来说其实性价比并不高,因为学习这些

C# 获取文件路径,读取项目中某程序集下文件

获取文件路径 ------------------------------------------------------------------------- winform获取文件路径: string str1 =Process.GetCurrentProcess().MainModule.FileName;//获得当前执行的exe的文件名.string str2=Environment.CurrentDirectory;//获取和设置当前目录的完全限定路径.string str3=Dire