一、場景應用
在一個開發場景下,需要動態處理不同類型的數據寫入。本來這個非常簡單,只要定義一個模板即可搞定,但這里偏偏有一個細節,是調用別人的庫來實現寫入。而這個庫對不同的數據類型的寫入,提供了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{}
}
這種方法實現起來既簡單又容易理解,類似的問題,都可以使用這種方式來處理。
四、總結
技術的前進一般是迭代推進的。完全全新的知識點,往往很少。要關于對老的知識點的綜合應用并不斷的總結這種用法的可用之處,從而不斷的推導出問題的解決辦法。再通過這種解決辦法可以看新的標準中是否有類似的更方便的方法,就可以更好的理解和認知一些技術點。從而可以更好的更深入的掌握它。與諸君共勉!