我尝试使用非静态类成员创建一个线程,如下所示:
template <class clName>
DWORD WINAPI StartThread(PVOID ptr) {
((clName*)(ptr))->testf(); // this is static member name I want to be able use different names with the same function
return 1;
}
class Thread {
private :
HANDLE native_handle = 0;
DWORD id = 0;
public :
template <class T,class U>
Thread(T U::*member,U* original); // I want to use different members with the same function
bool run();
}
template<class T,class U>
Thread::Thread(T U::*member, U* original)
{
native_handle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)StartThread<U>,original, CREATE_SUSPENDED, &id);
}
bool Thread::run() {
DWORD res = ResumeThread(native_handle);
if (res == (DWORD)-1) return false;
return true;
}
class testt {
public :
void testf() {
MessageBoxA(0, "working", "", 0);
}
void doIt() {
Thread t(&testt::testf,this);
t.run();
}
};
int main() {
testt tt;
tt.doIt();
}
如您所见,我只能运行特定成员,因此此方法不可移植,不能用于任何类或成员。
我知道我可以轻松地使用std::thread
,但我正在开发一个我不应该使用任何C ++运行库的项目,所以我正在创建new
/的包装器delete
,线程,文件I / O等。否则,我总是使用std::thread
,这太棒了。在这个项目中,我只能使用Win32 API。
答案 0 :(得分:1)
模板参数可以是pointer to member,因此您可以通过这种方式扩充StartThread
,并使Thread::Thread
的指向成员的参数成为模板参数。您不能向构造函数模板提供显式模板参数,因此您必须使用特殊的“tag”参数来传达它们:
template<class C,class T,T C::*P>
DWORD WINAPI StartThread(PVOID ptr) {
(static_cast<C*>(ptr)->*P)();
return 1;
}
template<class C,class T,T C::*P>
struct Tag {};
class Thread {
private :
HANDLE native_handle = 0;
DWORD id = 0;
public :
template<class C,class T,T C::*P>
Thread(Tag<C,T,P>,C*);
bool run();
};
template<class C,class T,T C::*P>
Thread::Thread(Tag<C,T,P>,C* original)
{
native_handle = CreateThread(0, 0, StartThread<C,T,P>,original,
CREATE_SUSPENDED, &id);
}
bool Thread::run() {
DWORD res = ResumeThread(native_handle);
if (res == (DWORD)-1) return false;
return true;
}
class testt {
public :
void testf() {
MessageBoxA(0, "working", "", 0);
}
void doIt() {
Thread t(Tag<testt,void(),&testt::testf>(),this);
t.run();
}
};
int main() {
testt tt;
tt.doIt();
}
注意void()
函数类型为成员类型T
; C ++ 17中提供了更简单的语法,模板参数的类型为auto
。
或者通过创建包含T*
和T::*
的结构并将指针作为PVOID
传递给它来将其保留为普通参数。这种方法的技巧是你需要使用类型擦除来正确地销毁该块,或者使用reinterpret_cast
临时将指针存储在固定类型下(如StartThread
所述)。
答案 1 :(得分:1)
不使用C ++运行时,例如std::thread
和std::function
,您的选项有点受限。
尝试更像这样的事情:
template <class U>
class Thread {
private:
HANDLE native_handle = 0;
DWORD id = 0;
U *object;
void (U::*object_member)();
static DWORD WINAPI ThreadProc(PVOID ptr);
public:
Thread(void U::*member, U* obj);
bool start();
};
template<class U>
DWORD WINAPI Thread<U>::ThreadProc(PVOID ptr) {
Thread *t = static_cast<Thread*>(ptr);
U *obj = t->object;
void (U::*member)() = t->object_member;
(obj->*member)();
return 1;
}
template<class U>
Thread<U>::Thread(void U::*member, U* obj) :
object_member(member), object(obj) {
native_handle = CreateThread(0, 0, &ThreadProc, this, CREATE_SUSPENDED, &id);
}
template <class U>
bool Thread<U>::start() {
return (ResumeThread(native_handle) != (DWORD)-1);
}
class testt {
public:
void testf() {
MessageBoxA(0, "working", "", 0);
}
void doIt() {
Thread<testt> t(&testt::testf, this);
t.start();
}
};
int main() {
testt tt;
tt.doIt();
}
否则,你可能不得不求助于这样的事情:
class Thread {
private:
HANDLE native_handle = 0;
DWORD id = 0;
void (*func)(void*);
void *param;
static DWORD WINAPI ThreadProc(PVOID ptr);
public:
Thread(void (*f)(void*), void* p);
bool start();
};
DWORD WINAPI Thread::ThreadProc(PVOID ptr) {
Thread *t = static_cast<Thread*>(ptr);
void (*func)(void*) = t->func;
(*func)(t->param);
return 1;
}
Thread::Thread(void (*f)(void*), void *p) :
func(f), param(p) {
native_handle = CreateThread(0, 0, &ThreadProc, this, CREATE_SUSPENDED, &id);
}
bool Thread::start() {
return (ResumeThread(native_handle) != (DWORD)-1);
}
class testt {
private:
static void proc(void *p) {
static_cast<testt*>(p)->testf();
}
public:
void testf() {
MessageBoxA(0, "working", "", 0);
}
void doIt() {
Thread t(&testt::proc, this);
t.start();
}
};
int main() {
testt tt;
tt.doIt();
}
答案 2 :(得分:0)
我以stap :: thread的winapi替代方式结束并且工作原理相同,可能更好
class letTest {
private :
void pri(int u,float g, char l) {
MessageBoxA(0, 0, 0, 0);
}
public :
int doIt(int o) {
auto t = thread(&letTest::pri, this,5,4.2,'k'); // works well with any number and type of parameters
t.join();
return 5;
}
};
也有通常的功能
void ltest(int i) {
MessageBoxA(0, to_string(i).c_str(), 0, 0);
}
int main() {
auto t = thread(ltest, 4);
t.join();
}
答案 3 :(得分:-1)
由CreateThread
执行的应用程序定义的函数必须具有签名:
DWORD WINAPI ThreadProc(
_In_ LPVOID lpParameter
);
如果我们使用类成员(非静态)函数 - 它必须有签名
class testt {
ULONG WINAPI testf();
};
需要记住,每个非静态成员函数都有指向 this 的指针作为第一个参数。只是我们没有明确声明它。结果ULONG WINAPI testf(/*testt* this*/);
与ThreadProc callback函数完全匹配。我们可以将它用作线程入口点。
在这个项目中,我只能使用Win32 API。
我没有查看需要使用包装器来实现这个线程api,但代码看起来像:
template <class U>
class Thread
{
HANDLE _hThread;
DWORD _id;
public :
Thread() : _hThread(0) {}
~Thread()
{
if (_hThread) CloseHandle(_hThread);
}
ULONG Create(ULONG (WINAPI U::*member)(), U* This)
{
union {
LPTHREAD_START_ROUTINE lpStartAddress;
ULONG (WINAPI U::*_member)();
};
_member = member;
if (_hThread = CreateThread(0, 0, lpStartAddress, This, CREATE_SUSPENDED, &_id))
{
return NOERROR;
}
return GetLastError();
}
ULONG run()
{
return ResumeThread(_hThread) == MAXULONG ? GetLastError() : NOERROR;
}
ULONG wait()
{
return WaitForSingleObject(_hThread, INFINITE);
}
};
class testt {
PCWSTR _txt, _caption;
public :
testt(PCWSTR txt, PCWSTR caption) : _txt(txt), _caption(caption) { }
ULONG WINAPI testf() {
return MessageBox(0, _txt, _caption, 0);
}
void doIt() {
Thread<testt> t;
if (t.Create(&testt::testf, this) == NOERROR)
{
if (t.run() == NOERROR)
{
t.wait();
}
}
}
};
void demo()
{
testt o(L"text", L"caption");
o.doIt();
}
但是对于没有模板类的比较代码,但直接启动线程到类成员函数:
class testt {
PCWSTR _txt, _caption;
public :
testt(PCWSTR txt, PCWSTR caption) : _txt(txt), _caption(caption) { }
ULONG WINAPI testf() {
return MessageBox(0, _txt, _caption, 0);
}
void doIt() {
union {
LPTHREAD_START_ROUTINE lpStartAddress;
ULONG (WINAPI testt::*pfn)();
};
pfn = &testt::testf;
if (HANDLE hThread = CreateThread(0, 0, lpStartAddress, this, 0, 0))
{
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
}
};
void demo()
{
testt o(L"text", L"caption");
o.doIt();
}