Reputation: 67
I have a method like this:
void syncOperation(ProgressCallback& progressCallback);
where ProgressCallback
is:
class ProgressCallback
{
public:
virtual void onProgress(std::size_t currValue, std::size_t maxValue) {}
virtual void onDone() {}
};
and I want to make it async, so I do the following:
void asyncOperation(ProgressCallback& progressCallback)
{
auto impl = [this](ProgressCallback& progressCallback_)
{
syncOperation(progressCallback_);
};
jobsPool.addJob(std::bind(impl, progressCallback));
}
but behaviour of progressCallback
in the 2nd case (asyncOperation(ProgressCallback&)
) isn't polymorphic, it calls methods of the base class always and that is definitely not what I expect. So my questions are: 1)why is it happening and 2)how to fix it (yes, I know I can just stop using lambdas in my case, but maybe there is some traditional workaround)?
Upvotes: 2
Views: 342
Reputation: 40859
The code you show has no room for polymorphism, but lets assume that asyncOperation
is passed a reference to something derived from ProgressCallback
:
You're creating a copy when you pass your reference to bind
. The object that bind returns stores the value based on the type information it has about it. It only knows that it has a reference to ProgressCallback
so that's what kind of value it stores. This causes a slicing copy.
You need to wrap your value in a reference_wrapper:
jobsPool.addJob(bind(impl, ref(progressCallback));
Or you could use lambda (better):
jobsPool.addJob([impl,&progressCallback](){ impl(progressCallback); });
Or you could just create the impl
so that it does everything on its own.
What you're doing now is relatively equivalent to:
jobsPool.addJob([impl,=progressCallback]() mutable { impl(progressCallback); });
Upvotes: 2
Reputation: 17704
You don't need to define a lambda, and then use bind, or use two lambdas, you can do this all in one go:
void asyncOperation(ProgressCallback& progressCallback)
{
jobsPool.addJob([&] { syncOperation(progressCallback); });
}
As noted, in the original code you're being bitten because of the slicing copy that is occurring. Because of how template argument deduction works, deferred function calls like std::bind
need to have by reference arguments passed via reference wrappers. This rather odd behavior is another good reason not to use bind
at all once you have access to lambdas. Though note that this behavior also applies to std::thread
.
Be aware that storing a lambda that captures something by reference is quite a dangerous thing to do. You could call asyncOperation
with a progressCallback object which is then alive, and it is possible that the object is destructed by the time that lambda is executed, causing undefined behavior.
Upvotes: 1