Why are Delegates so complicated?

Hi,

I started looking into delegates because I want to expose hookups in certain classes so that other classes may be notified, however I think that there’s 2 problems: a) documentation isn’t clear enough, b) the delegate system is just too complex.

(a) Delegate Documentation:

First of all, I think that out of the bat it doesn’t explain well what single cast vs multi cast vs multi cast dynamic.

  • Single cast should have a one liner that says “you can only hookup one function to this delegate”.
  • Multicast should have a one liner saying that it "allows you to add multiple hookups to this delegate, so multiple of them will execute.
  • Dynamic Multicast is the same as multicast, but you don’t specify the arguments (afaict that’s the only difference).
  • Events are Multicast, that can only be “executed” by the class they belong to.

Also there’s no place that mentions that for being able to use them in BP they have to be Dynamic Multicast or Events, this info is paramount and I only found it by some comment in the answers (please correct me if I’m wrong).

It should also mention that multicast does not do anything network wise. Given the terminology, at first I thought that multicast meant that it would be “multicast by the server”, which isn’t the case.

I also think that there should be actual examples of full start to finish on how they can be used (including hooking up to BP), atm this info is fragmented and a lot of times doesn’t come from official docs.

(B) Delegate API:

TBH I find it too confusing, and it seems for no reason. Maybe I’ve just been using C# for far too long, but looking into the delegates in UE brings back bad memories from my days with C++.

For example, when looking into Dynamic Multicast delegates, I saw people reference that you should call AddDynamic to add to the delegate. However, when I tried using it, the IDE didn’t show it to me. At first I thought it was a case of old APIs going away, but it turns out that it is a macro, but why use macro for such a thing?!

Macros shouldn’t be freely used (I’m of the opinion that they should be avoided at all cost) for things like this. It makes it much harder to use the engine (for example in this case the IDE didn’t show that the API existed), and furthermore it makes it much harder for new people to use the engine.

Second thing, is why even have single binding delegates? Like what’s the point? Does it add anything? IMO it only adds confusion, delegates should just be able to have N bindings, whomever wants to use only one can use the same thing.

The third thing is why have multicast and dynamic multicast? Is it for performance reasons? Even if multicast is a percentage better in terms of performance, does it make sense for it to exist given the incompatibility with BP system (again, correct me if I’m wrong)?

Looking at it, it seems that the only delegates that should exist are Events (assuming that the dynamic performance is much worse, otherwise dynamic ones are fair simpler to use).

Finally, the APIs are very all over the place. Some call bind / execute, others add / broadcast, tbh I think it would be easier for everybody if the APIs were all the same.

Regards,
Nuno Afonso

2 Likes

Not being snide here (I’m not!), but can you do a pull request on GitHub and then submit the changes for review?

The only one worth doing is the macro change, because the other ones influence the engine (e.g. removing the single binding delegates, changing apis, etc), and also are about the engine development mentality, so it basally needs to be at least acknowledged that they are a problem before it is even worth doing anything there.

I think that delegates are fine

there terminology is a bit weird for bind and add , broadcast or execute
and I don’t think AddDynamic macro is a great choice

but I believe there are reasons for that I hope they will improve their documentation for that

My two cents:

  • I suppose the dynamic versions exist because they need a bunch of additional data and logic to be compatible with blueprints, which would be a waste of resources if you’re using them for internal purposes (for which there are many).

  • Delegates aren’t a Unreal-thing, they are a programming thing. It’s basically a convenient way to store a function/method in a variable, so you can call that function later without having to deal with C++ function/method pointers. The multicast variety is just a helper that stores a list of delegates internally. “Single-binding” delegates are absolutely necessary when you’re performing asynchronous tasks, like HTTP requests: that’s how you continue execution after the async task was done. Also, “single-binding” support return values, which don’t make any sense in multicast delegates.

First a correction, to expose to BP you need to use Dynamic multi cast, others (including Events) don’t work.

Regarding the macro, basically they’re using the macro to convert the function pointer to a string using the compiler magic (basically #X, will create “X”). I doubt anybody would want it changed tbh.

Regarding the single / multi binding, true multi binding can’t have a return value, but you can get around that by only binding one thing to a multi cast and have one of the parameters be an “out parameter”.

So basically the way I see it is that it should only be:

  • Multicast (performance reasons)
  • Dynamic Multicast (blueprint reasons)

IMO single cast and events should just be dropped.

1 Like