Reputation: 1605
I'm trying to create a thread with a non-static class member, like this:
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();
}
As you see, I can only run a particular member, so this method isn't portable and can't be used for any class or member.
I know I can use std::thread
easily, but I'm working on a project that I shouldn't use any C++ runtime in, so I am creating wrappers for new
/delete
, threads, file I/O, etc. Otherwise, I always use std::thread
and it's fantastic. In this project, I have to use Win32 API only.
Upvotes: 0
Views: 521
Reputation: 1605
I ended with a winapi alternative for std::thread and works the same and is probably better
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;
}
};
also with usual functions
void ltest(int i) {
MessageBoxA(0, to_string(i).c_str(), 0, 0);
}
int main() {
auto t = thread(ltest, 4);
t.join();
}
Upvotes: 0
Reputation: 39818
A template parameter can be a pointer to member, so you can augment StartThread
that way and make Thread::Thread
’s pointer-to-member parameter a template parameter. You can’t supply explicit template arguments to a constructor template, so you’ll have to use a special “tag” argument to convey them:
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();
}
Note the void()
function type as the member type T
; simpler syntax is available in C++17 with auto
in a template parameter’s type.
Or preserve it as a normal parameter by making a struct containing a T*
and a T::*
and passing a pointer to it as your PVOID
. The trick this way is that you’ll need to use type erasure to destroy that block correctly, or else use reinterpret_cast
to store the pointers temporarily under fixed types (as already done for StartThread
).
Upvotes: 1
Reputation: 596156
Without the use of the C++ runtime, like std::thread
and std::function
, your options are a bit limited.
Try something more like this:
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();
}
Otherwise, you may have to resort to something more like this:
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();
}
Upvotes: 1
Reputation: 33706
application-defined function to be executed by the CreateThread
must have signature:
DWORD WINAPI ThreadProc(
_In_ LPVOID lpParameter
);
if we was use class member (non-static) function - it must have signature
class testt {
ULONG WINAPI testf();
};
need remember that every non static member function got pointer to this as first parameter. simply we not explicit declare it. as result ULONG WINAPI testf(/*testt* this*/);
exactly match to ThreadProc callback function. and we can use it as thread entry point.
In this project, I have to use Win32 API only.
i not view for what need use wrappers for concrete this thread api, but code can look like:
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();
}
however for compare code without template class, but direct start thread to class member function:
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();
}
Upvotes: -1