views:

271

answers:

7

Using C++.

pthread_t threads[STORAGE]; // 0-99

...

void run()

Error>>>    int status = pthread_create(&threads[0], NULL, updateMessages, (void *) NULL);
if (status != 0)
{
 printf("pthread_create returned error code %d\n", status);
 exit(-1);
}

...

void ClientHandler::updateMessages(void *)
{
    string reqUpdate = "91"; // Request for update
    string recvMSG;
    while (true)
    {
     sleep(5);
     sending(sock,reqUpdate); // send
     recvMSG = receiving(sock); // receive
     QString output(recvMSG);
     emit signal_chat(output, 0); // Print message to text box
    }
}

...

Compile Error: TCPClient.cpp:109: error: argument of type ‘void (ClientHandler::)(void*)’ does not match ‘void* (*)(void*)’

I can't figure out whats wrong. Thanks in advance.

+6  A: 

It's nothing to do with threads, it's a normal C++ error, you're just passing an incompatible type of function pointer.

A function pointer is not the same as a member instance function pointer, even if their signature is the same; this is because there is an implicit reference to *this passed. You can't avoid this.

MarkR
So I cannot create threads from within an object?
confusedEj
You can change updateMessages to be a static function of your class. Then if you want to access to the member data you can pass the "this" pointer to pthread_create as the context variable, and you will receive it in the first parameter of your updateMessages function.
rossoft
@rossoft yes that would work.
MarkR
A: 

http://www.newty.de/fpt/callback.html#member

DrPizza
That page is wrong as it uses a static member function as the callback. This is technically not allowed as the ABI for static member functions is compiler defined (and thus you can not gurantee) the calling convention. In pthreads (because it is a C library) it is expecting the function pointer to use the "C" ABI. Thus the function must be marked as extern "C" (from C++ code) to gurantee that the correct calling convention is used.
Martin York
Though the mechanism for specifying calling conventions is non-standard (since the standard doesn't specify calling conventions at all), it need not be extern "C"; e.g. VC++'s __cdecl, gcc's cdecl attribute, and so on and so forth.As such, your complaint seems to be more in theory than in practice.
DrPizza
@DrPizza: Actually the standard does specify what extern "C" does. As the "C" ABI is explicitly defined (in the C standard (inherited by the C++ standard)). This is one reason why "C" is the binder for all those other languages out there (an explicit and well defined ABI). The compiler "decl specs" are an alternative method. But requires explicit porting when moving platform.
Martin York
Please show me where in the standard it defines how arguments are passed to functions, thanks. The C spec does not define an ABI at all.Consider also that popular (or at least, widely-used--I doubt anyone actually likes it) APIs such as Win32 don't use __cdecl for their callbacks (the default convention on Windows is for callee to pop arguments, not caller).
DrPizza
+7  A: 

A pointer to a member function is different from a global function with the same signature since the member function needs an additional object on which it operates. Therefore pointers to these two types of functions are not compatible.

In this case this means that you cannot pass a member function pointer to pthread_create but only a pointer to a non-member (or static) function. A work around for this problem is to use the forth parameter of pthread_create to pass a pointer to a object to a global function which then calls the method of the passed object:

class ClientHandler {
public:
   void updateMessages();
   void run();
};

// Global function that will be the threads main function.
// It expects a pointer to a ClientHandler object.
extern "C"
void *CH_updateMessages(void *ch) {
   // Call "real" main function
   reinterpret_cast<ClientHandler*>(ch)->updateMessages();
   return 0;
}

void ClientHandler::run() {
  // Start thread and pass pointer to the current object
  int status = pthread_create(&threads[0], NULL, CH_updateMessages, (void*)this);
  ...
}
sth
Thank you very much ^^
confusedEj
A: 

As pthread_create takes a free function, create a static function(is a free function) inside ClientHandler

static void Callback(void * this_pointer,int other_arg) {
    ClientHandler* self = static_cast< ClientHandler*>(this_pointer);
    self-> updateMessages(other_arg);
}
and call pthread_create as follows

pthread_create(&threads[0], NULL, &ClientHandler::Callback, (void *) pointer_to_ClientHandler,int other_arg);

That works because Callback is free function

yesraaj
This is technically not allowed as the ABI for static member functions is compiler defined (and thus you can not guarantee) the calling convention. __If__ this works on your platform compiler you are just getting lucky. In pthreads (because it is a C library) it is expecting the function pointer to use the "C" ABI. Thus the function must be marked as extern "C" (from C++ code) to guarantee that the correct calling convention is used.
Martin York
thanks, but I am not able to remove the post
yesraaj
A: 

You're passing a member function instead of a global, normal, one.

Just define:

void updateMessages(void *) {
static ClientHandler c;
// use c..
}

Edit: This is just a quick fix. Was the down vote necessary?

rmn
A: 

YoLinux has a nice pthread tutorial that my help you in learning about threads.

ChadNC
A: 

As others have already said, the problem is that the signatures between the functions are different. Class member functions always have a "secret" extra parameter, the this pointer. So you can never pass a member function where a global function is expected. You can hack around this either with libraries such as Boost.Bind, or by making the function a static member of the class.

But the simplest, and most elegant solution is to use a different threading API.

Boost.Thread is a very nice threading library for C++ (pthreads is designed for C, and that's why it doesnt play well with C++ features such as class methods).

I'd recommend using that.

Your code could be rewritten as something like this:

class ClientHandler {
public:
  ClientHandler(/* All the parameters you want to pass to the thread. Unlike pthreads you have complete type safety and can pass as many parameters to this constructor as you like */){...}
  void operator()() // boost.thread calls operator() to run the thread, with no parameters. (Since all parameters were passed in the constructor and saved as member variables
  {
    string reqUpdate = "91"; // Request for update
    string recvMSG;
    while (true)
    {
        sleep(5);
        sending(sock,reqUpdate); // send
        recvMSG = receiving(sock); // receive
        QString output(recvMSG);
        emit signal_chat(output, 0);    // Print message to text box
    }
  }
  // whatever arguments you want to pass to the thread can be stored here as member variables
};


boost::threead_group gr; // can store all your threads here, rather than being limited to your fixed-size array

gr.create_thread(ClientHandler(/* construct a ClientHandler object with the parameters you like*/));
jalf