0

I created a widget.h file containing the declartions of pthread_function and I wanted to call it in a member function destroyWidget of that class Widget in widget.cpp. but always shows an error. I'll show the .cpp and .h file.

widget.h file


class Widget
{
public:
Widget();
void createWidget(int x,int y,int w,int h);
void showWidget();
int wid;
pthread_t thread;
int *incomingval,id;
void join();
Window win;
XEvent evt;
private:
void* destroyWidget(void* ptr);
Display *disp;
int screenNumber;
unsigned long white;
unsigned long black;
long eventMask;
GC gc;
int tbit;
int *incoming,val;
};

now the widget.cpp


Widget::Widget()
{
disp=XOpenDisplay( NULL );
screenNumber=DefaultScreen(disp);
white=WhitePixel(disp,screenNumber);
black=BlackPixel(disp,screenNumber);
eventMask=StructureNotifyMask;
tbit=0;
}

void Widget::createWidget(int x,int y,int w,int h)
{
wid=w;
win= XCreateSimpleWindow(disp,DefaultRootWindow(disp),x,y,w,h,1,white,black);
}

void Widget::showWidget()
{
XMapWindow(disp,win);
XFlush(disp);
gc=XCreateGC(disp,win,0,NULL);
XSetForeground(disp,gc,white);
XDrawLine(disp,win,gc,wid-10,0,wid,10);
XDrawLine(disp,win,gc,wid-10,10,wid,0);
//calling the thread function
pthread_create( &thread, NULL, destroyWidget, this);
}

void Widget::join()
{
pthread_join( thread, NULL);
}
void* Widget::destroyWidget(void* ptr)
{
Widget* mw = static_cast(ptr);
eventMask=ButtonPressMask|ButtonReleaseMask;
XSelectInput(disp,win,eventMask);
do{
printf("id= %d",id);
XNextEvent(disp,&evt);
}while(evt.type!=ButtonRelease);
XDestroyWindow(disp,win);
XCloseDisplay(disp);
return NULL;
}

now the main.cpp file



#include "widget.h"
#include
int main()
{
Widget* w=new Widget();
Widget* n=new Widget();
n->createWidget(20,20,150,150);
w->createWidget(50,50,250,250);
n->showWidget();
w->showWidget();
n->join();
w->join();
return 0;
}

the error is

widget.cpp: In member function ‘void Widget::showWidget()’:
widget.cpp:44:51: error: argument of type ‘void* (Widget::)(void*)’ does not match ‘void* (*)(void*)’
HariHaraSudhan
  • 1,330
  • 3
  • 15
  • 24
  • 1
    Indendation really does a lot to make your code readable. I highly recommend using it in the future. Even just a couple of spaces would be chill. – Cody Gray - on strike Jun 15 '11 at 13:58
  • You should **NOT** use a member function **Not even** a static one. Read here for the correct solution. http://stackoverflow.com/questions/6352280/pthread-create-error-in-c/6352434#6352434 – Martin York Jun 15 '11 at 14:59

3 Answers3

7

The problem is that pthread_create is a C-style function; you need to give it a pointer-to-function. Widget::destroyWidget() is a pointer-to-member-function. (Remember that non-static member functions always have an implied this argument, which pthread_create doesn't know how to provide.)

See the answers to this question for some possible solutions: pthread function from a class.

Community
  • 1
  • 1
Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • I tried using static , but this gives a error on variables used in that whole function – HariHaraSudhan Jun 15 '11 at 14:02
  • 1
    @user: Did you try the other idea? i.e. adding a `static` helper function which in turn calls the member-function? – Oliver Charlesworth Jun 15 '11 at 14:06
  • @Oli: Nope, help me with it if u can. – HariHaraSudhan Jun 15 '11 at 14:08
  • A static member function is not sufficient. The function must be `extern "C"`, and any language linkage on member functions is ignored. – James Kanze Jun 15 '11 at 14:11
  • @James: Perhaps you should raise that concern in the comments for the answer to that question? – Oliver Charlesworth Jun 15 '11 at 14:13
  • @Oli Is there any point of raising concerns about an answer that appeared a year or more ago? (Honest question: I don't really know yet how this forum works.) Anyway, while the standard is quite clear about this, several (but not all) compilers ignore it, so you can often compile your code anyway, only to have it not work with a different compiler. – James Kanze Jun 15 '11 at 18:02
0

Like Oli said you can't use a member function when a C-style function expects a "normal" function pointer. However, what you can do is make a separate function that calls back your destroyWidget() method.

Like so:

void* start_routine(void* arg)
{
    Widget* widget = static_cast<Widget* >(arg);

    widget->destroyWidget();

    return NULL;
}

void Widget::showWidget()
{
    pthread_create(&thread, NULL, &start_routine, this);
}

void Widget::destroyWidget()
{
    // your code
}
Seth Jackson
  • 539
  • 4
  • 16
  • Sorry! There was a typo in start_routine it's fixed now. – Seth Jackson Jun 15 '11 at 14:23
  • widget.cpp: In member function ‘void Widget::showWidget()’: widget.cpp:34:33: error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function. Say ‘&Widget::start_routine’ widget.cpp:34:52: error: cannot convert ‘void* (Widget::*)(void*)’ to ‘void* (*)(void*)’ for argument ‘3’ to ‘int pthread_create(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)’ – HariHaraSudhan Jun 15 '11 at 14:25
  • Is start_routine a static method of the Widget class? If it is try making it a "flat" function (taking it out of the class Widget) which should make your code compile and run. I've ripped this example straight out of a working codebase so it should work. – Seth Jackson Jun 15 '11 at 14:31
  • The correct call is `pthread_create(&thread, NULL, start_routine, this);` (no ampersand to take a function pointer). – datenwolf Jun 19 '11 at 10:57
0

The third argument to pthread_create has the signature (in C++):

extern "C" void* (*pointerToFunction)( void* );

You're trying to pass it the address of a member function:

void* (Widget::*pointerToMemberFunction)( void* );

The signatures are incompatible: the second requires an object on which to call it, and is not extern "C".

The simplest way of handling this is to use boost::thread, with all it's functional object support. Otherwise, you can define something like the following:

struct AbstractTask
{
    virtual ~AbstractTask() {}
    virtual void* run() = 0;
};

template<typename T, void* (T::*ptr)()>
class Task
{
    T* myObject;
public:
    Task( T* object ) : myObject( object ) {}
    virtual void* run()
    {
        return (myObject->*ptr)();
    }
};

extern "C" void* taskRunner( void* arg )
{
    std::auto_ptr<AbstractTask> p( static_cast<AbstractTask*>( arg ) );
    return p->run();
}

pthread_t taskStarter( AbstractTask* obj )
{
    pthread_t result;
    pthread_create( &result, NULL, &taskRunner, obj );
    return result;
}

To start a thread, you then call:

thread = taskStarter( new Task<Widget, &Widget::destroyWidget>( this ) );

(This is from memory, from an earlier project, so there might be some typos in it, but you get the idea. And you probably want to add some error handling in taskStarter.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Cant understand this procedure, can you explain it with reference to my program. I badly need it. – HariHaraSudhan Jun 15 '11 at 14:23
  • @user796094 The idea is that what you pass to `pthread_create` is a pointer to a function which casts its argument to a pointer to an interface, and calls a function in it. Then you derive from the interface to do whatever you want; in this case, to make things simpler, there is a template to create the derived class, so you don't have to write it each time. And the actual function passed to `pthread_create` will destruct the object you've created; since polymorphism is involved, and lifetime doesn't follow scope, you `new` it. – James Kanze Jun 15 '11 at 18:07