C++11 模板元編程 - TypeList高階算法


針對list的高階算法,是允許用戶在使用時傳入別的元函數(shù)做參數(shù)的元函數(shù)。在函數(shù)式語言里對于list的高階元函數(shù)(如經(jīng)典的filter,mapfold...),可以輕松地讓用戶通過函數(shù)組合來擴展對list的操作。

下面我們先實現(xiàn)一個簡單的__any()高階元函數(shù),它接收一個list以及一個單參元函數(shù)Pred做入?yún)ⅲㄔ撛瘮?shù)接受一個類型,返回一個BoolType類型);__any()對list中每個元素調(diào)用Pred,如果Pred對某一個元素返回TrueType,則__any()返回TrueType;否則如果Pred對所有的元素都返回FalseType,則__any()返回FalseType。

// "tlp/list/algo/Any.h"

template<typename TL, template<typename T> class Pred> struct Any;

template<template<typename T> class Pred>
struct Any<NullType, Pred>
{
    using Result = FalseType;
};

template<typename Head, typename Tail, template<typename T> class Pred>
struct Any<TypeElem<Head, Tail>, Pred>
{
    using Result = typename IfThenElse<typename Pred<Head>::Result, TrueType, typename Any<Tail, Pred>::Result>::Result;
};

#define __any(...)   typename Any<__VA_ARGS__>::Result

__any()的遞歸實現(xiàn)中,對list的頭元素調(diào)用Pred:Pred<Head>::Result,如果為真,則返回TrueType;否則對list的剩余尾list繼續(xù)調(diào)用__any()Any<Tail, Pred>::Result>::Result;一直遞歸到空list,最后返回FalseType。

在這里我們用了元函數(shù)IfThenElse,由于它的惰性,我們可以在Pred為當前元素求值為TrueType的時候提前停止遞歸,后面的元素就不用再算了。另外也可以這樣實現(xiàn):using Result = __bool(__value(typename Pred<Head>::Result) ? true : __value(typename Any<Tail, Pred>::Result))。這種實現(xiàn)用了三元表達式,但由于這種運行時技術缺乏惰性,所以實際上每個元素都被求值了!

接下來我們實現(xiàn)元函數(shù)__all()。它的入?yún)⒑?code>any()一樣,差別在于所有的元素應用Pred后都返回TrueType,__all()才會返回TrueType;只要有一個元素應用Pred后返回FalseType,則__all()返回FalseType。

參考了__any()的實現(xiàn),我們輕松實現(xiàn)__all()如下:

template<typename TL, template<typename T> class Pred> struct All;

template<template<typename T> class Pred>
struct All<NullType, Pred>
{
    using Result = TrueType;
};

template<typename Head, typename Tail, template<typename T> class Pred>
struct All<TypeElem<Head, Tail>, Pred>
{
    using Result = typename IfThenElse<typename Pred<Head>::Result, typename All<Tail, Pred>::Result, FalseType>::Result;
};

#define __all(...)   typename All<__VA_ARGS__>::Result

可以看到,__all()__any()的實現(xiàn)非常相像!事實上,我們可以借助__any()來實現(xiàn)__all(),從而消除這兩者之間的重復。

首先我們需要借助__any()計算list中是否有某一元素應用Pred后為假,如果沒有一個為假,則__all()返回真。我們知道客戶傳入給__all()的Pred是一個單參元函數(shù),它在滿足條件后返回真。我們需要把客戶傳入的Pred轉變成另一個在滿足條件后返回假的單參元函數(shù)。于是我們實現(xiàn)了NegativeOf元函數(shù),它是一個高階函數(shù),接受一個返回值是BoolType的單參元函數(shù),返回一個取反之后的單參元函數(shù)。

// tlp/func/NegativeOf.h

template<template<typename T> class Pred>
struct NegativeOf
{
    template<typename U>
    struct Apply
    {
        using Result = typename Not<typename Pred<U>::Result>::Result;
    };
};

#define __negative_of(...)  NegativeOf<__VA_ARGS__>::template Apply

最終__all()的新版本實現(xiàn)如下:

// "tlp/list/algo/All.h"

template<typename TL, template<typename T> class Pred>
struct All
{
    using Result = typename Not<typename Any<TL, NegativeOf<Pred>::template Apply>::Result>::Result;
};

TLP庫自身的實現(xiàn)中對元函數(shù)的調(diào)用都使用的是其模板格式。如果使用每個元函數(shù)封裝過后的宏格式的話,實現(xiàn)如下:

template<typename TL, template<typename T> class Pred>
struct All
{
    using Result = __not(__any(TL, __negative_of(Pred)));
};

相關的測試用例如下:

