Hi, bit of a long-time lurker here.
I had a few questions about D delegates: first, D delegates are
the size of two pointers (16 bytes) on my machine, so I'd assume
that they're implemented as a pair of pointers; presumably a C
function pointer, and a GC-managed payload / context / this
pointer – is this correct?
Secondly, is it safe to typecast delegates for storage purposes –
ie. cast `void delegate (ref Foo)` to `void delegate()`, or
something similar? Since delegates seem to just be a pair of
pointers, it seems like this should work; it's perfectly safe to
cast C function pointers, for instance, provided that they're
cast back to the correct type before actually being called. The
main worry that I have is that this could somehow wreak havoc
with the GC-managed payload pointer, but I'm not sure. It seems
like this shouldn't be a problem since payloads change between
delegates anyways (reference to object vs reference to function
scope), and delegates with different signatures could point to
the same object / scope – ie. the payload type and pointed-to
function signature have nothing to do with each other, and GC
references to and within the payload should still work properly
even if I cast a delegate with N arguments to one with zero. But
I'm just guessing here.
For context, I'm working on an event system for a small hobby
project, and it would be extremely useful if I could store
delegates with different type signatures in the same storage
container; these delegates will always be converted back to the
correct type signature before being called (probably by storing
typeinfo of the first / only event argument and comparing that),
so that's a non-issue. Using a different approach than putting
listeners that can listen to any kind of event everywhere might
be better and more efficient architecturally, but would be much
less flexible / powerful I think. And yes, I've looked into
std.variadic; I just want something slightly lower-level with
more control over how I actually dispatch and filter events.
Ah, and uh... one last thing. What's the worst that could happen
from calling a `ref Foo` signature through a void* pointer? Once
again, using delegate / function pointer casting shenanigans. Foo
is a struct. The memory layout seems to be the same, so this
technically works.
Thanks!

It works for me, but if you're asking in terms of spec: I don't
think it is specified.
Though considering that you have to cast it back to the correct
type I wouldn't call it safe.

The main worry that I have is that this could somehow wreak
havoc with the GC-managed payload pointer, but I'm not sure.

AFAIK the GC won't collect any object that it considers live, so
as long as you keep the context pointer somewhere the GC
considers live, you are safe. See [2] on what the current
implementation considers live.

For context, I'm working on an event system for a small hobby
project, and it would be extremely useful if I could store
delegates with different type signatures in the same storage
container; [...]

Be sure that if you interact with operating system mechanisms
such as epoll or kqueue to never have the OS C side point to
memory that is not also pointed to from the D side or registered
via GC.addRoot / GC.addRange, or you are likely going to end up
with C pointers into collected memory.

Ah, and uh... one last thing. What's the worst that could
happen from calling a `ref Foo` signature through a void*
pointer?