boost源码剖析----boost::any

有的时候我们需要有一个万能类型来进行一些操作,这时候boost::any就派上用场了。

boost::Any testInt(10);
int val = static_cast<int>(testInt);

用法比较简单,我们来研究下boost::any是如何实现的。

原理

c++是一个强类型的语言,要实现一个万能类型可以考虑用void*来保存数据,然后用类型转换进行操作,如:

class MyAny{
MyAny(void* input):content_(input){
} template<typename T>
T any_cast(){return *static_cast<T*>(content_)}
private:
void* content_;
}

但是这样的写法有一个明显的缺点就是类型不安全。

显然我们可以用template来改进我们的程序:

template<typename T>
class MyAny{
MyAny(T input):content_(input){
} T any_cast(){return content_;}
private:
T content_;
}

但是这样我们好像就没有解决问题:vector<MyAny> 好吧,这里就写不下去了。

为了能写下如下的代码:

vector<MyAny> items;
items.push_bacck(1);
items.push_bacck(2.0);

我们需要我们的万能类型有如下的行为:

  1. 对外是一个一般的类,使用者压入参数的时候不应该关心类型
  2. 它只是一个中间层,具体保存数据的应该是一个模板类(Holder)
  3. 必须要能有方法支持任意类型的输入和输出为任意类型

实现

我们可以通过添加一个中间层来解决任何问题。

在boost::any中, 通过两个中间层来达成我们上面的目标, 类any作为对外的接口层,承担以模板作为参数并提供必要的对外方法。

类placeholder作为接口类,让any使用。而holder是一个模板类作为类placeholder的实现者, 这样就避免的any对泛型参数的要求(能自动推到导出来)。

我这里模仿了相关的实现,其代码结构应该是这样的:

class  Any
{
public:
Any() :holder_(nullptr){} template<typename ValueType>
Any(const ValueType& val)
: holder_(new Holder<ValueType>(val)){ }
private:
IHolder* holder_;
}; mb_interface IHolder{
} template<typename ValueType>
class Holder : public IHolder{
public:
Holder(const ValueType& val) : value_(val){ }
} public:
ValueType value_;
}

其中Holder提供了具体的数据存储服务,而 Any提供了对外的接口能力。

其中Holder必须提供两个方法:

	mb_interface IHolder{
virtual ~IHolder(){} virtual const std::type_info& type() const = 0; virtual IHolder* clone() const = 0;
};
  1. type()提供了查询类型的能力
  2. clone()提供了产生数据的能力

在 Any中, 提供了以下几个个接口:

	bool	empty(){
return !holder_;
} const std::type_info& type() const {
return holder_ ? holder_->type() : typeid(void);
} Any& operator=(Any rhs){
return swap(rhs);
} template<typename ValueType>
Any& operator=(const ValueType& val){
return Any(val).swap(*this);
}

判断是否为空,查询类型操作,赋值操作

当然必须还有最重要的any_cast操作,我们看其实现:

template<typename ValueType>
ValueType* anyCast(Any* val){
return (val && val->type() == typeid(ValueType))
? &static_cast<Holder<ValueType>*>(val->getHolder())->value_ : 0;
} template<typename ValueType>
ValueType anyCast(Any& val){
ValueType* rtn = anyCast<ValueType>(&val);
if (!rtn)boost::throw_exception(badAnyCast());
return *rtn;
}

果然好简单。呵呵~~~

最后添上单元测试,整个代码就完善了:

mb::Any testInt(10);
mb::Any testDouble(10.0);
mb::Any testInt2(testInt);
EXPECT_EQ(testInt.empty(), false);
EXPECT_EQ(std::string(testDouble.type().name()), "double");
EXPECT_EQ(std::string(testInt2.type().name()), "int"); int val = mb::anyCast<int>(testInt);
EXPECT_EQ(val, 10);

总结

  1. 代码和boost::any中有一些出入,但是我们的目的是为了研究其实现,就忽略了某些细节
  2. 模板技巧: 模板类原来还可以这么用---声明非模板接口,并用模板类实现, 这样在使用这个接口的时候就能避免宿主类显示声明参数类型
  3. boost::any是整个boost库中最简单的类之一,但是某些代码细节还是非常值得学习和借鉴的。
  4. typeid和type_info 感觉有点像c++中的鸡肋,但是某些时候还是有用的
  5. 相关的代码上传到github上,有需要的同学可以看下any.h,

    holder.h

