紀(jì)錄C++11的Template Metaprogramming (樣板元編程)練習(xí)範(fàn)例
影片00:04,constexpr最晚在程式編譯期(compile time)就要給值,無(wú)法在執(zhí)行期間(run time)動(dòng)態(tài)給值,
__FILE__是C的標(biāo)準(zhǔn)語(yǔ)法,也能在編譯期就從完整檔案路徑取出檔名,並儲(chǔ)存在宣告為constexpr的字串內(nèi),
還能在編譯期算出檔名字串長(zhǎng)度(strlen),並儲(chǔ)存在宣告為constexpr的int內(nèi)
影片00:12,__FUNCTION__好像是gcc特有的語(yǔ)法,非標(biāo)準(zhǔn),不知為何,儲(chǔ)存在constexpr char array會(huì)跳錯(cuò),
但能用strlen算出長(zhǎng)度,並儲(chǔ)存在宣告為constexpr的int內(nèi) (也可能是gcc版本(4.8.5)太舊造成)
影片01:23,編譯期算出long long以下整數(shù)數(shù)值型態(tài)的最大值(或最小值)的字串長(zhǎng)度(*MaxStringLength),
以及儲(chǔ)存字串所需的陣列大小(*MaxArraySize,StringLength+1,結(jié)尾要多給0代表字串結(jié)束)
影片01:52,範(fàn)例,多維陣列在編譯期給值
影片02:07,編譯期從完整檔案路徑取出檔名的程式碼(GetFileName)
加映把__LINE__,__FUNCTION__,__FILE__打包成_LOG_POS_的程式碼,
遺憾這段有部分是執(zhí)行期計(jì)算,還在研究有沒(méi)有辦法都優(yōu)化成編譯期計(jì)算
影片02:48,編譯期給定long long範(fàn)圍內(nèi)的正整數(shù)或負(fù)整數(shù),計(jì)算出字串長(zhǎng)度
Template Metaprogramming (樣板元編程)應(yīng)用面極廣,雖然程式編譯上會(huì)多花一些時(shí)間,
但能大幅優(yōu)化執(zhí)行期效能(畢竟能提前計(jì)算的都盡量先算好並儲(chǔ)存在執(zhí)行檔內(nèi)了)
由於IDE的支援,程式編譯好後,滑鼠移到constexpr參數(shù)上,能直接看到計(jì)算後的值,
因此程式開(kāi)發(fā)上,能節(jié)省很多(單元)測(cè)試和除錯(cuò)的時(shí)間
可惜C++11的Template Metaprogramming功能受限,C++14/17/20陸陸續(xù)續(xù)增加了很多神奇的功能,
但C++11不失於新手入門Template Metaprogramming的起手式,
能先練習(xí)基本觀念和語(yǔ)法,不會(huì)太抽象,現(xiàn)今各平臺(tái)的編譯器也已經(jīng)實(shí)作很完整的支援
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補(bǔ)充,樣板元編程的語(yǔ)法
const T (&arr)[N]
N是陣列大小,之前寫(xiě)成
const char (&fullPath)[Len]
Len命名不太適合,應(yīng)該改成
const char (&fullPath)[N]
Len容易和strlen搞混,以為是字串長(zhǎng)度,
但其實(shí)是陣列大小,也就是字串長(zhǎng)度+1 (結(jié)尾補(bǔ)0)
所以之前寫(xiě)的GetFileName微修改成以下寫(xiě)法較適合
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);
}