www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Typecasting delegates

reply Seiji Emery <seijiemery gmail.com> writes:
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!
Jun 02
next sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Friday, 2 June 2017 at 21:14:29 UTC, Seiji Emery wrote:
 [...] 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?
Essentially, yes[1]. Though it's a D function pointer, not C.
 Secondly, is it safe to typecast delegates for storage purposes 
 – ie. cast `void delegate (ref Foo)` to `void delegate()`, or 
 something similar?
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?
Undefined behaviour. [1] https://dlang.org/spec/abi.html#delegates [2] https://dlang.org/phobos/core_memory.html#.GC
Jun 02
parent Mike B Johnson <Mikey Ikes.com> writes:
On Friday, 2 June 2017 at 21:42:57 UTC, Moritz Maxeiner wrote:
 On Friday, 2 June 2017 at 21:14:29 UTC, Seiji Emery wrote:
 [...] assume that they're implemented as a pair of pointers;
 Ah, and uh... one last thing. What's the worst that could 
 happen from calling a `ref Foo` signature through a void* 
 pointer?
Undefined behaviour.
His computer could expode! Or it could trigger a black hole near Alpha Centauri... You never know!
Jun 03
prev sibling next sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Fri, Jun 02, 2017 at 09:14:29PM +0000, Seiji Emery via Digitalmars-d wrote:
[...]
 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;
[...] Adam Ruppe has done this, you can probably find your answers in his code: https://github.com/adamdruppe/arsd/blob/master/eventloop.d especially the parts related to addListener() and wrap(). T -- Once bitten, twice cry...
Jun 02
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2017-06-02 23:14, Seiji Emery wrote:

 The main worry that I have is that this could
 somehow wreak havoc with the GC-managed payload pointer, but I'm not
 sure.
As long as you cast between different type of delegates I don't think it would be a problem. The context pointer is always void* regardless of the signature of the delegate. -- /Jacob Carlborg
Jun 03