Reputation: 27
I am having a problem with shared pointers, lambdas and scope.
My situation is that I have a method which I call, so I can use later the returned value. Kind of normal until here. The problem comes when I have an async method inside this method, that I have to make a set of an exception to a shared_pointer. I am using a lambda function to process the callback from this async method. In this lambda I have to use my shared_pointer and set an exception on it (a method from its class) resultPtr->setException();
I first used a shared_ptr, then a weak_ptr in the lambda (because I read in some places that the shared_ptr could lead to memory leaks). But until now I guess I really need to use a shared_ptr.
I have to test if this exception is being set in a second .cpp, so I call the method and use the returned value. I put the thread waiting for about 500ms then I test if it has exceptions, but it does not. However, in very rare cases, it "hasException". I can see that the lambda is being called and that it sets the exception through my logs, however it seems like it does not change the value I returned first (like it is pointing to a different place).
The inputs of the first method I mentioned are not important for my case, only that they are Pointers.
In my case I have something like:
file1.cpp
typedef shared_ptr<Result> ResultPtr;
ResultPtr method_name(TypePtr args)
{
if(args != nullptr)
{
ResultPtr result = make_shared<Result>();
stuff->callAsync([this, result](input)
{
if(result == nullptr)
{
result->setException();
}
});
}
else
{
result->setError();
}
return result;
}
file2.cpp
bool testingMethod()
{
ResultPtr result = file1::methodName(TypePtr args)
std::this_thread::sleep_for(std::chrono::milliseconds(500));
test(result->hasException);
}
For testing I am using another class where I call this method with certain inputs(not important here) and I need to compare the object value of resultPtr. What happens is that as soon as it goes out of scope (return a), I can't access the object anymore. After I call this method, I put the thread waiting for about 1 second, but it never changes the state (since it can't point to a destroyed object)
Is there anyway to overcome this? Other suggestions?
Thank you!
Upvotes: 1
Views: 1568
Reputation: 960
If you need the lambda to guarantee that it keeps the object alive, why are you using a weak pointer to pass in, instead of a shared pointer? The purpose of a weak pointer is the behaviour you are seeing, where it does not itself keep the object alive.
Because you are only capturing a weak pointer it does not keep the shared pointer alive when the only shared pointer goes out of scope, if it goes out of scope before you have locked the weak pointer. But you could just capture the shared pointer (by value, so a copy is taken) instead of using a weak pointer. Then the reference count will be incremented and the shared pointer inside the lambda will keep it alive, and it will only be deleted when both shared pointers have gone out of scope.
Shared ownership like this is the reason for a shared pointer, you should only use a weak pointer if it is desirable that the object will no longer be accessible if the shared pointer reference count reaches zero.
Upvotes: 2
Reputation: 3999
Of course you can't access the object anymore, because it is destroyed when the shared pointer leaves the scope of the if(args)
block, where it is created. You'll need to have the object living outside the method, e. g. storing it as class member or (<deity> forbid!) in a global variable.
Whether you need to wrap it in a shared pointer or not is unclear, you'll need to provide a MCVE together with a clearer description of what you want to achieve.
Upvotes: 2
Reputation: 2982
If your callAsync
is really asynchronous, your lambda could start to execute when local variables result
and a
is alreay out of scope. You should pass needed variables as parameters, e.g. as binding with std::bind
:
typedef shared_ptr<Result> ResultPtr;
ResultPtr method_name(args)
{
if(args)
{
ResultPtr a = make_shared<Result>();
std::weak_ptr<Result> result(a);
stuff->callAsync(std::bind([this](std::weak_ptr<Result> result_param)
{
auto resultPtr = result_param.lock();
if(resultPtr)
{
resultPtr->setValue()
...other stuff...
}
}, result));
}
else
{
a->setError();
}
return a;
}
and yes, your example is confusing
Upvotes: 1