|
Boost Users :
|
Hi Boost users,
I use boost 1.57 on Windows 64 bit platfrom
with Visual Studio 2013. It seems to me that the boost::coroutines and
the lambda capture somehow does not play nice together.
I want to do something like the iterator
block in C# and I came up with the following code:
template
<typename
TOutput>
using
GeneratorBlockYield
= typename
boost::coroutines::asymmetric_coroutine<TOutput>::push_type;
template<typename
TOutput>
class
GeneratorBlockEnumerator
: public
IEnumerator<TOutput>
{
public:
explicit
GeneratorBlockEnumerator(std::function<void(GeneratorBlockYield<TOutput>&)>
generator)
: _firstMoveNextCall(true)
, _generatorPuller(generator)
{
}
~GeneratorBlockEnumerator() override
= default;
bool
MoveNext() override
{
auto
generatorNotFinished = static_cast<bool>(_generatorPuller);
if
(!generatorNotFinished)
return
false;
if
(_firstMoveNextCall)
{
_firstMoveNextCall
= false;
}
else
{
_generatorPuller();
}
generatorNotFinished
= static_cast<bool>(_generatorPuller);
return
generatorNotFinished;
}
TOutput
Current() const
override
{
return
_generatorPuller.get();
}
private:
bool
_firstMoveNextCall;
typename
boost::coroutines::asymmetric_coroutine<TOutput>::pull_type
_generatorPuller;
};
Here is a test:
1, Create an vectotr with smart pointer
2, I capture that smart pointer inside
the lambda, so the lambda has a copy of the smart pointer. This means the
"useCount" shall show 2 strong references and it does.
3, I remove the lambda, so the captured
smart pointer is released, so the next time I refresh "useCount"
it shows 1, which is exactly what I have expected
4, I have only one reference ("input")
to the object.
5, I create the iterator block which
immediately executes, because coroutines immediately execute after they
created.
6, In the gernerator block I create
a copy of the smart pointer and I also have the captured smart pointer.
With the original one together they shall make 3 strong references.
7, Now when I yield from the generator
and return back to the main flow, the "useCount" only shows 2
strong references instead of 3.
I have realized that the 2 strong references
are the original ("input") and the copy inside the generator
block ("c"). The captured one somehow removed from the refrence
counter, but its internal pointer is not set to null, so it is in a strange
state - if I can trust the debugger.
Sumamry: somehow the captured smart
pointer gets destroyed+corrupted when I yield from the coroutine, but the
local copy survives. When I use just normally use lambdas to capture smart
pointers without coroutines then it looks fine.
void
GeneratorErrortest()
{
auto
input = std::make_shared<std::vector<int>>(5);
std::function<void()>
simpleLambda = [=]()
{
auto
a = input->size();
a = input->size();
};
auto
useCount = input.use_count();
simpleLambda =
nullptr;
useCount = input.use_count();
using
namespace
DeferredLinq;
auto
numbersEnumerable = boost::make_shared<GeneratorBlockEnumerator<int>>([=](GeneratorBlockYield<int>&
yield)
{
auto
c = input;
auto
a = input->size();
yield(1);
a = input->size();
});
useCount = input.use_count();
input.reset();
numbersEnumerable->MoveNext();
numbersEnumerable->MoveNext();
}
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net