這一講的內(nèi)容和我們的框架有些偏,,但是他實現(xiàn)基礎(chǔ)實現(xiàn)的一部分,,這一講的主題應(yīng)該叫巧用C 的異常機制配合模板元技巧將一定格式的字符串轉(zhuǎn)換到指定類型的tuple中。 大家都知道字符串是沒有類型的,,或者說他是一種更為一般的泛型,,凡是可以通過流序列化的對象即可轉(zhuǎn)換為字符的形式,比如“123”是可以轉(zhuǎn)換到double,同樣可以轉(zhuǎn)換到int等等,。而C 的tuple卻又是強類型的東西,,這兩者的區(qū)別可以是千差萬別,而我們今天的主題是要將兩者結(jié)合在一起,。 std::tuple_element<0,std::tuple<...>>::type std::tuple_element<1,std::tuple<...>>::type 上面的方式是我們獲取tuple元素類型的方法,。下面進入主題。 數(shù)據(jù)儲存在文本中那么都是字符串,,所以當(dāng)需要處理數(shù)據(jù)的時候需要將字符串轉(zhuǎn)換成相應(yīng)的類型,,這本來沒什么,只需要知道類型即可轉(zhuǎn)換,,那么如何將一個字符串轉(zhuǎn)換到指定類型的tuple中呢,?因為把參數(shù)放進數(shù)據(jù)放進tuple中有很多好處,簡單點說在操作lua函數(shù)中可以帶來很大的便利,,好吧,,就算不在C 中使用lua,那么統(tǒng)一管理數(shù)據(jù)也是很好的,,再比如在操縱函數(shù)過程中用來打包函數(shù)參數(shù)也是很好的. 假如有一個函數(shù)需要int,,float,float的參數(shù),,而這些參數(shù)我們大概可能是從文件中讀取,,那么他們是字符串,通常一般大概都會這么做,,知道參數(shù)類型,,將字符串轉(zhuǎn)換成相應(yīng)的類型的數(shù)據(jù),然后調(diào)用函數(shù),,問題來了,,這樣硬編碼是不是真的很好呢? void test(int i, double b){ std::cout << i << std::endl; std::cout << b << std::endl; } double test(int i, double b,double c){ std::cout << i << std::endl; std::cout << b << std::endl; std::cout << c << std::endl; return i*b*c; } typedef double(*F)(int, double, double); F __f = test; std::string str; std::cin>>str; ManualFun(__f, str); // 使用輸入的參數(shù)調(diào)用函數(shù) 如果我們想要達到上面的效果,,我們通常會根據(jù)函數(shù)特性來根據(jù)參數(shù)類型把字符串轉(zhuǎn)換到tuple,,在將tuple解包得到我們要的函數(shù)執(zhí)行所必須的參數(shù)即可,當(dāng)然這些都在內(nèi)部函數(shù)的調(diào)用中完成,,所以比較簡單,,這里就不多說,,下面我們來看看怎么將那個儲存參數(shù)的tuple提取出來,。 嗯,為什么說把儲存參數(shù)的tuple提取出來需要拿出來說一下呢,?因為tuple的操作都是使用了大量的模板,,其實就是tuple就是一個跨越編譯期和運行期的一個模板元應(yīng)用,所以操作tuple不能像操作一般的容器對待,這是因為每一次操作tuple其實都是在操作一個臨時的類型不同的對象,。簡單點說,,你要檢查tuple的每一個數(shù)據(jù)的類型都是一個模板的不同實例化:
std::tuple_element<0,std::tuple<...>>::type std::tuple_element<1,std::tuple<...>>::type 同樣是獲取tuple中元素的類型,但是不同的元素就是使用tuple_element的不同實例化,,也就是說std::tuple_element<0,std::tuple<...>> 和 std::tuple_element<1,std::tuple<...>> 就像大圣和白龍馬一樣沒啥關(guān)系,。而問題是我們想要讓字符串轉(zhuǎn)換到指定的tuple中我們就要知道tuple中每一個元素的類型。所以,,這樣一來,,還是逃離不了使用模板元的節(jié)奏。
template<size_t index,size_t M,class T> struct TypeConvert; template<size_t index, size_t M, class ... Args> struct TypeConvert<index, M, std::tuple<Args...>>{ typedef typename std::tuple_element<index, std::tuple<Args...>>::type Type; template<class T> struct Apply{ Apply(T t) :mT(t){} inline void apply(std::vector<MString>& v) { MString str = v.front(); v.erase(v.begin()); auto tt = std::tuple_cat(mT, std::make_tuple(str.ToOtherType<Type>())); TypeConvert<index 1, M, std::tuple<Args...>>::Apply<decltype(tt)>(tt).apply(v); } T mT; };
}; template<size_t M,class ... Args> struct TypeConvert<M,M,std::tuple<Args...>>{ typedef typename std::tuple_element<M-1, std::tuple<Args...>>::type Type; template<class T> struct Apply{ Apply(T t) :mT(t){} inline void apply(std::vector<MString>& v) { ; } T mT; };
};
template<class...Args> std::tuple<Args...>& Totuple(const std::string& str, std::tuple<Args...>& output){ MString Mstr = str; std::vector<MString> v; Mstr.split(' \t', v); if (v.size() < sizeof...(Args)){ return output; } TypeConvert<0, sizeof...(Args), std::tuple<Args...>>::Apply<std::tuple<>>(std::tuple<>()).apply(v); return output; // 這里不是我們想要的結(jié)果 } 好吧,,到現(xiàn)在為止,,我們確實是將字符串的內(nèi)容轉(zhuǎn)換到tuple里面了,mT就是儲存結(jié)果的,,但是問題來了,,我們該怎么獲取這最后一個mT呢?
當(dāng)然下面的使用方式是不可以的: inline auto apply(std::vector<MString>& v)->decltype(...............) { return mT; } 就算通過各種技巧將返回類型給推導(dǎo)出來,,那代碼大概也是相當(dāng)難看的,,之所以不這么做,是因為可以很簡單的獲取我們想要的結(jié)果,,那就是異常,,只需要在遞歸終止時將結(jié)果當(dāng)做異常拋出來即可:
inline auto apply(std::vector<MString>& v)->decltype(...............) { throw mT; } 然后在ToTuple函數(shù)中抓一下異常: template<class...Args> std::tuple<Args...>& Totuple(const std::string& str, std::tuple<Args...>& output){ MString Mstr = str; std::vector<MString> v; Mstr.split(' \t', v); // 使用空格來或者\t來分隔字符串 // MString是自己寫的一個字符串庫,使用boost里的算法也能夠完成這些操作 if (v.size() < sizeof...(Args)){ return output; } try{ TypeConvert<0, sizeof...(Args), std::tuple<Args...>>::Apply<std::tuple<>>(std::tuple<>()).apply(v); } catch (std::tuple<Args...> e){ output = std::forward<std::tuple<Args...>&&>(e); return output; // 這里就是我們想要的結(jié)果 } catch (...){ return output; // 如果得到的結(jié)果不是我們想要的 } } 有木有覺得很巧妙呢,?在對輸出流操作符重載一下讓他可以接受輸入是不是更好呢,? template<class...Args> inline std::istream& operator>>(std::istream& is, std::tuple<Args...>& t){ std::string str; std::getline(is, str); if (str.empty()) return is; t = Totuple(str, t); return is; } 現(xiàn)在可以這么來對tuple進行操作,還能夠從文件中讀取數(shù)據(jù)并保存在指定的類型中: std::tuple<int, float, float> t; Totuple('10 20.3 30.5',t); std::cout << t << std::endl; std::cin >> t; std::cout << t << std::endl;
//============================================ |
|