一、场景应用
在一个开发场景下,需要动态处理不同类型的数据写入。本来这个非常简单,只要定义一个模板即可搞定,但这里偏偏有一个细节,是调用别人的库来实现写入。而这个库对不同的数据类型的写入,提供了N种不同的函数,这些函数只是名字和写入数据类型不同,其它都完全一样。象下面的样子:
void putIntData(int *buf,int count,...);
void putCharData(char*buf,int count,...);
void putFloatData(float*buf,int count,...)
......
如果在上层应用调用这些函数,如下:
int * buf[1024] = {......};
int size = 1024;void SaveData(int *buf,uint32_t size)
{if(std::is_same_v<decltype(buf[0]),int>){putIntData(buf,size,...);}else if(std::is_same_v<decltype(buf[0]),char>){putIntData(buf,size,...);}else{}
}
这样的话编译无法通过,会报类型转换的错误。可上层的给定的缓冲区内的数据类型确实是可变的,做为一个中间处理层,如何能够正确的引导程序自动适配准确的函数调用呢?很容易想到使用模板。但是单纯的使用模板,仍然会报上面的错误。这也提醒,应该在编译期处理这个逻辑,理论上就会没有问题了。
二、分析
既然使用编译期来定位函数的调用,首先想到的使用用模板的特化来处理:
template <typename T> bool SaveDataSecond(T *buf, int size) {SaveCharData(buf, size);return true;
}
template <> bool SaveDataSecond(int *buf, int size) {SaveIntData(buf, size);return true;
}
template <> bool SaveDataSecond(float *buf, int size) {SaveFloatData(buf, size);return true;
}
int main() {// template specializationSaveDataSecond(buf, size);SaveDataSecond(fbuf, size);return 0;
}
然后可以想到学过的SFINAE,先考虑一下SFINAE的实现,最先想到的是std::enable_if系列:
#include <iostream>int buf[1024] = {0};
int size = 1024;float fbuf[1024] = {0.f};void SaveIntData(int *buf, int size, bool used = false) { std::cout << "save int type buffer!" << std::endl; }
void SaveCharData(char *buf, int size, bool used = false) { std::cout << "save char type buffer!" << std::endl; }
void SaveFloatData(float *buf, int size, bool used = false) { std::cout << "save float type buffer!" << std::endl; }template <typename T> std::enable_if_t<std::is_integral<T>::value, bool> SaveData(T *buf, int size) {SaveIntData(buf, size);return 0;
}
template <typename T> std::enable_if_t<std::is_floating_point<T>::value, bool> SaveData(T *buf, int size) {SaveFloatData(buf, size);return 0;
}
// void SaveData(int *buf, int size) {}
int main() {SaveData(buf, size);SaveData(fbuf, size);return 0;
}
看到这些代码是不是想到了std::is_same系列,即把is_floating_point等替换为std::is_same,如下:
template <typename T> std::enable_if_t<std::is_same_v<T, float>, bool> SaveData(T *buf, int size) {SaveFloatData(buf, size);return 0;
}
然后就是考虑一下在C++高版本(C++17及以后)有没有更好的解决办法即C++17后的if constexpr,如下面的代码用法:
if constexpr(std::is_same<T,int>::value){//call switch}
三、解决
最后的解决办法就是使用if constexpr在编译期处理函数的分支调用:
int * buf[1024] = {......};
int size = 1024;template<typename T>
void SaveData(T*buf,uint32_t size)
{if constexpr(std::is_same_v<T,int>){putIntData(buf,size,...);}else if constexpr(std::is_same_v<T,char>){putIntData(buf,size,...);}else{}
}
这种方法实现起来既简单又容易理解,类似的问题,都可以使用这种方式来处理。
四、总结
技术的前进一般是迭代推进的。完全全新的知识点,往往很少。要关于对老的知识点的综合应用并不断的总结这种用法的可用之处,从而不断的推导出问题的解决办法。再通过这种解决办法可以看新的标准中是否有类似的更方便的方法,就可以更好的理解和认知一些技术点。从而可以更好的更深入的掌握它。与诸君共勉!