FIXTURE(TestAdvancedAlgo)
{
    __func_forward_1(IsLargerThan2Bytes, __bool((sizeof(_1) > 2)));

    TEST("any one of the list satisfied the given prediction")
    {
        ASSERT_TRUE(__any(__type_list(char, short, int), IsLargerThan2Bytes));
    };

    TEST("none of the list satisfied the given prediction")
    {
        ASSERT_FALSE(__any(__type_list(char, short), IsLargerThan2Bytes));
    };

    TEST("all of the list satisfied the given prediction")
    {
        ASSERT_TRUE(__all(__type_list(int, long), IsLargerThan2Bytes));
    };

    TEST("any of the type in list not satisfied the given prediction")
    {
        ASSERT_FALSE(__all(__type_list(int, long, short), IsLargerThan2Bytes));
    };
}

上面的fixture中我們使用前面介紹過的__func_forward_1定義了一個單參元函數(shù)IsLargerThan2Bytes,它會判斷入?yún)㈩愋偷膕ize是否大于兩個字節(jié)。

接下來我們再來實現(xiàn)一個高階元函數(shù)__map(),它的用法如下面的測試用例:

TEST("map the list to it's size value list")
{
    __func_forward_1(TypeSize, __int(sizeof(_1)));

    using List = __type_list(int, char, short, long);
    using Expected = __value_list(4, 1, 2, 8);

    ASSERT_EQ(__map(List, TypeSize), Expected);
};

__map()的入?yún)⑿枰粋€list和一個類型映射元函數(shù)。它會對入?yún)ist的每個元素調(diào)用映射函數(shù),最終將list映射成一個新的list返回。__map()可以組合使用,如下:

TEST("map the type in list to it's twice pointer type list")
{
    template<typename T> struct TransToPointer { using Result = T*; };

    using List = __type_list(int, const char);
    using Expected = __type_list(int**, const char**);

    ASSERT_EQ(__map(__map(List, TransToPointer), TransToPointer), Expected);
};

__map()的實現(xiàn)如下:

template<typename TL, template<typename T> class Func> struct Map;

template<template<typename T> class Func>
struct Map<NullType, Func>
{
    using Result = NullType;
};

template<typename Head, typename Tail, template<typename T> class Func>
struct Map<TypeElem<Head, Tail>, Func>
{
    using Result = TypeElem<typename Func<Head>::Result, typename Map<Tail, Func>::Result>;
};

#define __map(...) typename Map<__VA_ARGS__>::Result

TLP庫一共實現(xiàn)了如下針對list的高階元函數(shù):

  • __any(List, Pred(T)):對List中每個元素調(diào)用Pred,如果有一個返回TrueType,則__any返回TrueType,否則返回FalseType;

  • __all(List, Pred(T)):對List中每個元素調(diào)用Pred,如果有所有都返回TrueType,則__all返回TrueType,否則返回FalseType;

  • __transform(List1, List2, Func(T1, T2)):遍歷List1和List2,對兩個list中每對元素調(diào)用Func,根據(jù)Func的返回值類型組成一共新的List返回;

  • __filter(List, Pred(T)):過濾List中所有調(diào)用Pred為真得類型,組成一個新的List返回;

  • __map(List, Func(T)):將List中每個元素調(diào)用Func映射成一個新的類型,返回映射后類型組成的新List;

  • __fold(List, Acc, Func(T1, T2)):折疊算法;將List所有元素按照Func給的算法折疊成一個值返回,Acc是折疊啟動參數(shù);

  • __sort(List, Compare(T1, T2)):將List按照傳入的Compare規(guī)則進行排序,返回排序后的新List;

上述沒有介紹到的List的高階算法的實現(xiàn)在這里就不再詳述了,感興趣的話請直接閱讀TLP的源碼。TLP的“tlp/test/TestList.cpp”文件中有這些元函數(shù)的詳細測試,通過測試也可以看到它們的詳細用法。

最后,高階元函數(shù)的強大之處是可以讓代碼在更高的抽象層次上進行復用和組合,通過更貼近領域的語義描述出操作意圖。如下測試用例中,我們想要計算一組類型中大于兩字節(jié)的所有類型的size之和,我們使用前面介紹的高階元函數(shù)進行組合來實現(xiàn):

TEST("calculate the total size of the types that larger than 2 bytes")
{
    using List = __type_list(char, short, int, long, char*, short*, int*, long*);

    ASSERT_EQ(__fold(__map(__filter(List, IsLargerThan2Bytes), TypeSize) , __int(0), Add), __int(44));
};

上述測試是在64位操作系統(tǒng)上的計算結果(指針、int和long都是8字節(jié),short是4字節(jié))。在計算中先通過__filter()在list中過濾出所有大于2字節(jié)的類型,然后通過__map()將過濾后的類型列表映射成對應的size數(shù)值列表,最后通過__fold()元函數(shù)進行累計求和。上述所有操作的抽象層次都將list作為一個整體,沒有降級到針對list內(nèi)部進行算法描述。


TypeList應用舉例

返回 C++11模板元編程 - 目錄

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容