I have an array of delegates which return numbers as such:
UDELEGATE()
DECLARE_DYNAMIC_DELEGATE_RetVal(int, FNumBP);
DECLARE_DELEGATE_RetVal(int, FNum);
UCLASS()
class TESTING_API AMyActor : public AActor {
GENERATED_BODY()
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FNumBP> AllNumsBP;
TArray<FNum> AllNums;
UFUNCTION()
virtual int NumA() { return 37; }
UFUNCTION()
virtual int NumB() { return 73; }
UFUNCTION()
virtual int Sum() {
int Total = 0;
for (auto& Num : AllNumsBP) {
Total += Num.Execute();
}
for (auto& Num : AllNums) {
Total += Num.Execute();
}
return Total;
}
virtual void BeginPlay() override {
Super::BeginPlay();
// Dynamic
FNumBP NumDelA;
NumDelA.BindDynamic(this, &AMyActor::NumA);
FNumBP NumDelB;
NumDelB.BindDynamic(this, &AMyActor::NumB);
AllNumsBP.Add(NumDelA);
AllNumsBP.Add(NumDelB);
// Non Dynamic
FNum NumDelC;
NumDelC.BindLambda([]() { return 10; });
FNum NumDelD;
NumDelD.BindLambda([]() { return 11; });
AllNums.Add(NumDelC);
AllNums.Add(NumDelD);
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, FString::Printf(TEXT("Sum=%d"), Sum()));
}
};
And it works perfectly, but I’m not sure if this is the right way to store RetVal delegates in an array, could they be invalidated at any time? or something else that might cause errors?
Note: This is just a simplified example, not actually how I use them
Delegates are fairly simple structs, even though their macro declarations are quite complex. They’re not UObjects, they’re not reference counted, they won’t suddenly get invalidated while you’re using them. As long as the actor containing them is valid (so probably a manager of some sorts with an appropriate lifetime) and the objects registering are valid (so make sure to clean up any objects that get destroyed after registering), everything should work fine.
While I obviously don’t see the full logic since this is a simplified example (which I appreciate), I’d still blindly suggest (re)considering somehow merging the delegates if possible because this seems quite involved, but you seem to have a good grasp of the situation seeing as you know how to simplify it into an example so you’ve probably considered it.
Thanks for the reassurance! Once your game crashes you tend to become paranoid
Also could you give a short example of what you mean by these 2
so probably a manager of some sorts with an appropriate lifetime
so make sure to clean up any objects that get destroyed after registering
I think you mean using AActor::EndPlay(), ~destructor, IsValid() etc ? but just to make sure a little example would go a long way
Also it’s not possible to merge the functionality into a single delegate, It’s kind of fundamental & intentional for what I’m trying to make but thank you for the suggestion!
I think you got the idea, I’ll rephrase anyway just in case.
so probably a manager of some sorts with an appropriate lifetime
In other words, make sure the Actor holding your delegates isn’t destroyed during the lifetime of other objects that use it to register/deregister delegates. People usually use “manager” (just a name) classes for functionalities like these, creating them when the game starts and never destroying them. Subsystems (actual UE keyword) are also very appropriate to hold such functionalities, possibly better than actors.
so make sure to clean up any objects that get destroyed after registering
Your delegate container (AMyActor in the example) has no idea of the lifetime of objects registering on it so it’s up to them (the objects) to clean up their delegates (as you yourself said) on EndPlay and/or other appropriate events. Otherwise you may end up broadcasting/executing invalid callbacks.