I decided to start up a new Redpoint Games blog as I wanted somewhere to put all the esoteric and weird Unreal Engine knowledge that I’ve accumulated over the past year.
Without further ado, here is how to make circularly referenced lambdas in C++; that is, lambdas that reference each other or themselves.
When writing test code for asynchronous behaviour, I’d frequently run into a problem where lambdas are dependent on each other, like so:
This obviously doesn’t work. We can’t shift the
FDelegateHandle declarations above
Cleanup, because the delegate handle is on the stack, and when the clean up lambda gets created, it’ll capture the current value of the delegate handle. Even if we later assign to
CallbackHandle, that won’t get propagated into
At the same time, the lambda we’re passing into
AddEventListener needs to be able to reference
Cleanup so that the test ends and passes immediately when the event listener fires. We could workaround the problem in this particular scenario by only cleaning up in the timer, but not only does this make the test slower, it’s not applicable to all cases with circularly dependent lambdas.
For trivial cases where we need a lambda to reference itself, we can create a double pointer to the lambda. We pass that pointer into the lambda, and after we’ve declared the lambda, we then move it onto the heap with
With the lambda referencing itself, this enables the lambda to re-invoke itself at some later point. This is useful if you need to perform retries or exponential back-off with some service, and you want to allow the developer to just pass in an
std::function or lambda and call it a day.
This solution only works if you’re cleaning up the lambda in exactly one place, and you can guarantee the code performing the clean up will only ever be called once.
For the problem we started with, we’ll need a solution that’s a little more involved. We’ll need to put our lambdas on the heap and reference count them.
FHeapLambda. This class allows you to create a “to be filled in” reference to a lambda; you can let this value be captured by other lambdas, and when it’s finally assigned, all of the copies of the
FHeapLambda will point at the same place.
This is done by allocating a state object on the heap when
FHeapLambda is first constructed (with the default constructor). Every copy and move increments the reference counter; while the destructor decrements the counter. When the reference counter reaches
0 in any of the copies, it knows it’s the last possible reference to the state and thus the heap-allocated state can be freed.
When you assign a lambda to an
FHeapLambda, the lambda gets copied into that heap allocated state.
Since we’ll often be using
FHeapLambda with clean up actions and
delete statements, the implementation below optionally supports both “always fire at least once” and “fire at most once” semantics. Combined together you can guarantee that your clean up code runs regardless of the circumstances that causes the lambda to no longer be referenced.
FHeapLambdaimplementation below has been modified to fix unsafe usage of
thisin the invocation handler and to support Clang (Android), since this blog post was originally published.
Now we can use
FHeapLambda to solve our original problem quite nicely.