打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
第125讲 DirectX11Frame(5)

这一讲的内容和我们的框架有些偏,但是他实现基础实现的一部分,这一讲的主题应该叫巧用C 的异常机制配合模板元技巧将一定格式的字符串转换到指定类型的tuple中。


大家都知道字符串是没有类型的,或者说他是一种更为一般的泛型,凡是可以通过流序列化的对象即可转换为字符的形式,比如“123”是可以转换到double,同样可以转换到int等等。而C 的tuple却又是强类型的东西,这两者的区别可以是千差万别,而我们今天的主题是要将两者结合在一起。


std::tuple_element<0,std::tuple<...>>::type

std::tuple_element<1,std::tuple<...>>::type


上面的方式是我们获取tuple元素类型的方法。下面进入主题。


    数据储存在文本中那么都是字符串,所以当需要处理数据的时候需要将字符串转换成相应的类型,这本来没什么,只需要知道类型即可转换,那么如何将一个字符串转换到指定类型的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;

    


//============================================

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
如何设计一个简单的C++ ORM
第123讲DirectX11Frame(3)
从 C 98 到 C 17,元编程是如何演进的?|技术头条
C++雾中风景16:std::make_index_sequence, 来试一试新的黑魔法吧
他来了,他来了,C 17新特性精华都在这了
C 17 中那些值得关注的特性
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服