經(jīng)常會有一些類庫或者API要求傳入一個(gè)等效于全局函數(shù)的函數(shù)指針作為回調(diào)函數(shù),一個(gè)典型的例子是Win32的建立線程

DWORD WINAPI ThreadFunc(LPVOID lpParameter);
HANDLE hThread 
= CreateThread(NULL, NULL, ThreadFunc, NULL, NULL, NULL);

然而,對于我們自己的工程來說,更希望作為線程的函數(shù)是某個(gè)類的成員函數(shù),所以需要在這個(gè)全局函數(shù)里調(diào)用類的成員函數(shù),像這樣

ClassA a;
DWORD WINAPI ThreadFunc(LPVOID lpParameter)
{
    
return ((ClassA*) lpParameter)->SomeMethod();
}


HANDLE hThread 
= CreateThread(NULL, NULL, ThreadFunc, &a, NULL, NULL);

DirectX SDK以前就用過這樣的方法,也可以把全局函數(shù)換成類的靜態(tài)成員函數(shù),

但這樣用起來很麻煩,每次都要寫一個(gè)全局函數(shù)的Shell,所以這里,我們想辦法把這個(gè)過程自動化起來.

問題的關(guān)鍵是在靜態(tài)或者全局的函數(shù)里,只能得到一個(gè)傳進(jìn)來的參數(shù),而要指定一個(gè)成員函數(shù),需要一個(gè)this指針,以及一個(gè)指向類成員函數(shù)的指針,這兩個(gè)參數(shù)沒法全部通過lpParameter傳進(jìn)來,除非lpParameter傳一個(gè)額外寫的結(jié)構(gòu)的指針,這個(gè)結(jié)構(gòu)里包含this指針和成員函數(shù)指針,但這樣需要臨時(shí)分配一個(gè)對象,不方便,所以只能犧牲運(yùn)行時(shí)代碼的簡潔性,用模板參數(shù)來傳成員函數(shù)指針的值,而lpParameter只負(fù)責(zé)傳this,但是另一個(gè)問題是用模板參數(shù)來傳具體的值時(shí),必須類型已知,像下面的代碼是不能運(yùn)作的

template<typename T, PtrToMemThreadFun pFunc>
DWORD WINAPI ThreadFunc(LPVOID lpParameter)
{
    
return (((T*)lpParameter)->*pFunc)();
}


CreateThread(NULL, NULL, ThreadFunc
<ClassA, &ClassA::SomeMethod>&a, NULL, NULL);

因?yàn)镻trToMemThreadFun的類型不定,所以只能先外包一層模板類來確定類型,像這樣

template<typename T>
struct ThreadFac
{
    typedef DWORD (T::
*PtrToMemThreadFunc)();
    typedef T ClassType;

    template
<PtrToMemThreadFunc pFunc>
    
static DWORD WINAPI ThreadFunc(LPVOID lpParameter)
    
{
        
return (((ClassType*)lpParameter)->*pFunc)();
    }

}
;

調(diào)用時(shí),就可以自動化了

// Globally
CreateThread(NULL, NULL, ThreadFac<ClassA>::ThreadFunc<&ClassA::SomeMethod>&a, NULL, NULL);

// In methods of ClassA
CreateThread(NULL, NULL, ThreadFac<ClassA>::ThreadFunc<&ClassA::SomeMethod>this, NULL, NULL);