Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing function Pointers in C++

i want to do this simple piece of code work.

#include <iostream>
#include <windows.h>


    void printSome (int i)
    {
        std::cout << i << std::endl;
    }

    void spawnThread (void (*threadName)(int i))
    {
        CreateThread 
            (
                0,      // default security attributes
                0,          // use default stack size 
                (LPTHREAD_START_ROUTINE)threadName,  // thread function name
                (LPVOID)i,          // argument to thread function 
                0,          // use default creation flags 
                0       // returns the thread identifier 
            );  
    }

    int main ()
    {
        spawnThread(printSome(155));
    }

i am on windows, using vs. Any help will be greatly appriciated.

like image 223
SMeyers Avatar asked Dec 31 '08 13:12

SMeyers


3 Answers

CreateThread wants 2 arguments: pointer to the function to execute as a thread, and a DWORD argument that will be given to the thread. your spawnThread() function only has 1 argument (threadName); you think it has 2 args because of the "i", but that is really part of the definition of the "threadName" type. (you could just as well leave out the "i"; that is, you don't need to name the arguments to "threadName".)

anyway, given that you NEED 2 arguments, redefine spawnThread:

   void spawnThread(void (*threadEntryPoint)(int), int argument)
   {
      CreateThread(0,0,
                   (LPTHREAD_START_ROUTINE)threadEntryPoint,
                   (LPVOID)argument,
                   0,0);
   }

notice that i did not name the int argument to the threadEntryPoint; it is sufficient to tell the compiler that the function must have a single int argument.

and call it:

   spawnThread(printSome, 155);

anyway, quick and dirty, this will do what you want.

hth.

reilly.

like image 125
reilly Avatar answered Oct 28 '22 04:10

reilly


Personally, I wouldn't consider passing in a function pointer like you are trying to do as very C++ like. That's coding C in C++

Instead, I'd wrap that thing in a class. The big advantage there is you can just override the class to have however many members you want, rather than having to perform greazy casting tricks to get at your parameters every time.

The code's a little long-winded, so I pushed it to the end. But what it lets you do is something like this:

   class print_some : public basic_thread {
    private:
       int i;
    public:     
       print_some (int i) : i(i) {};
       action_callback () {
          std::cout << i << std::endl;
       }
    }
    int main () {
       print_some printer (155);
    }

Here's some exerpted example code from one of our classes that does this:

class basic_thread : 
{
public:
   basic_thread();
protected:
   unsigned long m_ThreadId;

   virtual void action_callback () {};

   // Internal routine used to bridge between OS callback format and 
   // action_callback. *Must* be static for the OS.
   static unsigned long __stdcall self_calling_callback (void *parameter);
}

...and in the .cpp:

unsigned long __stdcall basic_thread::self_calling_callback (void *parameter) {
   if (parameter) {
      basic_thread * thread = reinterpret_cast<basic_thread *>(parameter);
      thread->action_callback();
   }
   return 0; // The value returned only matters if someone starts calling GetExitCodeThread
             // to retrieve it.
}

basic_thread::basic_thread () {
   // Start thread.
   m_Handle = CreateThread(NULL,
                           0,
                           self_calling_callback,
                           (PVOID)this,
                           0,
                           &m_ThreadId );
   if( !IsHandleValid() )
      throw StartException("CreateThread() failed", GetLastError());

}
like image 23
T.E.D. Avatar answered Oct 28 '22 05:10

T.E.D.


You cannot pass parameter information in a function pointer; it must be passed separately. That is exactly why the CreateThread function provides a void* parameter that can point to whatever you want.

Additionally, you should use _beginthread instead of CreateThread for C++ applications.

Finally, your program is more than likely to terminate before the thread ever runs. Therefore, you must either enter an indefinite loop or use an API call to wait for the thread to finish.

The following is a working version using WaitForSingleObject to block until the thread completes.

#include <iostream>
#include <process.h>
#include <windows.h>

void
printSome(int i)
{
    std::cout << i << std::endl;
}

HANDLE
spawnThread(void (*threadName)(int), int i)
{
    return (HANDLE) _beginthread((void (*)(void*)) threadName, 0, (LPVOID) i);      
}

int
main(int argc, char *argv[])
{
    HANDLE threadHandle;

    threadHandle = spawnThread(printSome, 155);
    WaitForSingleObject(threadHandle, INFINITE);

    return 0;
}

Here is a much more C++/object-oriented way of handling this same situation:

#include <iostream>
#include <process.h>
#include <windows.h>

class Thread {
    static void proxy(void *arg) { (*(reinterpret_cast<Thread *> (arg)))(); }
    HANDLE thread_;

public:
    virtual ~Thread() {}
    virtual void operator()() = 0;  
    void start() { thread_ = (HANDLE) _beginthread(Thread::proxy, 0, this);}    
    void waitForExit() { WaitForSingleObject(thread_, INFINITE); }
};

class Printer : public Thread {
    int i_;

public:
    Printer(int i) : i_(i) {}
    void operator()() { std::cout << i_ << std::endl; }
};

int
main(int argc, char *argv[])
{
    Printer p(155);

    p.start();
    p.waitForExit();

    return 0;
}
like image 24
Judge Maygarden Avatar answered Oct 28 '22 05:10

Judge Maygarden