前往
大廳
主題

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

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


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

創作回應

更多創作