ETH官方钱包

前往
大廳
主題

Constexpr and Template Metaprogramming (樣板元編程) in C++11 Example1

Yang | 2023-03-19 10:59:20 | 巴幣 2 | 人氣 263


紀(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);
}
送禮物贊助創(chuàng)作者 !
0
留言

創(chuàng)作回應(yīng)

相關(guān)創(chuàng)作

更多創(chuàng)作