Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: how to define a class method as a start routine to thread (with pthread library)

i have a Base class and Derived class. they have a virtual function- virtual void action() how can i pass it to *pthread_create()* function?

example(with errors):

class Base{
  protected:
     pthread_t tid;
  public:
  virtual void* action() = 0;
};

class Derived : public Base{
  void* action();
  Derived(){
    pthread_create(&tid, NULL, &action, NULL);
  } 
};

maybe it should be static? i tried lot of combinations but cant find solution..

like image 848
itamar Avatar asked Dec 27 '22 12:12

itamar


2 Answers

I ran into this problem a couple months back working on my senior design project. It requires some knowledge of underlying C++ mechanics.

The underlying issue is that pointers to functions are different from pointers to member functions. This is because member functions have an implicit first parameter, this.

From the man page:

int pthread_create(pthread_t *thread,
                   const pthread_attr_t *attr,
                   void *(*start_routine) (void *),
                   void *arg);

The thread entrance point is a void* (*)(void*). Your function, Base::action has the type void* (Base::*)(). The Base:: part of that ugly type declaration denotes the type of this. The type discrepancy is why the compiler won't accept your code.

There's two things we need to fix to make this work. We can't use a member function, because pointers to member functions don't bind this to an instance. We also need a single parameter of type void*. Thankfully, these two fixes go hand in hand because the solution is to explicitly pass this ourselves.

class Base {
public:
    virtual void* action() = 0;

protected:
    pthread_t tid;

    friend void* do_action(void* arg) {
        return static_cast<Base*>(arg)->action();
    }
};

class Derived : public Base {
public:
    Derived() {
        // This should be moved out of the constructor because this
        // may (will?) be accessed before the constructor has finished.
        // Because action is virtual, you can move this to a new member
        // function of Base. This also means tid can be private.
        pthread_create(&tid, NULL, &do_action, this);
    }

    virtual void* action();
};

Edit: Woops, if tid is protected or private, then do_action needs to be a friend.

like image 150
Oscar Korz Avatar answered Jan 17 '23 14:01

Oscar Korz


You have to have a function that takes one void pointer to pass to pthread_create. I'd write the function myself, as a function that takes a pointer to Base (Derived would work as well), then call the action function of the parameter. You can then create a thread that runs the function and received this as parameter:

void *f(void *param)
{
    Base* b = (Base *)param;
    return b->action();
}

class Derived : public Base{
  void* action();
  Derived() {
    pthread_create(&tid, NULL, f, this);
  }
};
like image 25
Antti Avatar answered Jan 17 '23 14:01

Antti