boost源码剖析----boost::any的更多相关文章

  1. Boost源码剖析之:泛型指针类any

    C++是强类型语言,所有强类型语言对型别的要求都是苛刻的,型别一有不合编译器就会抱怨说不能将某某型别转换为某某型别,当然如果在型别之间提供了转换操作符或是标准所允许的一定程度的隐式转换(如经过非exp ...

  2. STL源码剖析之组件

    本篇文章开始,进行STL源码剖析的一些知识点,后续系列笔记全是参照<STL源码剖析>进行学习记录的 STL在现在的大部分项目中,实用性已经没有Boost库好了,毕竟STL中仅仅提供了一些容 ...

  3. jQuery之Deferred源码剖析

    一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...

  4. Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现

    声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...

  5. Apache Spark源码剖析

    Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著   ISBN 978-7-121-25420- ...

  6. 基于mybatis-generator-core 1.3.5项目的修订版以及源码剖析

    项目简单说明 mybatis-generator,是根据数据库表.字段反向生成实体类等代码文件.我在国庆时候,没事剖析了mybatis-generator-core源码,写了相当详细的中文注释,可以去 ...

  7. STL"源码"剖析-重点知识总结

    STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...

  8. SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现

    SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过serv ...

  9. 自己实现多线程的socket,socketserver源码剖析

    1,IO多路复用 三种多路复用的机制:select.poll.epoll 用的多的两个:select和epoll 简单的说就是:1,select和poll所有平台都支持,epoll只有linux支持2 ...

随机推荐

  1. C# PInvoke(DllImport使用) 进阶教程(一)转

    我们曾经熟悉的WindowsAPI, 我们曾经花费了大量精力写的代码,难道我们就要轻易放弃吗 不过当下微软已经把向下兼容性放在很重要的位置.C#程序员使用已有的代码来作为自己程序的一部分是很普通的事情 ...

  2. oracle免安装客户端设置

    对oracle不是很熟悉,就是使用层面的,开发时往往需要连接oracle,又不想单独安装,一般都用个免安装的客户端罢了,再次记录一下自用 1.下载oracle免安装的客户端 下载地址:http://w ...

  3. [MetroUI-1]无边框模式

    Wpf中取消边框,使用 WindowStyle="None" AllowsTransparency="True"

  4. CAS单点登录系统整合——注册的问题

    最近一段时间在搞CAS单点登录系统,涉及到几个子系统的整合问题.对于注册,这里遇到了一个选择: 在子系统内完成注册,然后把信息同步到CAS系统: 在CAS系统中完成基本信息的注册,比如:用户名.邮箱. ...

  5. HDU 2546

    饭卡 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...

  6. 获取到body的offsetTop和offsetLeft值

    function offsetTL(obj){//获取到body的offsetTop和offsetLeft var t=0,l=0; while(obj){ t=t+obj.offsetTop; l= ...

  7. mysql添加外键错误

    异常信息如下: ERROR <HY000>:Can't create table '.\itac\#sql-6fc_546f.frm' <errno:121> 我的问题是新建的 ...

  8. MVC Model数据验证

    概述 上节我们学习了Model的数据在界面之间的传递,但是很多时候,我们在数据传递的时候为了确保数据的有效性,不得不给Model的相关属性做基本的数据验证. 本节我们就学习如何使用 System.Co ...

  9. 20145337 《Java程序设计》第10周学习总结

    20145337 <Java程序设计>第10周学习总结 教材学习内容总结 网络编程 网络编程的实质就是两个(或多个)设备(例如计算机)之间的数据传输. 路由器和交换机组成了核心的计算机网络 ...

  10. 【iCore3 双核心板】例程十四:FATFS实验——文件操作

    实验指导书及代码包下载: http://pan.baidu.com/s/1dEpechF iCore3 购买链接: https://item.taobao.com/item.htm?id=524229 ...