使用c++11打造好用的variant方法
boost中的variant的基本用法:
typedef variant<int,char, double> vt;
vt v = 1;
v = '2';
v = 12.32;
用variant一個(gè)好處是可以擦除類型,不同類型的值都統(tǒng)一成一個(gè)variant,雖然這個(gè)variant只能存放已定義的類型,但這在很多時(shí)候已經(jīng)夠用了。 取值的時(shí)候,通過(guò)get<T>(v)來(lái)獲取真實(shí)值。然而,當(dāng)T類型與v的類型不匹配時(shí),會(huì)拋出一個(gè)bad_cast的異常來(lái)。boost的variant拋出的異常往往沒(méi)有更多的信息,不知道到底是哪個(gè)類型轉(zhuǎn)換失敗,導(dǎo)致發(fā)生異常調(diào)試時(shí)很不方便。因此,就考慮用c++11去實(shí)現(xiàn)一個(gè)vairiant, 這個(gè)variant可以很容易知道取值時(shí),是什么類型轉(zhuǎn)換失敗了。
打造variant需要解決的問(wèn)題:
第一,要在內(nèi)部定義一個(gè)char緩沖區(qū)。
緩沖區(qū)用來(lái)存放variant的值,這個(gè)值是variant定義的多種類型中的某種類型的值,因此,這個(gè)緩沖區(qū)要足夠大,能夠存放類型最大(sizeof(Type))的值才可以,這個(gè)緩沖區(qū)的大小還必須在編譯期計(jì)算出來(lái)。因此需要首先要解決的是variant值存放的緩沖區(qū)定義的問(wèn)題。
第二,要解決賦值的問(wèn)題。
將值賦給vairiant時(shí),需要將該值的類型ID記錄下來(lái),以便在后面根據(jù)類型取值。將值保存到內(nèi)部緩沖區(qū)時(shí),還需要用palcement new在緩沖區(qū)創(chuàng)建對(duì)象。另外,還要解決一個(gè)問(wèn)題,就是賦值時(shí)需要檢查variant中已定義的類型中是否含有該類型,如果沒(méi)有則編譯不通過(guò),以保證賦值是合法的。
第三,要解決取值的問(wèn)題。
通過(guò)類型取值時(shí),要判斷類型是否匹配,如果不匹配,將詳情打印出來(lái),方便調(diào)試。
打造variant的關(guān)鍵技術(shù):
1.找出最大的typesize
第一個(gè)問(wèn)題中需要解決的問(wèn)題是如何找出多種類型中,size最大的那個(gè)類型的size??纯慈绾螐亩喾N類型中找出最大類型的size。
template<typename T, typename... Args>
struct MaxType : std::integral_constant<int,
(sizeof(T)>MaxType<Args...>::value ? sizeof(T) : MaxType<Args...>::value) >
{};
template<typename T>
struct MaxType<T> : std::integral_constant<int, sizeof(T) >{};
通過(guò)這個(gè)MaxType就可以在編譯期獲取類型中最大的maxsize了:MaxType<Types...>::value。
2.類型檢查和緩沖區(qū)中創(chuàng)建對(duì)象
第二個(gè)問(wèn)題中需要解決兩個(gè)問(wèn)題,1.檢查賦值的類型是否在已定義的類型中;2.在緩沖區(qū)中創(chuàng)建對(duì)象及析構(gòu);
先看看如何判斷類型列表中是否含有某種類型:
template < typename T, typename... List >
struct Contains : std::true_type {};
template < typename T, typename Head, typename... Rest >
struct Contains<T, Head, Rest...>
: std::conditional< std::is_same<T, Head>::value, std::true_type,
Contains<T, Rest...>>::type{};
template < typename T >
struct Contains<T> : std::false_type{};
通過(guò)bool值Contains<T, Types>::vaule就可以判斷是否含有某種類型。
再看看如何在緩沖區(qū)中創(chuàng)建對(duì)象。
通過(guò)placement new在該緩沖區(qū)上創(chuàng)建對(duì)象,new(data) T(value);其中data表示一個(gè)char緩沖區(qū),T表示某種類型。在緩沖區(qū)上創(chuàng)建的對(duì)象還必須通過(guò)~T去析構(gòu),因此還需要一個(gè)析構(gòu)vairiant的幫助類:
template<typename... Args>
struct VariantHelper;
template<typename T, typename... Args>
struct VariantHelper<T, Args...> {
inline static void Destroy(type_index id, void * data)
{
if (id == type_index(typeid(T)))
((T*) (data))->~T();
else
VariantHelper<Args...>::Destroy(id, data);
}
};
template<> struct VariantHelper<> {
inline static void Destroy(type_index id, void * data) { }
};
通過(guò)VariantHelper::Destroy函數(shù)就可以析構(gòu)variant了。
3.取值問(wèn)題
第三個(gè)問(wèn)題中需要解決取值問(wèn)題,如果發(fā)生異常,就打印出詳細(xì)信息。這個(gè)就比較簡(jiǎn)單了,看后面的實(shí)現(xiàn)代碼就行了。
c++11中完整的variant是如何實(shí)現(xiàn)的:
#include <typeindex>
#include <iostream>
#include <type_traits>
using namespace std;
template<typename T, typename... Args>
struct MaxType : std::integral_constant<int,
(sizeof(T)>MaxType<Args...>::value ? sizeof(T) : MaxType<Args...>::value) >
{};
template<typename T>
struct MaxType<T> : std::integral_constant<int, sizeof(T) >{};
template < typename T, typename... List >
struct Contains : std::true_type {};
template < typename T, typename Head, typename... Rest >
struct Contains<T, Head, Rest...>
: std::conditional< std::is_same<T, Head>::value, std::true_type, Contains<T,
Rest...>>::type{};
template < typename T >
struct Contains<T> : std::false_type{};
template<typename... Args>
struct VariantHelper;
template<typename T, typename... Args>
struct VariantHelper<T, Args...> {
inline static void Destroy(type_index id, void * data)
{
if (id == type_index(typeid(T)))
((T*) (data))->~T();
else
VariantHelper<Args...>::Destroy(id, data);
}
};
template<> struct VariantHelper<> {
inline static void Destroy(type_index id, void * data) { }
};
template<typename... Types>
class Variant
{
typedef VariantHelper<Types...> Helper_t;
public:
Variant(void) :m_typeIndex(typeid(void))
{
}
~Variant()
{
Helper_t::Destroy(m_typeIndex, &m_data);
}
template<typename T>
bool Is()
{
return (m_typeIndex == type_index(typeid(T)));
}
template<typename T>
T& Get()
{
if (!Is<T>())
{
cout << typeid(T).name() << " is not defined. " << "current type is " <<
m_typeIndex.name() << endl;
throw std::bad_cast();
}
return *(T*) (&m_data);
}
template <class T,
class = typename std::enable_if<Contains<typename
std::remove_reference<T>::type, Types...>::value>::type>
Variant(T&& value) : m_typeIndex(type_index(typeid(T)))
{
Helper_t::Destroy(m_typeIndex, &m_data);
typedef typename std::remove_reference<T>::type U;
new(m_data) U(std::forward<T>(value));
}
private:
char m_data[MaxType<Types...>::value];
std::type_index m_typeIndex;
};
測(cè)試代碼:
void TestVariant()
{
typedef Variant<int, char, double> cv;
int x = 10;
cv v =x;
v = 1;
v = 1.123;
v = "";//compile error
v.Get<int>(); //1
v.Get<double>(); //1.23
v.Get<short>(); //exception: short is not defined. current type is
int.
v.Is<int>();//true
}
總結(jié):c++11實(shí)現(xiàn)的variant在用法上和boost.variant保持一致,但實(shí)現(xiàn)更簡(jiǎn)潔,50行代碼搞定。而且還能在拋出異常時(shí)提示詳細(xì)信息,方便調(diào)試。
c++11 boost技術(shù)交流群:296561497,歡迎大家來(lái)交流技術(shù)。
相關(guān)文章
畢業(yè)論文-計(jì)算機(jī)論文注意事項(xiàng)
畢業(yè)論文-計(jì)算機(jī)論文注意事項(xiàng)...2007-03-03基于MEF打造的插件系統(tǒng)的實(shí)現(xiàn)詳解
最好自己動(dòng)手實(shí)踐下MEF,不過(guò)講的都是MEF的基礎(chǔ),希望對(duì)你有所幫助2013-05-05畢業(yè)論文-中小企業(yè)辦公自動(dòng)化系統(tǒng)的開發(fā)
畢業(yè)論文-中小企業(yè)辦公自動(dòng)化系統(tǒng)的開發(fā)...2007-03-03C語(yǔ)言如何實(shí)現(xiàn)計(jì)算器簡(jiǎn)單混合運(yùn)算功能
這篇文章主要給大家介紹了關(guān)于C語(yǔ)言如何實(shí)現(xiàn)計(jì)算器簡(jiǎn)單混合運(yùn)算功能的相關(guān)資料,這個(gè)計(jì)算器程序可以執(zhí)行基本的數(shù)學(xué)運(yùn)算,例如加法、減法、乘法和除法,需要的朋友可以參考下2007-11-11畢業(yè)論文-計(jì)算機(jī)軟件知識(shí)產(chǎn)權(quán)保護(hù)所面臨的挑戰(zhàn)及對(duì)策
畢業(yè)論文-計(jì)算機(jī)軟件知識(shí)產(chǎn)權(quán)保護(hù)所面臨的挑戰(zhàn)及對(duì)策...2007-03-03畢業(yè)論文-電子商務(wù)罪在何處?--淺談?dòng)嘘P(guān)電子商務(wù)認(rèn)識(shí)的幾個(gè)誤區(qū)
畢業(yè)論文-電子商務(wù)罪在何處?--淺談?dòng)嘘P(guān)電子商務(wù)認(rèn)識(shí)的幾個(gè)誤區(qū)...2007-03-03畢業(yè)論文-客戶關(guān)系管理與數(shù)據(jù)挖掘技術(shù)綜述
畢業(yè)論文-客戶關(guān)系管理與數(shù)據(jù)挖掘技術(shù)綜述...2007-03-03