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
相關文章
相關標籤/搜索