May 07, 2012
Oh, and ditto with the destructor: I need to be able to call the destructor manually, because the C does that inside a callback on my behalf.

On Monday, 7 May 2012 at 19:08:18 UTC, Mehrdad wrote:
> No, I *am* placing it on the heap.
>
> I'm just asking if I can call the constructor manually, because
> (like I wrote in my first post...) sometimes the C code you're
> interoperating with takes control away from you, and just calls a
> callback on your behalf when constructing the object.
>
> (Yes, I realize there are different solutions to this problem.
> They're just not as elegant.)
May 07, 2012
On Mon, 07 May 2012 15:08:16 -0400, Mehrdad <wfunction@hotmail.com> wrote:

> On Monday, 7 May 2012 at 17:04:08 UTC, Steven Schveighoffer wrote:
>> Not really, but then again, if you are not placing the class into the GC heap, who cares?  You have to manually delete anyways, just use your specialized 'delete' function instead of delete.
>>
>> -Steve
>
> No, I *am* placing it on the heap.

You hadn't made that clear.

In your first post, I was assuming your ptr came from some non-GC allocated space, which is why you wanted the ability to intercept it.

> I'm just asking if I can call the constructor manually, because
> (like I wrote in my first post...) sometimes the C code you're
> interoperating with takes control away from you, and just calls a
> callback on your behalf when constructing the object.

I wasn't sure, but I just tried it out:

import std.stdio;
extern(C) void *_d_newclass(TypeInfo t); // this is defined in rt/lifetime.d  I cheated and used this, because I didn't want to have to type everything that was in it :)

class C
{
    int x;
    this(int x){this.x = x;}
}

void main()
{
    C c = cast(C)_d_newclass(typeid(C));
    c.__ctor(1);
    writeln(c.x); // outputs 1
}

Seems to work

-Steve
May 07, 2012
On Mon, 07 May 2012 15:09:34 -0400, Mehrdad <wfunction@hotmail.com> wrote:

> Oh, and ditto with the destructor: I need to be able to call the destructor manually, because the C does that inside a callback on my behalf.

You definitely can do this. I think it's just __dtor.  But I'm not sure if that calls the whole chain.  rt_finalize, the function that actually calls the dtors calls each dtor in the chain individually.  You can see how it works here:

https://github.com/D-Programming-Language/druntime/blob/master/src/rt/lifetime.d#L1161

However, if C is calling your dtor, I'd highly recommend *not* using GC memory for your class data, you can std.conv.emplace it in C-malloced memory.  If it has any references to GC data, use GC.addRoot to make sure that data isn't accidentally collected, and use GC.removeRoot before destruction.

-Steve
May 07, 2012
On 2012-05-07 18:01, Mehrdad wrote:
> On Monday, 7 May 2012 at 09:18:11 UTC, David Nadlinger wrote:
>>
>> Doing it without a separate factory function (and maybe disabling new
>> along with it by protecting the constructor) is not possible in D.
>
> Okay that answers my question then.

No, have a look at this example:

http://pastebin.com/UV5R82dg

A bit of a hack. An other option would to use a separate public method that acts as a constructor.

-- 
/Jacob Carlborg
May 07, 2012
On 2012-05-07 11:18, David Nadlinger wrote:
> On Monday, 7 May 2012 at 07:28:18 UTC, Mehrdad wrote:
>> Is this something that actually modifies the 'new' operator, or is it
>> just a separate factory function that my code would need to switch to
>> using?
>
> Doing it without a separate factory function (and maybe disabling new
> along with it by protecting the constructor) is not possible in D.
> However, I don't quite see what it would gain you in the first place –
> besides potentially screwing up the semantics users expect from new…
>
> David

http://pastebin.com/UV5R82dg

A bit of a hack. An other option would to use a separate public method that acts as a constructor.

-- 
/Jacob Carlborg
May 07, 2012
On 2012-05-07 09:28, Mehrdad wrote:

> Is this something that actually modifies the 'new' operator, or is it
> just a separate factory function that my code would need to switch to
> using?

This does not modify the new-operator. "_d_newclass" is actually the runtime function that is called by the compiler when it sees "new" in the code.

It would be a separate factory function. As far as I know it's not possible to _not_ call the constructor when using new.

-- 
/Jacob Carlborg
May 07, 2012
On 2012-05-07 21:09, Mehrdad wrote:
> Oh, and ditto with the destructor: I need to be able to call the
> destructor manually, because the C does that inside a callback on my
> behalf.

About the destructor, have a look at how "clear" is implemented, it's supposed to replace "delete". Don't remember where it's defined.

-- 
/Jacob Carlborg
May 07, 2012
On Monday, 7 May 2012 at 19:39:04 UTC, Steven Schveighoffer wrote:
>> I'm just asking if I can call the constructor manually, because
>> (like I wrote in my first post...) sometimes the C code you're
>> interoperating with takes control away from you, and just calls a
>> callback on your behalf when constructing the object.
>
> I wasn't sure, but I just tried it out:
>
> import std.stdio;
> extern(C) void *_d_newclass(TypeInfo t);
>
> class C
> {
>     int x;
>     this(int x){this.x = x;}
> }
>
> void main()
> {
>     C c = cast(C)_d_newclass(typeid(C));
>     c.__ctor(1);
>     writeln(c.x); // outputs 1
> }
>
> Seems to work
>
> -Steve


Nonono :( you still missed what I was saying.

Unless you're suggesting I replace _d_newclass, but that isn't type-specific (and I certainly don't want to replace it globally).

Here's another attempt at the explanation:

I definitely CAN make a helper method to do this for me. It's the whole factory pattern, etc.

The problems are that:
(1) The user wouldn't be able to say "new Window()" anymore (I already explained why)
(2) The object couldn't be garbage collected.

Regarding #2, in case it doesn't make sense why:
It's because the C code takes control *AWAY* from you when you tell it to destroy the object.
In my case, DestroyWindow(HWND) calls WndProc(WM_DESTROY,..), which is a member function.

I CANNOT call DestroyWindow() in the finalizer, because then WndProc would be called on a *derived* class during cleanup. (It's the *same* issue as with the constructor.) So I'd have to do this manually, which defeats the whole point of making the object garbage-collectible in the first place.


What I'm  *want* to be able to do is, basically, to specify that I have a static method that will take care of finalizing an object. (Something like: void finalize(Window o))

Then, when the object is being finalized, the garbage collector would NOT call the object's finalizer directly. Instead, it would call my STATIC method to do the rest of the cleanup.

The STATIC destruction method would call DestroyWindow() on the handle, and DestroyWindow() would send the notification to the window through a modified WndProc() (which I can always redirect, to make it NOT be a member function during the following phase).

It would pass in a context parameter -- which would probably be the Window object itself -- to my (redirected) WndProc.
The WndProc() would THEN call the Window's finalizer manually (and tell the GC that the object is done being finalized, so it can be collected).



I can't think of any "nice" way around this. Either the HWND would have to be managed manually (in which case, that defeats the purpose of using the GC to ensure its destruction along with the object), or I would need an extra indirection in Window (which REALLY overcomplicates the design, and introduces lots of inheritance/template usage issues).



The thing is, I think this doesn't need any compiler support either -- just a change in druntime. (I could be wrong though, since it might involve modifying TypeInfo_Class, I'm not sure.)

Do you (or others) reckon a pull request to modify _d_newclass and _d_delclass so that they would call a custom static "constructor" and "destructor" for a class (if it specifies this should be the case) might be accepted?

If there's the possibility then I might try hacking around with it...

I can already see there being issues with inheritance, but I think it would be fixable in some cases, and disallowable in others. (Would obviously need to try it and see though.)
May 07, 2012
On Mon, 07 May 2012 16:09:06 -0400, Mehrdad <wfunction@hotmail.com> wrote:

> On Monday, 7 May 2012 at 19:39:04 UTC, Steven Schveighoffer wrote:
>>> I'm just asking if I can call the constructor manually, because
>>> (like I wrote in my first post...) sometimes the C code you're
>>> interoperating with takes control away from you, and just calls a
>>> callback on your behalf when constructing the object.
>>
>> I wasn't sure, but I just tried it out:
>>
>> import std.stdio;
>> extern(C) void *_d_newclass(TypeInfo t);
>>
>> class C
>> {
>>     int x;
>>     this(int x){this.x = x;}
>> }
>>
>> void main()
>> {
>>     C c = cast(C)_d_newclass(typeid(C));
>>     c.__ctor(1);
>>     writeln(c.x); // outputs 1
>> }
>>
>> Seems to work
>>
>> -Steve
>
>
> Nonono :( you still missed what I was saying.
>
> Unless you're suggesting I replace _d_newclass, but that isn't type-specific (and I certainly don't want to replace it globally).

All _d_newclass does is allocate memory for the object and initialize it to it's initial state (i.e. if an int member is initialized to 5, it does this).  It does not call the constructor.  So no, I was assuming you'd either re-use it, or implement some other allocation means.

> Here's another attempt at the explanation:
>
> I definitely CAN make a helper method to do this for me. It's the whole factory pattern, etc.
>
> The problems are that:
> (1) The user wouldn't be able to say "new Window()" anymore (I already explained why)

Right.  new === allocate memory on the GC heap (via _d_newclass) and call the constructor.  If you don't want that you cannot use new.

> (2) The object couldn't be garbage collected.

Then you definitely don't want GC allocated memory.

> Regarding #2, in case it doesn't make sense why:
> It's because the C code takes control *AWAY* from you when you tell it to destroy the object.
> In my case, DestroyWindow(HWND) calls WndProc(WM_DESTROY,..), which is a member function.

So it should not call GC.malloc to create the memory, use C's malloc, and GC.addRoot.

> I CANNOT call DestroyWindow() in the finalizer, because then WndProc would be called on a *derived* class during cleanup. (It's the *same* issue as with the constructor.) So I'd have to do this manually, which defeats the whole point of making the object garbage-collectible in the first place.
>
>
> What I'm  *want* to be able to do is, basically, to specify that I have a static method that will take care of finalizing an object. (Something like: void finalize(Window o))

What I think you want is both an allocator/initializer and a destructor.  Kind of like malloc and free.  The two are going to be tied together, so you should always have to call the correct destructor for an object allocated with the initializer.

> Then, when the object is being finalized, the garbage collector would NOT call the object's finalizer directly. Instead, it would call my STATIC method to do the rest of the cleanup.
>
> The STATIC destruction method would call DestroyWindow() on the handle, and DestroyWindow() would send the notification to the window through a modified WndProc() (which I can always redirect, to make it NOT be a member function during the following phase).
>
> It would pass in a context parameter -- which would probably be the Window object itself -- to my (redirected) WndProc.
> The WndProc() would THEN call the Window's finalizer manually (and tell the GC that the object is done being finalized, so it can be collected).

I think avoiding the GC completely is a much better idea.  All you need to be concerned about is references from your object into the GC heap (i.e. use addRoot/removeRoot).

> I can't think of any "nice" way around this. Either the HWND would have to be managed manually (in which case, that defeats the purpose of using the GC to ensure its destruction along with the object), or I would need an extra indirection in Window (which REALLY overcomplicates the design, and introduces lots of inheritance/template usage issues).

I guess I don't really understand that.  Who is responsible for cleaning up your class instance?  The way I was understanding your description, I thought it was the C window runtime calling a callback you provide to it.  Why do you need to have the GC clean it up?

> Do you (or others) reckon a pull request to modify _d_newclass and _d_delclass so that they would call a custom static "constructor" and "destructor" for a class (if it specifies this should be the case) might be accepted?

_d_newclass does *not* call ctors, the compiler does that when you say "new"  Otherwise, _d_newclass it would have to handle passing through all the parameters to the ctor.

It does roughly what I showed you in my quick example.

-Steve
May 07, 2012
On Monday, 7 May 2012 at 21:07:15 UTC, Steven Schveighoffer wrote:
> I guess I don't really understand that.  Who is responsible for cleaning up your class instance?  The way I was understanding your description, I thought it was the C window runtime calling a callback you provide to it.  Why do you need to have the GC clean it up?


Ah, I see why that's confusing.

Here's a (hopefully better) explanation:

Just as with any other object that represents an outside resource (e.g. a File/Stream/whatever), the lifetime of the unmanaged object should always follow the lifetime of the managed object.

In other words, this means that the creation of a Window object MUST be associated with the system call CreateWindow() (which in turns calls the window dispatcher function, WndProc, with the message WM_CREATE).

And, more importantly, this means that if the GC collects the Window object, then DestroyWindow() MUST be called on the kernel object, so that the window handle doesn't get leaked.

Just as with any other resource.


The trouble is that, as-is, this behavior is NOT implementable with a simple Window class whose constructor calls CreateWindow() and whose destructor calls DestroyWindow().

Why? Because if you were to do that in the constructor or destructor, the system would call back your WndProc() function, which is a *virtual* function meant to be overridden in the derived classes (so that they can handle events, such as the creation/destruction of the window, or the calculation of the window size, etc. properly).

That would mean your WndProc() in the derived instance would be called *before* the constructor of the derived instance is called, which is obviously not what you want.

Ditto with the destructor -- if you were to call DestroyWindow() in the ~Window(), then it would call back WndProc with the message WM_DESTROY in the derived class.



The only solution I see is to somehow call DestroyWindow() *BEFORE* you call the destructor, and call the destructor manually when the WM_DESTROY message is received. Ditto with the constructor: you need to somehow call CreateWindow() *BEFORE* you call the constructor, so that when you receive the WM_CREATE message, you can call the constructor manually.

Only by hijacking the construction and destruction call can you guarantee that the construction/destruction happens in an orderly fashion, i.e. that the kernel object lifetime tracks the user object lifetime properly. Otherwise you either miss getting some notifications at critical points (which results in incorrect code, which some people may or may not care about), or you risk getting a handle leak (also obviously bad).

Or you risk making the design pattern pretty horrific, kind of like how C# has a "NativeWindow" inside of the Control class, and they both have creation parameters, and they both register themselves ("park" themselves) globally on a static/shared field, and run into all sorts of ugly issues that you shouldn't need to run into. (If you look at the code and understand what they're doing, you'll see what I mean when I say it's ugly...)


Summary: Yes, I need the GC so I can manage the HWND lifetime properly, i.e. so it tracks the lifetime of the Window object (otherwise you can easily leak a handle). But in order to do this, I also need to be able to call the constructor/destructor manually, because the C code does this through a callback, which isn't possible when your object is being GC'd, due to virtual method re-entrancy issues with finalization.


Does that make sense? Is any part of it still unclear?