经常会有一些类库或者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);