boost源碼剖析----boost::any


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM