[thread] About the virtual in virtual operator()

I don't really know wether this is a Boost.Thread related question or a general c++ related, but since I couldn't decide I will disturb you first ;) Maybe you have the patience to look at this minimalistic framework (I stripped almost all functionality): http://pastebin.com/m7dcc833f This compiles well and running it gives me the expected: # starting # This is A # This is B # shutting down The problem is, that I want to run the kernels as threads. Have (again) a look at the highlighted lines (27-29): If I replace 27 by 28+29 I (expectedly) get a compile error since Kernel is abstract. However if I make it non-abstract by implementing the operator() in it, it is this function (namely Kernel::operator()) which get's called instead of TestKernel::operator(). Note that Program will not be able to know about the concrete child classes of Kernel so it cannot dynamic_cast<ConcretKernel*> the pointers and I don't want to use maschine-specific ways like __decltype. I imagine this is merely the old non-static member function pointer problem, however I tried dozens of ways to get around this and the only thing that worked was to add a ConcreteKernel::run() { thread t(*this); t.join(); } to *each* single child class of Kernel which gets rather un- managable with 50 and more descendants. I hope that made sense and that I didn't fail that hard, Richard Vock

Richard Vock wrote:
I don't really know wether this is a Boost.Thread related question or a general c++ related, but since I couldn't decide I will disturb you first ;)
If I replace 27 by 28+29 I (expectedly) get a compile error since Kernel is abstract. However if I make it non-abstract by implementing the operator() in it, it is this function (namely Kernel::operator()) which get's called instead of TestKernel::operator().
Note that Program will not be able to know about the concrete child classes of Kernel so it cannot dynamic_cast<ConcretKernel*> the pointers and I don't want to use maschine-specific ways like __decltype.
There are lots of ways to skin this cat. The smallest change to your code I think would be this though. --- thread t( boost::bind( &Kernel::operator(), m_kernel[i] ) ); t.join(); --- Regards, Nigel

Richard Vock wrote:
The smallest change to your code I think would be this though. --- thread t( boost::bind( &Kernel::operator(), m_kernel[i] ) ); t.join();
I've always had problems understanding boost::bind, but this helps a lot.
And it works too...
Thank you!
I realise this solves your problem but didn't really answer your question though. The reason it doesn't work with an abstract class is because the thread class attempts to copy the functor you're passing in. Since Kernel is abstract you can't do this. If you make Kernel a concrete class you get a different problem. The statement: thread t( *(m_kernel[i]) ) slices the object down to a plain old Kernel, irrespective of what the pointer originally referenced. You can only call virtual functions through pointers or references so you lose dynamic binding once this happens. n

Richard Vock <vock@cs.uni-bonn.de> writes:
I don't really know wether this is a Boost.Thread related question or a general c++ related, but since I couldn't decide I will disturb you first ;)
Maybe you have the patience to look at this minimalistic framework (I stripped almost all functionality):
This compiles well and running it gives me the expected:
# starting # This is A # This is B # shutting down
The problem is, that I want to run the kernels as threads. Have (again) a look at the highlighted lines (27-29):
If I replace 27 by 28+29 I (expectedly) get a compile error since Kernel is abstract. However if I make it non-abstract by implementing the operator() in it, it is this function (namely Kernel::operator()) which get's called instead of TestKernel::operator().
Yes. This is because boost::thread copies the objects into the new thread. You can't copy an object through a reference to abstract base since you cannot create an instance of the base class. If you make the base non-abstract then it slices, and calls the base operator(). You can use boost::ref to fix this: boost::thread t(boost::ref(*m_kernel[i])); Of course you have to ensure that the m_kernel objects outlive the threads if you do this. Anthony -- Author of C++ Concurrency in Action | http://www.manning.com/williams just::thread C++0x thread library | http://www.stdthread.co.uk Just Software Solutions Ltd | http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976
participants (3)
-
Anthony Williams
-
Nigel Rantor
-
Richard Vock