經常會有一些類庫或者API要求傳入一個等效于全局函數的函數指針作為回調函數,一個典型的例子是Win32的建立線程

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

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

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


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

DirectX SDK以前就用過這樣的方法,也可以把全局函數換成類的靜態成員函數,

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

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

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


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

因為PtrToMemThreadFun的類型不定,所以只能先外包一層模板類來確定類型,像這樣

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)();
    }

}
;

調用時,就可以自動化了

// 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);