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

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

有的时候咱们须要有一个万能类型来进行一些操做,这时候boost::any就派上用场了。c++

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

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

原理

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

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);

咱们须要咱们的万能类型有以下的行为:this

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

实现

咱们能够经过添加一个中间层来解决任何问题。
在boost::any中, 经过两个中间层来达成咱们上面的目标, 类any做为对外的接口层,承担以模板做为参数并提供必要的对外方法。
类placeholder做为接口类,让any使用。而holder是一个模板类做为类placeholder的实现者, 这样就避免的any对泛型参数的要求(能自动推到导出来)。
我这里模仿了相关的实现,其代码结构应该是这样的:code

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
相关文章
相关标签/搜索