Delegate gets unbound when captured by lambda

Hi, I’d like to know if this is a bug, an expected behaviour or if I’m doing something wrong. So, first, I declare a dynamic delegate inside my class:

DECLARE_DYNAMIC_DELEGATE_OneParam(FOnMapReceivedSignature, UTexture2D*, MapTexture);

Then, I declare a method that receives this delegate as a parameter:

UFUNCTION(BlueprintCallable)
bool RequestMap(const FVector2D Center, const float MinRadius, const int32 TextureWidth, const int32 TextureHeight, const FOnMapReceivedSignature& OnMapReceived);

On the RequestMap method, I then capture the delegate inside a lambda, by doing this:

Request->OnProcessRequestComplete().BindLambda(
[this, &OnMapReceived](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
     /* the lambda code */
     OnMapReceived.ExecuteIfBound(MapTexture);
}

I’m calling the method RequestMap from a blueprint. According to my debugging, right after entering the method, the delegate is bound, as expected. However, inside the lambda, it’s not anymore, i.e., the captured reference somehow becomes unboud. I have confirmed this by creating an attribute inside my class, holding the delegate on that attribute and then calling that attribute from the lambda, which works.

I’ve tried capturing the delegate either by reference and by value and, in both cases, it becomes unboud. Is that normal?

Thanks in advance!

1 Like

Doesn’t help you but I’d pass in by value unless you are certain that the delegate passed in is not on the stack (and nor will it be ever in the future).

Not sure what the best way to debug this is but potentially you could pass in a wrapper object (a static might help) that contains the delegate and see if you can capture it becoming unbound using a data breakpoint.

OK, so I’m stupid. I was sure I had made a test declaring the method parameter as a value as well, but I hadn’t. Basically, when I receive by reference and copy to a local attribute, it works, but if I capture either by value or by reference in a lambda, it doesn’t. However, If I do everything by value then it works with the lambda as well. Now I’m pretty sure this is just a C++ intricacy, but If someone could explain what’s going on, please do. :slight_smile:

As a reference you are at the mercy of whoever owns the memory. The object could be very transient - such as on the stack during program flow, or fairly transient - for instance in unreal’s memory management system but wherever it is if you don’t have control of the object and it’s memory you want to pass by value rather than reference.

When you pass an object into a lambda function C++ nicely creates a temporary class and an object for it. The class has members matching your passed in parameters. A reference is just a pointer, with some syntactic differences (probably not null, can’t be changed) and the compiler stores a pointer.

Now - beware - if you pass in a large or complex object by value it will get copied. So you need to pass in the smallest thing you safely can (a pointer to an object).

There are some interesting tricks. For instance if you pass in a ref counting smart pointer to a lambda (like TSharedPtr) the lambda object will automatically ref count the referenced object for it’s lifetime.

So in your case - the blueprint is giving you an object - the delegate - but expecting to hand it over to you. It’s done with it and cleans up, you are referencing the object it cleans up and your delegate no longer works.

There’s no magic in here, but it gets pretty complicated very fast!

And that doesn’t explain what you’re seeing exactly… but I’m going to leave the text as I enjoyed writing it.