2

I'm trying to pass a virtual method to the thread class' constructor (C++ thread).

After searching all over, I've only been able to pass a non-virtual member method.

My base class A has a start method as follows:

void A::start() {
    thread(&A::runnable,A()); // <--- What do I change here?
}

The function runnable is virtual and is also implemented in derived class B. I override runnable in derived class B.

I then invoke start on B.

Obviously, and undesirably, the start function uses runnable implemented in A (instead of B) because it is explicitly defined in A::start. Is there any way to let the runnable function be dynamically bound?

I thought of using templates and a couple of other creative solutions. (I eventually will implement start in B if there are no real solutions)

Any help would be greatly appreciated.

Ori Gil
  • 161
  • 2
  • 7

2 Answers2

5

Obviously, and undesirably, the start function uses runnable implemented in A (instead of B) because it is explicitly defined in A::start.

This could be obvious to you, but it is incorrect. When you create a thread you pass an unnamed temporary instance of class A, which obviously has type A so A::runnable would be always called, but you should pass this:

void A::start() {
    thread(&A::runnable,this); // <--- What do I change here?
}

Then proper virtual function would be called. See Boost::Bind and virtual function overloads: why do they work? for details why.

Community
  • 1
  • 1
Slava
  • 43,454
  • 1
  • 47
  • 90
2

There are a couple of things to address.

First in A::start() you create an anonymous local thread object. Unfortunately, this object will be destructed as soon as you leave A::start(). This will trigger an abort.

When you create a thread object you must always either join() it or detach() it before the object gets destructed.

For the rest of the answer, I'll use a private thread t in A:

class A {
    ...
protected:
    thread t;       // default constructed thread object, i.e. without associated  thread of execution 
public:  
    ...
    ~A() {
        if (t.joinable())   // If thread active and not detached, be sure that it's joined before it is destructed !!
            t.join();
    }
....
};

Next, in your thread creation, you use A() as parameter. This means that you will create a new anonymous A object and pass it as argument. I guess it's not what you intend to do, so you use this instead.

Then, as was told by Slava, when &A::runnable is used in combination with this, it is the virtual function that is called. So start() should look like:

void start() {
    t = move (thread (&A::runnable, this )) ;  // Here I create an anonymous thread and move it to t.   
}

If your run this code, you'll notice that A::runnable() is called for class A objects and B::runnable() for class B objects.

Christophe
  • 68,716
  • 7
  • 72
  • 138