c ++ CreateThread нестатический член без определенного имени члена - PullRequest
0 голосов
/ 27 мая 2018

Я пытаюсь создать поток с нестатическим членом класса, например:

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, потоков, файлового ввода-вывода и т. д. В противном случае я всегда использую std::thread, и это фантастика.В этом проекте мне нужно использовать только Win32 API.

Ответы [ 4 ]

0 голосов
/ 09 июня 2018

Я закончил с альтернативой winapi для std :: thread и работает так же, вероятно, лучше

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();
}
0 голосов
/ 27 мая 2018

Без использования среды выполнения 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();
}
0 голосов
/ 27 мая 2018

определяемая приложением функция, которая должна выполняться CreateThread, должна иметь подпись:

DWORD WINAPI ThreadProc(
  _In_ LPVOID lpParameter
);

, если мы использовали функцию члена класса (нестатическую) - она ​​должна иметьподпись

class testt {
    ULONG WINAPI testf();
};

необходимо помнить, что каждая нестатическая функция-член получает указатель на этот в качестве первого параметра.просто мы не явно заявляем об этом.в результате ULONG WINAPI testf(/*testt* this*/); точно соответствует функция обратного вызова ThreadProc .и мы можем использовать его в качестве точки входа потока.

В этом проекте мне нужно использовать только 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();
}
0 голосов
/ 27 мая 2018

Параметр шаблона может быть указателем на элемент , поэтому вы можете увеличить 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).

...