itamar
itamar

Reputation: 1940

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..

Upvotes: 4

Views: 9417

Answers (5)

Oscar Korz
Oscar Korz

Reputation: 2487

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.

Upvotes: 4

demarr
demarr

Reputation: 108

I usually do something similar to this, I'll let you fill in the other details (error handling, locking, etc):

Start method:

bool pthreadBase::start()
{
   return pthread_create(&threadID, NULL, &execute,this);
}

Static void* Execute method:

void *pthreadBase::execute(void *t)
{
   reinterpret_cast<pthreadBase *> (t)->processLoop();
   return NULL;
}

After, you can just make a virtual method called processLoop that will act as an entry point for your thread.

Here's a simple implementation (NOT TESTED):

class theThread: public pthreadBase
{
   public:
      theThread(SharedMemoryStructure *SharedMemory)
      {
         _Running = start();
         _Shared = SharedMemory;
      }

      ~theThread()
      {
         stop(); //Just do a join or something
         _Running = false;
      }

   private:
      void processLoop()
      {
         while(_Shared->someQuitFlag() == false)
         {
            /*Do Work*/
         }
      }

   private:
      bool _Running;
      SharedmemoryStructure *_Shared;
};

Upvotes: 1

Antti
Antti

Reputation: 12489

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

Upvotes: 3

user3458
user3458

Reputation:

Indeed it has to be static. You'll also need to pass your object as an argument to pthread_create:

void *Base::static_action(void *v)
{
    ((Base*)v)->action();
    return NULL;
}

pthread_create(&tid, NULL, &Base::static_action, myObject);

Upvotes: 2

Jerry Coffin
Jerry Coffin

Reputation: 490218

Making it static isn't really guaranteed to work, but actually does at least with most implementations (and enough people depend on it that I'd be a little surprised to see that change soon either).

Upvotes: 0

Related Questions