www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Allocating delegate on heap?

reply Rick Mann <rmann-d-lang latencyzero.com> writes:
It appears that a delegate can't be directly allocated on the heap; only as
part of a struct, class or array. This is rather annoying.

I'm trying to wrap a C api for event handling (on Mac OS X). In it, you
register event handlers as C-linkage callback functions, and you register a bit
of "user data" (4 bytes, usually a pointer to something). In your callback, the
user data used when you registered is provided, and you typically cast it to
your struct or class or whatever.

I'd like to use a delegate as my user data, but it's 8 bytes. So I need to pass
a pointer to the delegate, and I need the delegate to live longer than the
scope of the code that registered the callback in the first place. However, I
don't see how to create a delegate on the heap. Is there any way? (Other than
to wrap it in some clunky struct?)

TIA,
Rick
Feb 19 2007
next sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Rick Mann wrote:
 It appears that a delegate can't be directly allocated on the heap; only as
part of a struct, class or array. This is rather annoying.
 
 I'm trying to wrap a C api for event handling (on Mac OS X). In it, you
register event handlers as C-linkage callback functions, and you register a bit
of "user data" (4 bytes, usually a pointer to something). In your callback, the
user data used when you registered is provided, and you typically cast it to
your struct or class or whatever.
 
 I'd like to use a delegate as my user data, but it's 8 bytes. So I need to
pass a pointer to the delegate, and I need the delegate to live longer than the
scope of the code that registered the callback in the first place. However, I
don't see how to create a delegate on the heap. Is there any way? (Other than
to wrap it in some clunky struct?)
 
 TIA,
 Rick
[test.d] import std.stdio; void main() { auto dg = new void delegate(); } $ dmd test.d test.d(4): Error: new can only create structs, dynamic arrays or class objects, not void delegate()'s No, you can't place delegates on the heap. I'm afraid you're stuck with using a struct. However, there's a very important detail which you should not overlook: You are passing a pointer to GC-controlled data to a C library. The D garbage collector doesn't scan the C library for references, so you will need to keep a reference to any structs you pass to Carbon in your D code. Otherwise, the GC will feel free to collect them when you least expect it. This can be done with an AA: struct DelegateStruct { void delegate() dg; } bool[DelegateStruct*] references; DelegateStruct* get_ref(void delegate() dg) { auto s = new DelegateStruct; references[s] = true; s.dg = dg; return s; } void drop_ref(DelegateStruct* s) { references.remove(s); } -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Feb 19 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Kirk McDonald wrote:
 [test.d]
 import std.stdio;
 
 void main() {
     auto dg = new void delegate();
 }
 
 $ dmd test.d
 test.d(4): Error: new can only create structs, dynamic arrays or class 
 objects, not void delegate()'s
That error message is wrong, by the way. It may not work for delegates, but it works just fine for ints. Does anyone know the particular reason delegates are disallowed here? Since I knew it worked for things like ints, I assumed it worked for all types. I hadn't actually ever tried it though, so I was a bit surprised by this thread...
Feb 19 2007
parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Frits van Bommel wrote:
 Kirk McDonald wrote:
 [test.d]
 import std.stdio;

 void main() {
     auto dg = new void delegate();
 }

 $ dmd test.d
 test.d(4): Error: new can only create structs, dynamic arrays or class 
 objects, not void delegate()'s
That error message is wrong, by the way. It may not work for delegates, but it works just fine for ints. Does anyone know the particular reason delegates are disallowed here? Since I knew it worked for things like ints, I assumed it worked for all types. I hadn't actually ever tried it though, so I was a bit surprised by this thread...
In my opinion, it /should/ work, but I just go by what the compiler tells me. :-) Even if this did work, the point about keeping local references to GC-controlled data remains. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Feb 19 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Kirk McDonald wrote:
 Frits van Bommel wrote:
 Kirk McDonald wrote:
 [test.d]
 import std.stdio;

 void main() {
     auto dg = new void delegate();
 }

 $ dmd test.d
 test.d(4): Error: new can only create structs, dynamic arrays or 
 class objects, not void delegate()'s
That error message is wrong, by the way. It may not work for delegates, but it works just fine for ints. Does anyone know the particular reason delegates are disallowed here? Since I knew it worked for things like ints, I assumed it worked for all types. I hadn't actually ever tried it though, so I was a bit surprised by this thread...
In my opinion, it /should/ work, but I just go by what the compiler tells me. :-)
I agree, that's why I asked if there was any particular reason it didn't :). It seems like a very silly restriction to me.
 Even if this did work, the point about keeping local references to 
 GC-controlled data remains.
I didn't comment on that, did I?
Feb 19 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Frits van Bommel" <fvbommel REMwOVExCAPSs.nl> wrote in message 
news:erc6ut$2pbv$1 digitalmars.com...
 Even if this did work, the point about keeping local references to 
 GC-controlled data remains.
I didn't comment on that, did I?
You shouldn't take things so personally, Frits! Kirk was just saying that as a "back on topic" comment.
Feb 19 2007
prev sibling parent reply Rick Mann <rmann-d-lang latencyzero.com> writes:
Kirk McDonald Wrote:

 However, there's a very important detail which you should not overlook: 
 You are passing a pointer to GC-controlled data to a C library. The D 
 garbage collector doesn't scan the C library for references, so you will 
 need to keep a reference to any structs you pass to Carbon in your D 
 code. Otherwise, the GC will feel free to collect them when you least 
 expect it. This can be done with an AA:
Yes, thank you, I was aware of that. As it turns out, there's other stuff I need to do with my delegate that I can't do (namely, treat the .ptr portion of it as a pointer to a class derived from some interface Foo), so I ended up having to create a little class wrapper. I add the class to the GC's root with std.gc.addRoot(). This should work, right (rather than storing it in an AA)?
Feb 19 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Rick Mann wrote:
 Kirk McDonald Wrote:
 
 However, there's a very important detail which you should not overlook: 
 You are passing a pointer to GC-controlled data to a C library. The D 
 garbage collector doesn't scan the C library for references, so you will 
 need to keep a reference to any structs you pass to Carbon in your D 
 code. Otherwise, the GC will feel free to collect them when you least 
 expect it. This can be done with an AA:
Yes, thank you, I was aware of that. As it turns out, there's other stuff I need to do with my delegate that I can't do (namely, treat the .ptr portion of it as a pointer to a class derived from some interface Foo), so I ended up having to create a little class wrapper. I add the class to the GC's root with std.gc.addRoot(). This should work, right (rather than storing it in an AA)?
That's going to tie you to a particular gc implementation, though. So I think the practice should be avoided unless you have a compelling reason to do it that way. --bb
Feb 19 2007