0

Happy new year!

This is my baby steps in C++ world.

I used an example from learn.microsoft.com to create an example Win32 Console project which uses the WaitForMultipleObjects function to persist until all worker threads have terminated. All worked great!

Things get complicated (argument of type DWORD (Thread::*)(LPVOID lpParam) is incompatible with parameter of type "LPTHREAD_START_ROUTINE") when i start to try to port the functionality of the concept inside a class which is similar like this:

    class Threads
    {
    
     private:

        HANDLE comThreads[1];

        DWORD WINAPI closeThreadProc(LPVOID lpParam)
        {
            // lpParam not used in this example.
            UNREFERENCED_PARAMETER(lpParam);
            printf("Thread %d exiting\n", GetCurrentThreadId());
            return 1;
        }
    
        BOOL CreateThreads(void)
        {
            DWORD dwThreadID;
            
            comThreads[0] = CreateThread(
                NULL,              // default security
                0,                 // default stack size
                closeThreadProc,   // Close thread function
                NULL,              // no thread parameters
                0,                 // default startup flags
                &dwThreadID);
        }

     public:
        
         void Init()
         {
             CreateThreads();
         }
    }

I will try to use this class to create a Dynamic-link library(DLL).

While i am searching the answer to my own question. I would like to ask you:

  1. Is this even possible?
  2. If it is possible. How can i achieve this, without loosing the underlying concept?

Thank you!

Edit:

Sorry for forgetting to tell if is it possible to make this, without making DWORD WINAPI closeThreadProc(LPVOID lpParam) static!

I did try to make it static before i posted the Question and things became even more wild (I barely forced to make everything in the class static).

I think this is the C++'s way to punish a rookie.

swartkatt
  • 368
  • 3
  • 10
  • Why don't you use [std::thread](https://en.cppreference.com/w/cpp/thread) ? – Basile Starynkevitch Jan 01 '21 at 08:45
  • I would love to do it, if i knew how? Can you suggest me some example? Thank you! – swartkatt Jan 01 '21 at 09:03
  • @swartkatt [Start thread with member function](https://stackoverflow.com/a/10673671/13433323) Search engines are your friends – Sprite Jan 01 '21 at 09:27
  • You need to take several days to read some [good C++ programming book](https://www.stroustrup.com/programming.html) then the documentation of your C++ compiler (e.g. [GCC](http://gcc.gnu.org/)...) and debugger (e.g. [GDB](https://www.gnu.org/software/gdb/)...) – Basile Starynkevitch Jan 01 '21 at 09:54
  • Thank you, for your recommendations Basile. – swartkatt Jan 01 '21 at 10:11
  • My recommendation is to compile with `g++ -Wall -Wextra -g`. If you are allowed to, consider also installing [Debian](http://debian.org/) on your laptop - it is very developer friendly and mostly made of open source software, whose source code you are allowed to study and improve – Basile Starynkevitch Jan 01 '21 at 14:35
  • Thank you very much Basile. I am trying to write the code for the Windows OS with Visual Studio 2017 Community. I have Debian installed on my workstation. While it is really early for me to use Gcc and Gdb and compile things from command line is a bit excessive. Time will come someday. When i look at someone's Cmakelist.txt files, my mind is blowing with the fact that how come you people can sort these things up. Compiling from command line is for me whole another world to study. When i see some compiling commands with all those flags, i sometimes think that it is even harder than to learn C++ – swartkatt Jan 01 '21 at 15:49
  • Consider then studying the source code of [Qt](http://qt.io/) - it is opensource – Basile Starynkevitch Jan 01 '21 at 16:56
  • My bit of advice would be that unless you honestly need something that native Windows threads can provide, but C++11's, threads can't, you just use `std::thread`. Roughly 95% of the time, it'll be *much* easier to deal with (and as a bonus, your code becomes portable across most of the major OSes). Even if you don't do that, I've found that the pattern of a thread proc in a class, with some virtual function you override to provide the needed functionality rarely works out well, so even if you do continue using `CreateThread`, this way of using it is probably kind of a dead end. – Jerry Coffin Jan 01 '21 at 23:03
  • Thank you for both of you for your advices. I will look for `std::thread` next time. In this project, i have already used this concept and i have already completed the project as a Win32 console application. I just needed to convert it to a DLL. Therefore, i needed a solution for the problem, and as usual, i found it here. I have learned very much from this experience. For starters, i will never write a console application again, if i am intending to make it a DLL. – swartkatt Jan 02 '21 at 19:22

2 Answers2

2

The LPVOID argument is there for a reason. The trick is to make the callback a static member but pass this as the extra parameter. You can then cast the LPVOID argument back to your object and call the method you want to. Some code will make it clearer

static DWORD WINAPI closeThreadProcCallback(LPVOID lpParam)
    {
        return static_cast<Threads*>(lpParam)->closeThreadProc();
    }

BOOL CreateThreads(void)
    {
        DWORD dwThreadID;
        
        comThreads[0] = CreateThread(
            NULL,              // default security
            0,                 // default stack size
            closeThreadProcCallback,   // Close thread callback function
            this,              // this object is the thread parameter
            0,                 // default startup flags
            &dwThreadID);
    }

EDIT added WINAPI as suggested by Tomer W.

john
  • 85,011
  • 4
  • 57
  • 81
  • OK! When i looked at your example closely. I have seen that there is a little trick out there different than what i tried. I hope it will work :) Thank you! – swartkatt Jan 01 '21 at 09:56
1

a threadStartfunction cant be _thiscall, and have to be _stdcall,
therefore i'd declare a static private method to pass your call to your object, i use the lpParameter to pass the object to the static function.

class Threads
{
private:

    HANDLE comThreads[1];

    static DWORD WINAPI staticThreadProc(LPVOID lpParam)
    {
        Threads& callingThread = *reinterpret_cast<Threads*>(lpParam);
        return callingThread.closeThreadProc();
    }

    DWORD closeThreadProc()
    {
        printf("Thread %d exiting\n", GetCurrentThreadId());
        return 1;
    }

    BOOL CreateThreads(void)
    {
        DWORD dwThreadID;

        comThreads[0] = CreateThread(
            NULL,              // default security
            0,                 // default stack size
            staticThreadProc,   // Close thread function
            this,              // no thread parameters
            0,                 // default startup flags
            &dwThreadID);
    }

public:

    void Init()
    {
        CreateThreads();
    }
}
Tomer W
  • 3,395
  • 2
  • 29
  • 44
  • Just write your own `CreateThread` function that takes whatever parameters you want. Have it call your own thread start function that then invokes whatever function you want with whatever parameters. You can even make it a `template` so you have `myCreateThread` that takes a `T*` and a `T::*`. – David Schwartz Jan 01 '21 at 09:22
  • Happy new year to you too David :) I got it! With C, you can do whatever you want. Let me process what you wanted me to do. Though i do not know where to start :) – swartkatt Jan 01 '21 at 09:49