views:

3175

answers:

5

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.

+1  A: 

You can read how you do that here: http://www.newty.de/fpt/fpt.html

2.6 How to Pass a Function Pointer as an Argument ?

You can pass a function pointer as a function's calling argument. You need this for example if you want to pass a pointer to a callback function. The following code shows how to pass a pointer to a function which returns an int and takes a float and two char:

//------------------------------------------------------------------------------------
// 2.6 How to Pass a Function Pointer

// <pt2Func> is a pointer to a function which returns an int and takes a float and two char
void PassPtr(int (*pt2Func)(float, char, char))
{
   int result = (*pt2Func)(12, 'a', 'b');     // call using function pointer
   cout << result << endl;
}

// execute example code - 'DoIt' is a suitable function like defined above in 2.1-4
void Pass_A_Function_Pointer()
{
   cout << endl << "Executing 'Pass_A_Function_Pointer'" << endl;
   PassPtr(&DoIt);
}
Filip Ekberg
thanks that works pretty well
+10  A: 

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.

reilly
+5  A: 

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;
}
Judge Maygarden
Great answer - except you should be using _beginthread() or _beginthreadex() instead of CreateThread(). See http://stackoverflow.com/questions/331536/windows-threading-beginthread-vs-beginthreadex-vs-createthread-c for more details.
Adam Rosenfield
True, I updated the answer. There were too many things to fix! ;)
Judge Maygarden
+5  A: 

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());

}
T.E.D.
+4  A: 

As many people already mentioned here, you can't pass a function pointer and the argument it should be called with in one parameter.

Your line

    spawnThread(printSome(155));

"should" (in DWIM world) mean "Invoke printSome on a separate thread with argument 155". However, it's not how C++ understands it. C++ sees "Pass the result of printSome invoked on 155 as a parameter to spawnThread". In other words, the sequence of steps is:

  • call prinotSome with 155 as argument. Store it in temporary memory.
  • call spawnThread with the contents of temporary memory as its argument.

In order to do what you really mean, you have to humor C++ and separate argument from function. How to do it is already explained in other answers. The short of it is:

callOnOtherThreadWithArgument(function, integer);

Arkadiy