C++ 模板元中巧用异常让字符串储存在指定类型的tuple中

1 篇文章 0 订阅


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;
           说了这些,其实是想说,异常有时候不仅仅只是标识错误的发生,他还能够帮我们解决很多事,我就不说以前就干过用异常来传递数据的事了。





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值