C++ 模板元中巧用异常让字符串储存在指定类型的tuple中
2016-02-17 15:47
513 查看
数据储存在文本中那么都是字符串,所以当需要处理数据的时候需要将字符串转换成相应的类型,这本来没什么,只需要知道类型即可转换,那么如何将一个字符串转换到指定类型的tuple中呢?因为把参数放进数据放进tuple中有很多好处,简单点说在操作lua函数中可以带来很大的便利,好吧,就算不在C++中使用lua,那么统一管理数据也是很好的,再比如在操纵函数过程中用来打包函数参数也是很好的.
假如有一个函数需要int,float,float的参数,而这些参数我们大概可能是从文件中读取,那么他们是字符串,通常一般大概都会这么做,知道参数类型,将字符串转换成相应的类型的数据,然后调用函数,问题来了,这样硬编码是不是真的很好呢?
如果我们想要达到上面的效果,我们通常会根据函数特性来根据参数类型把字符串转换到tuple,在将tuple解包得到我们要的函数执行所必须的参数即可,当然这些都在内部函数的调用中完成,所以比较简单,这里就不多说,下面我们来看看怎么将那个储存参数的tuple提取出来。
嗯,为什么说把储存参数的tuple提取出来需要拿出来说一下呢?因为tuple的操作都是使用了大量的模板,其实就是tuple就是一个跨越编译期和运行期的一个模板元应用,所以操作tuple不能像操作一般的容器对待,这是因为每一次操作tuple其实都是在操作一个临时的类型不同的对象。简单点说,你要检查tuple的每一个数据的类型都是一个模板的不同实例化:
同样是获取tuple中元素的类型,但是不同的元素就是使用tuple_element的不同实例化,也就是说std::tuple_element<0,std::tuple<...>> 和 std::tuple_element<1,std::tuple<...>> 就像大圣和白龙马一样没啥关系。而问题是我们想要让字符串转换到指定的tuple中我们就要知道tuple中每一个元素的类型。所以,这样一来,还是逃离不了使用模板元的节奏。
好吧,到现在为止,我们确实是将字符串的内容转换到tuple里面了,mT就是储存结果的,但是问题来了,我们该怎么获取这最后一个mT呢?
当然下面的使用方式是不可以的:
就算通过各种技巧将返回类型给推导出来,那代码大概也是相当难看的,之所以不这么做,是因为可以很简单的获取我们想要的结果,那就是异常,只需要在递归终止时将结果当做异常抛出来即可:
然后在ToTuple函数中抓一下异常:
有木有觉得很巧妙呢?在对输出流操作符重载一下让他可以接受输入是不是更好呢?
现在可以这么来对tuple进行操作,还能够从文件中读取数据并保存在指定的类型中:
std::tuple_element<0,std::tuple<...>>::type std::tuple_element<1,std::tuple<...>>::type
数据储存在文本中那么都是字符串,所以当需要处理数据的时候需要将字符串转换成相应的类型,这本来没什么,只需要知道类型即可转换,那么如何将一个字符串转换到指定类型的tuple中呢?因为把参数放进数据放进tuple中有很多好处,简单点说在操作lua函数中可以带来很大的便利,好吧,就算不在C++中使用lua,那么统一管理数据也是很好的,再比如在操纵函数过程中用来打包函数参数也是很好的.
假如有一个函数需要int,float,float的参数,而这些参数我们大概可能是从文件中读取,那么他们是字符串,通常一般大概都会这么做,知道参数类型,将字符串转换成相应的类型的数据,然后调用函数,问题来了,这样硬编码是不是真的很好呢?
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); // 使用输入的参数调用函数
如果我们想要达到上面的效果,我们通常会根据函数特性来根据参数类型把字符串转换到tuple,在将tuple解包得到我们要的函数执行所必须的参数即可,当然这些都在内部函数的调用中完成,所以比较简单,这里就不多说,下面我们来看看怎么将那个储存参数的tuple提取出来。
嗯,为什么说把储存参数的tuple提取出来需要拿出来说一下呢?因为tuple的操作都是使用了大量的模板,其实就是tuple就是一个跨越编译期和运行期的一个模板元应用,所以操作tuple不能像操作一般的容器对待,这是因为每一次操作tuple其实都是在操作一个临时的类型不同的对象。简单点说,你要检查tuple的每一个数据的类型都是一个模板的不同实例化:
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<...>> 就像大圣和白龙马一样没啥关系。而问题是我们想要让字符串转换到指定的tuple中我们就要知道tuple中每一个元素的类型。所以,这样一来,还是逃离不了使用模板元的节奏。
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; // 这里不是我们想要的结果 }
好吧,到现在为止,我们确实是将字符串的内容转换到tuple里面了,mT就是储存结果的,但是问题来了,我们该怎么获取这最后一个mT呢?
当然下面的使用方式是不可以的:
inline auto apply(std::vector<MString>& v)->decltype(...............) { return mT; }
就算通过各种技巧将返回类型给推导出来,那代码大概也是相当难看的,之所以不这么做,是因为可以很简单的获取我们想要的结果,那就是异常,只需要在递归终止时将结果当做异常抛出来即可:
inline auto apply(std::vector<MString>& v)->decltype(...............) { throw mT; }
然后在ToTuple函数中抓一下异常:
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; // 这里就是我们想要的结果 } catch (...){ return output; // 如果得到的结果不是我们想要的 } }
有木有觉得很巧妙呢?在对输出流操作符重载一下让他可以接受输入是不是更好呢?
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; }
现在可以这么来对tuple进行操作,还能够从文件中读取数据并保存在指定的类型中:
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;说了这些,其实是想说,异常有时候不仅仅只是标识错误的发生,他还能够帮我们解决很多事,我就不说以前就干过用异常来传递数据的事了。
相关文章推荐
- 自己用C++写的图像处理软件试用版下载
- c++ 惯用法:定界加锁
- C语言 预处理一(文件包含--#include)
- C++简单实现对象引用计数示例
- 从头再来,C++学习笔记。-01(初窥C++)
- 递归字符串C++编程(1)
- UVa 10474 Where is the Marble?
- 合并石子c++
- leetcode刷题系列C++-Longest Consecutive Sequence
- c++ vector赋值
- C++中的异常操作:throw&try&catch
- c++内存分配
- C++结构(structure)详解
- C语言enum枚举类型解析
- c++ char 运算
- C语言中extern的用法
- hadoop2.7伪分布式模式运行C++程序
- C++ cin.getline()函数的使用
- 线性表详解(c语言版)
- python是c语言开发的