紀錄C++11的Template Metaprogramming (樣板元编程)練習範例
影片00:04,constexpr最晚在程式編譯期(compile time)就要給值,無法在執行期間(run time)動態給值,
__FILE__是C的標準語法,也能在編譯期就從完整檔案路徑取出檔名,並儲存在宣告為constexpr的字串內,
還能在編譯期算出檔名字串長度(strlen),並儲存在宣告為constexpr的int內
影片00:12,__FUNCTION__好像是gcc特有的語法,非標準,不知為何,儲存在constexpr char array會跳錯,
但能用strlen算出長度,並儲存在宣告為constexpr的int內 (也可能是gcc版本(4.8.5)太舊造成)
影片01:23,編譯期算出long long以下整數數值型態的最大值(或最小值)的字串長度(*MaxStringLength),
以及儲存字串所需的陣列大小(*MaxArraySize,StringLength+1,結尾要多給0代表字串結束)
影片01:52,範例,多維陣列在編譯期給值
影片02:07,編譯期從完整檔案路徑取出檔名的程式碼(GetFileName)
加映把__LINE__,__FUNCTION__,__FILE__打包成_LOG_POS_的程式碼,
遺憾這段有部分是執行期計算,還在研究有沒有辦法都優化成編譯期計算
影片02:48,編譯期給定long long範圍內的正整數或負整數,計算出字串長度
Template Metaprogramming (樣板元编程)應用面極廣,雖然程式編譯上會多花一些時間,
但能大幅優化執行期效能(畢竟能提前計算的都盡量先算好並儲存在執行檔內了)
由於IDE的支援,程式編譯好後,滑鼠移到constexpr參數上,能直接看到計算後的值,
因此程式開發上,能節省很多(單元)測試和除錯的時間
可惜C++11的Template Metaprogramming功能受限,C++14/17/20陸陸續續增加了很多神奇的功能,
但C++11不失於新手入門Template Metaprogramming的起手式,
能先練習基本觀念和語法,不會太抽象,現今各平台的編譯器也已經實作很完整的支援
template<typename T, long long N = std::numeric_limits<T>::max()>
class NumberWidthMax
{
public:
enum
{
StringLength = NumberWidthMax<T, N / 10>::StringLength + (std::numeric_limits<T>::min() >= 0 ? 0 : 1),
ArraySize = NumberWidthMax<T, N / 10>::ArraySize + (std::numeric_limits<T>::min() >= 0 ? 0 : 1),
};
};
template<typename T>
class NumberWidthMax<T, 0>
{
public:
enum
{
StringLength = 1,
ArraySize = 1 + 1,
};
};
constexpr int BoolMaxStringLength = NumberWidthMax<bool>::StringLength;
constexpr int BoolMaxArraySize = NumberWidthMax<bool>::ArraySize;
constexpr int CharMaxStringLength = NumberWidthMax<char>::StringLength;
constexpr int CharMaxArraySize = NumberWidthMax<char>::ArraySize;
constexpr int ShortMaxStringLength = NumberWidthMax<short>::StringLength;
constexpr int ShortMaxArraySize = NumberWidthMax<short>::ArraySize;
constexpr int IntMaxStringLength = NumberWidthMax<int>::StringLength;
constexpr int IntMaxArraySize = NumberWidthMax<int>::ArraySize;
constexpr int LongMaxStringLength = NumberWidthMax<long long>::StringLength;
constexpr int LongMaxArraySize = NumberWidthMax<long long>::ArraySize;
template<unsigned int Len>
constexpr const char *GetFileName(const char (&fullPath)[Len], unsigned int pos)
{
return pos == 0 ? fullPath : (fullPath[pos] == '/' || fullPath[pos] == '\\') ? fullPath + pos + 1 : GetFileName(fullPath, pos - 1);
}
template<unsigned int Len>
constexpr const char *GetFileName(const char (&fullPath)[Len])
{
return GetFileName(fullPath, Len - 1);
}
thread_local char LogPosBuf[512];
#define _LOG_POS_ \
[] (const int& ln, const char *fn, const char *fi) \
{ \
snprintf(LogPosBuf, sizeof(LogPosBuf), "%d|%s|%s", ln, fn, fi); \
return LogPosBuf; \
} (__LINE__, __FUNCTION__, GetFileName(__FILE__))
{
char arr01[BoolMaxStringLength];
char arr02[BoolMaxArraySize];
char arr03[BoolMaxArraySize + BoolMaxStringLength];
char arr04[BoolMaxArraySize * 2 + BoolMaxStringLength];
char arr05[CharMaxStringLength];
char arr06[CharMaxArraySize];
char arr07[ShortMaxStringLength];
char arr08[ShortMaxArraySize];
char arr09[IntMaxStringLength];
char arr10[IntMaxArraySize];
char arr11[LongMaxStringLength];
char arr12[LongMaxArraySize];
printf("BoolMaxStringLength = %ld\r\n", sizeof(arr01));
printf("BoolMaxArraySize = %ld\r\n", sizeof(arr02));
printf("BoolMaxArraySize + BoolMaxStringLength = %ld\r\n", sizeof(arr03));
printf("BoolMaxArraySize * 2 + BoolMaxStringLength = %ld\r\n", sizeof(arr04));
printf("CharMaxStringLength = %ld\r\n", sizeof(arr05));
printf("CharMaxArraySize = %ld\r\n", sizeof(arr06));
printf("ShortMaxStringLength = %ld\r\n", sizeof(arr07));
printf("ShortMaxArraySize = %ld\r\n", sizeof(arr08));
printf("IntMaxStringLength = %ld\r\n", sizeof(arr09));
printf("IntMaxArraySize = %ld\r\n", sizeof(arr10));
printf("LongMaxStringLength = %ld\r\n", sizeof(arr11));
printf("LongMaxArraySize = %ld\r\n", sizeof(arr12));
printf("\r\n");
}
{
constexpr int n0 = YangToolBox::NumberWidth<0>::Value;
constexpr int n1 = YangToolBox::NumberWidth<1>::Value;
constexpr int n9 = YangToolBox::NumberWidth<9>::Value;
constexpr int n12 = YangToolBox::NumberWidth<12>::Value;
constexpr int n123 = YangToolBox::NumberWidth<123>::Value;
constexpr int n1234 = YangToolBox::NumberWidth<1234>::Value;
constexpr int n12345 = YangToolBox::NumberWidth<12345>::Value;
constexpr int n123456 = YangToolBox::NumberWidth<123456>::Value;
constexpr int n1234567 = YangToolBox::NumberWidth<1234567>::Value;
constexpr int n12345678 = YangToolBox::NumberWidth<12345678>::Value;
constexpr int n123456789 = YangToolBox::NumberWidth<123456789>::Value;
constexpr int n1234567890 = YangToolBox::NumberWidth<1234567890>::Value;
constexpr int n12345678901 = YangToolBox::NumberWidth<12345678901>::Value;
constexpr int n123456789012 = YangToolBox::NumberWidth<123456789012>::Value;
constexpr int n1234567890123 = YangToolBox::NumberWidth<1234567890123>::Value;
printf("0 string length = %d\r\n", n0);
printf("1 string length = %d\r\n", n1);
printf("9 string length = %d\r\n", n9);
printf("12 string length = %d\r\n", n12);
printf("123 string length = %d\r\n", n123);
printf("1234 string length = %d\r\n", n1234);
printf("12345 string length = %d\r\n", n12345);
printf("123456 string length = %d\r\n", n123456);
printf("1234567 string length = %d\r\n", n1234567);
printf("12345678 string length = %d\r\n", n12345678);
printf("123456789 string length = %d\r\n", n123456789);
printf("1234567890 string length = %d\r\n", n1234567890);
printf("12345678901 string length = %d\r\n", n12345678901);
printf("123456789012 string length = %d\r\n", n123456789012);
printf("1234567890123 string length = %d\r\n", n1234567890123);
printf("\r\n");
}
{
constexpr int nm0 = YangToolBox::NumberWidth<-0>::Value;
constexpr int nm1 = YangToolBox::NumberWidth<-1>::Value;
constexpr int nm9 = YangToolBox::NumberWidth<-9>::Value;
constexpr int nm12 = YangToolBox::NumberWidth<-12>::Value;
constexpr int nm123 = YangToolBox::NumberWidth<-123>::Value;
constexpr int nm1234 = YangToolBox::NumberWidth<-1234>::Value;
constexpr int nm12345 = YangToolBox::NumberWidth<-12345>::Value;
constexpr int nm123456 = YangToolBox::NumberWidth<-123456>::Value;
constexpr int nm1234567 = YangToolBox::NumberWidth<-1234567>::Value;
constexpr int nm12345678 = YangToolBox::NumberWidth<-12345678>::Value;
constexpr int nm123456789 = YangToolBox::NumberWidth<-123456789>::Value;
constexpr int nm1234567890 = YangToolBox::NumberWidth<-1234567890>::Value;
constexpr int nm12345678901 = YangToolBox::NumberWidth<-12345678901>::Value;
constexpr int nm123456789012 = YangToolBox::NumberWidth<-123456789012>::Value;
constexpr int nm1234567890123 = YangToolBox::NumberWidth<-1234567890123>::Value;
printf("-0 string length = %d\r\n", nm0);
printf("-1 string length = %d\r\n", nm1);
printf("-9 string length = %d\r\n", nm9);
printf("-12 string length = %d\r\n", nm12);
printf("-123 string length = %d\r\n", nm123);
printf("-1234 string length = %d\r\n", nm1234);
printf("-12345 string length = %d\r\n", nm12345);
printf("-123456 string length = %d\r\n", nm123456);
printf("-1234567 string length = %d\r\n", nm1234567);
printf("-12345678 string length = %d\r\n", nm12345678);
printf("-123456789 string length = %d\r\n", nm123456789);
printf("-1234567890 string length = %d\r\n", nm1234567890);
printf("-12345678901 string length = %d\r\n", nm12345678901);
printf("-123456789012 string length = %d\r\n", nm123456789012);
printf("-1234567890123 string length = %d\r\n", nm1234567890123);
printf("\r\n");
}
231104補充,樣板元编程的語法
const T (&arr)[N]
N是陣列大小,之前寫成
const char (&fullPath)[Len]
Len命名不太適合,應該改成
const char (&fullPath)[N]
Len容易和strlen搞混,以為是字串長度,
但其實是陣列大小,也就是字串長度+1 (結尾補0)
所以之前寫的GetFileName微修改成以下寫法較適合
template<unsigned int N>
constexpr const char *GetFileName(const char (&fullPath)[N], unsigned int pos)
{
return pos == 0 ? fullPath : (fullPath[pos] == '/' || fullPath[pos] == '\\') ? fullPath + pos + 1 : GetFileName(fullPath, pos - 1);
}
template<unsigned int N>
constexpr const char *GetFileName(const char (&fullPath)[N])
{
return GetFileName(fullPath, N - 1);
}