www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - scope(~this)

reply Inquie <Inquie data1.com> writes:
Is there any easy way to create a scope for termination of the 
object?

I have a template method that takes a type and allocates and 
deallocates based on that type.

class bar
{
    void foo(T)()
    {
       T x;
       alloc(x);
       scope(~this) dealloc(x); // hypothetical that wraps the 
statement in a lambda and deallocates in the destructor

       ... x must stay allocated until class instance 
termination(has to do with COM, can't release it in foo)
    }

}

Now, x cannot be a field because T is unknown(i suppose I could 
use object, void*, etc, but...).



I realize there are ways to work around this. I will create 
lambdas that release them since that is easiest. I could return x 
but that creates a mess.

Hoping that either something like this exists or could be a 
feature enhancement.
Mar 12
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Sunday, 12 March 2017 at 21:38:44 UTC, Inquie wrote:
 Is there any easy way to create a scope for termination of the 
 object?

 [...]
scope(exit)
Mar 12
parent Inquie <Inquie data1.com> writes:
On Sunday, 12 March 2017 at 22:13:21 UTC, Stefan Koch wrote:
 On Sunday, 12 March 2017 at 21:38:44 UTC, Inquie wrote:
 Is there any easy way to create a scope for termination of the 
 object?

 [...]
scope(exit)
That is for the function, correct? If I release the resource at the end of the function, the COM interface will no longer be valid. It must be done when the class terminates, not the function.
Mar 12
prev sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Sunday, 12 March 2017 at 21:38:44 UTC, Inquie wrote:
 Is there any easy way to create a scope for termination of the 
 object?

 I have a template method that takes a type and allocates and 
 deallocates based on that type.

 class bar
 {
    void foo(T)()
    {
       T x;
       alloc(x);
       scope(~this) dealloc(x); // hypothetical that wraps the 
 statement in a lambda and deallocates in the destructor

       ... x must stay allocated until class instance 
 termination(has to do with COM, can't release it in foo)
    }

 }

 Now, x cannot be a field because T is unknown(i suppose I could 
 use object, void*, etc, but...).
If it is COM then you should use IUnknown (the COM root interface),or if you are expecting multiple calls to foo, an array of IUnknown. I think the GC will clean up completely for you in either case and call release(?) on the member(s). Also as it is COM you probably don't need to template it, just choose T to be the most recent ancestor of all (old) T's you would be expecting foo to be instantiated with (e.g. IUnknown if you expect any COM object.
Mar 12
parent reply Inquie <Inquie data1.com> writes:
On Monday, 13 March 2017 at 05:18:18 UTC, Nicholas Wilson wrote:
 On Sunday, 12 March 2017 at 21:38:44 UTC, Inquie wrote:
 Is there any easy way to create a scope for termination of the 
 object?

 I have a template method that takes a type and allocates and 
 deallocates based on that type.

 class bar
 {
    void foo(T)()
    {
       T x;
       alloc(x);
       scope(~this) dealloc(x); // hypothetical that wraps the 
 statement in a lambda and deallocates in the destructor

       ... x must stay allocated until class instance 
 termination(has to do with COM, can't release it in foo)
    }

 }

 Now, x cannot be a field because T is unknown(i suppose I 
 could use object, void*, etc, but...).
If it is COM then you should use IUnknown (the COM root interface),or if you are expecting multiple calls to foo, an array of IUnknown. I think the GC will clean up completely for you in either case and call release(?) on the member(s). Also as it is COM you probably don't need to template it, just choose T to be the most recent ancestor of all (old) T's you would be expecting foo to be instantiated with (e.g. IUnknown if you expect any COM object.
Sorry, but that is missing the point! First, it isn't that simple. I do not think the GC will call release as the GC has no idea of COM nor the interfaces. While it might be safe not to release them and let the OS deal with it, this is not standard. The template is to not part of COM but required to dynamically instantiate the COM interface that is not supplied(using GetIDsOfNames). So, the way it work is, I have a function that takes an interface which specifies the dynamic COM functions. Using GetIDsOfNames(since that seems to be the only way) gets the function pointers to these functions in which I build up a new object representing that interface. The problem is, One must use CoCreateInterface to create the COM object to use GetIDsOfNames(neither of which depends on the interface). But CoCreateInterface returns the interface with the Release function as a member and this function must be called later(at terminate of the overall class handling all this stuff). But since I have to create a variable with the interface to get the methods and to pass to CoCreateInstance, and eventually use it to call Release, I can't discard the variable at the end of the function. Basically the interface's lifetime must exist for the entirety of the containing object. Normally one would use the constructor and class fields to manage this, but it is impossible here because the actual interfaces are unknown at compile time(they are extracted from an idl by this class). The only thing I could do is keep an array of all these IUnknown interfaces(sorta using the above technique), since IUnknown has the Release function in it, basically using your suggestion or what I originally said is a feasible solution. But this pollutes the class name space unnecessarily(I know it is just an array, but ultimately should be unnecessary). Rather, if I could simply use a scope(~this) or some other mechanism, everything will be automatically taken care of. scope(~this) X.Release(); would realize that X, the local variable in the function, will be used and the GC will not free it just like delegates do(although it would need to be copied to the heap so future function calls won't corrupt the value when used on the stack, or, alternatively I could malloc it in the function instead). Then, on destruction, the scope is executed. X still exists. In this case, it's 1 line of code rather than about 5(a class field array, an append, and calling release on all appends in the destructor). It's very clear and very precise. It's very similar to scope(exit) for function but for classes(since classes can "exit" too). It may be better to call it scope(this) which is more clear(when the scope of the object instance is over, then "execute" what follows). I think this feature would actually be quite useful. Instead of handling many things in the destructor, we can handle them on site. e.g., any class resources that are allocated could then have a scope(this) right after allocation to deallocate them instead of having to push it in the the destructor which separates the code visually and programmatically. The same argument which is used to make scope(exit) useful, but on a higher level... Which, if we follow it, we should then also have scope(application) or something for application level scope.
Mar 13
parent reply thedeemon <dlang thedeemon.com> writes:
On Monday, 13 March 2017 at 14:28:01 UTC, Inquie wrote:
 On Monday, 13 March 2017 at 05:18:18 UTC, Nicholas Wilson wrote:
 On Sunday, 12 March 2017 at 21:38:44 UTC, Inquie wrote:
 Is there any easy way to create a scope for termination of 
 the object?

 I have a template method that takes a type and allocates and 
 deallocates based on that type.

 class bar
 {
    void foo(T)()
    {
       T x;
       alloc(x);
       scope(~this) dealloc(x); // hypothetical that wraps the 
 statement in a lambda and deallocates in the destructor

       ... x must stay allocated until class instance 
 termination(has to do with COM, can't release it in foo)
    }

 }
I think the feature you're asking for is too complicated/involved for a language feature. Because it means there must be some implicit array in each object of your 'bar' class that holds some number of closures that will be executed in destructor. This affects object's memory layout and raises questions of allocating memory for those closures and since those closures will have pointers to some data (like 'x' here) it affects garbage collection. So there are a lot of things to be careful about and things that might affect other language features we haven't thought about yet. This is something quite big and something that affects a lot of code, not just a couple of classes you'll write in your one app. Probably it would be better to implement it as a library feature. Just make a base class having a method for registering such closures and calling them in destructor, and inherit from it or just embed it in your 'bar'.
Mar 13
parent reply Inquie <Inquie data1.com> writes:
On Tuesday, 14 March 2017 at 05:33:28 UTC, thedeemon wrote:
 On Monday, 13 March 2017 at 14:28:01 UTC, Inquie wrote:
 On Monday, 13 March 2017 at 05:18:18 UTC, Nicholas Wilson 
 wrote:
 On Sunday, 12 March 2017 at 21:38:44 UTC, Inquie wrote:
 Is there any easy way to create a scope for termination of 
 the object?

 I have a template method that takes a type and allocates and 
 deallocates based on that type.

 class bar
 {
    void foo(T)()
    {
       T x;
       alloc(x);
       scope(~this) dealloc(x); // hypothetical that wraps 
 the statement in a lambda and deallocates in the destructor

       ... x must stay allocated until class instance 
 termination(has to do with COM, can't release it in foo)
    }

 }
I think the feature you're asking for is too complicated/involved for a language feature. Because it means there must be some implicit array in each object of your 'bar' class that holds some number of closures that will be executed in destructor. This affects object's memory layout and raises questions of allocating memory for those closures and since those closures will have pointers to some data (like 'x' here) it affects garbage collection. So there are a lot of things to be careful about and things that might affect other language features we haven't thought about yet. This is something quite big and something that affects a lot of code, not just a couple of classes you'll write in your one app. Probably it would be better to implement it as a library feature. Just make a base class having a method for registering such closures and calling them in destructor, and inherit from it or just embed it in your 'bar'.
Complexity is in the eye of the beholder. Children think many things are complex when they are not. If a library solution could be created that is as seamless as a language solution, then I guess it would work. The downside of a library solution is uniformity of syntax and added verbosity. There is really no any arrays to keep track of or anything like that matter you stated. It requires creating a delegate to wrap the scope block and a copy of the variable to one on the heap. The GC uses arrays and that happens regardless. No reason for the compiler to create a new array. 3 steps: 1. Compiler copies local variables to heap(the "closure" part, which actually means it is not closing anything as a normal delegate would require). 2. The compiler creates a delegate. No big deal, does this in many places. 3. The compiler calls all the delegates on destruction. The only new part. But not difficult. Create a ScopeThis(...) and adds no extra overhead would be nice but I see that as being more complex. How can we determine what are variables that need to be copied to the heap? How can we hook in to the ~this? (can't have multiple ones, can we?) If you can come up with a working ScopeThis that doesn't have any more overhead than a language version, I'd be all for it, I don't know or see how it could be done.
       ScopeThis!("dealloc(x);")
Must determine that x is a variable(hard?) and copy it to the heap(easy). Must create access to any local functions used(e.g., if dealloc is local). Then must hook in to ~this to execute the code. It would be nicer to not have to use a string but it would work easy since we could use a mixin and modify the string easily once we could parse it.
Mar 14
next sibling parent reply thedeemon <dlang thedeemon.com> writes:
On Tuesday, 14 March 2017 at 14:35:11 UTC, Inquie wrote:
 There is really no any arrays to keep track of or anything like 
 that matter you stated.
...
 3 steps:
...
 3. The compiler calls all the delegates on destruction.
Here you are. "All the delegates" - where are they stored? This is the array of delegates I was talking about. In a library solution you don't even need to think about the string mixins and variables copying. Just make a method that takes a delegate. Then call it like this: scopeThis({ dealloc(x); }); The compiler will create the delegate for you and store x in the heap in the first place. In scopeThis() you just add the passed delegate to an array of other delegates, then in destructor call them. Seems rather trivial, I don't see a need for a language feature that affects all the classes and objects.
Mar 15
parent reply Inquie <Inquie data1.com> writes:
On Wednesday, 15 March 2017 at 08:17:11 UTC, thedeemon wrote:
 On Tuesday, 14 March 2017 at 14:35:11 UTC, Inquie wrote:
 There is really no any arrays to keep track of or anything 
 like that matter you stated.
...
 3 steps:
...
 3. The compiler calls all the delegates on destruction.
Here you are. "All the delegates" - where are they stored? This is the array of delegates I was talking about. In a library solution you don't even need to think about the string mixins and variables copying. Just make a method that takes a delegate. Then call it like this: scopeThis({ dealloc(x); }); The compiler will create the delegate for you and store x in the heap in the first place. In scopeThis() you just add the passed delegate to an array of other delegates, then in destructor call them. Seems rather trivial, I don't see a need for a language feature that affects all the classes and objects.
If it is trivial, can you write up a quick solution. I don't see how to accomplish it easily. If ScopeThis({dealloc(x);}); copies locals to the heap, then that solves part 1 trivially. But a bit of CT reflection is required to store the delegates in the class and I don't see it as being trivial... although it very well might be.
Mar 15
parent thedeemon <dlang thedeemon.com> writes:
On Wednesday, 15 March 2017 at 12:56:10 UTC, Inquie wrote:
 If it is trivial, can you write up a quick solution. I don't 
 see how to accomplish it easily.
Here it is, source and output: https://dpaste.dzfl.pl/bbf162529c6c
Mar 15
prev sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Tuesday, 14 March 2017 at 14:35:11 UTC, Inquie wrote:
 On Tuesday, 14 March 2017 at 05:33:28 UTC, thedeemon wrote:
 [...]
Complexity is in the eye of the beholder. Children think many things are complex when they are not. If a library solution could be created that is as seamless as a language solution, then I guess it would work. The downside of a library solution is uniformity of syntax and added verbosity. There is really no any arrays to keep track of or anything like that matter you stated. It requires creating a delegate to wrap the scope block and a copy of the variable to one on the heap. The GC uses arrays and that happens regardless. No reason for the compiler to create a new array. 3 steps: 1. Compiler copies local variables to heap(the "closure" part, which actually means it is not closing anything as a normal delegate would require). 2. The compiler creates a delegate. No big deal, does this in many places. 3. The compiler calls all the delegates on destruction. The only new part. But not difficult. Create a ScopeThis(...) and adds no extra overhead would be nice but I see that as being more complex. How can we determine what are variables that need to be copied to the heap? How can we hook in to the ~this? (can't have multiple ones, can we?) If you can come up with a working ScopeThis that doesn't have any more overhead than a language version, I'd be all for it, I don't know or see how it could be done.
       [...]
Must determine that x is a variable(hard?) and copy it to the heap(easy). Must create access to any local functions used(e.g., if dealloc is local). Then must hook in to ~this to execute the code. It would be nicer to not have to use a string but it would work easy since we could use a mixin and modify the string easily once we could parse it.
I was not sure to answer yesterday because the conversation turned on the COM thing. I wanted to say that you can use a mixin template because they can introduce destructors that are called automatically with the aggregate destrcutor mixin template Foo(T) { T x; void foo() { alloc(x); } ~this() // auto-called by the target aggregate { dealloc(x); } } class bar { mixin Foo!Stuff; }
Mar 15
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 03/15/2017 03:23 AM, Basile B. wrote:

 you can use a mixin template because
 they can introduce destructors that are called automatically with the
 aggregate destrcutor

 mixin template Foo(T)
 {
     T x;
     void foo()
     {
         alloc(x);
     }
     ~this() // auto-called by the target aggregate
     {
         dealloc(x);
     }
 }

 class bar
 {
     mixin Foo!Stuff;
 }
Wow! Is this specified anywhere or have you come across this by chance? :) Ali
Mar 15
next sibling parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Wed, Mar 15, 2017 at 12:34:32PM -0700, Ali «ehreli via Digitalmars-d-learn
wrote:
 On 03/15/2017 03:23 AM, Basile B. wrote:
 
 you can use a mixin template because they can introduce destructors
 that are called automatically with the aggregate destrcutor

 mixin template Foo(T)
 {
     T x;
     void foo()
     {
         alloc(x);
     }
     ~this() // auto-called by the target aggregate
     {
         dealloc(x);
     }
 }

 class bar
 {
     mixin Foo!Stuff;
 }
Wow! Is this specified anywhere or have you come across this by chance? :)
[...] Whoa. This is a really bizarre unknown (AFAICT) bit of the language (or at least, dmd's implementation thereof). I just did a bit of local testing, and found that while dmd happily chains mixin template dtors into an aggregate, it behaves erratically if the mixin template declares this(). It works if there's only a single this() declared either in the class itself, or a single mixin template. But if the class declares this() and the (single) mixin template also declares this(), then the mixin template's version is ignored and the class-declared this() is run upon construction. However, if there are two mixin templates both declaring this(), the compiler dies with an ICE. :-D T -- It is widely believed that reinventing the wheel is a waste of time; but I disagree: without wheel reinventers, we would be still be stuck with wooden horse-cart wheels.
Mar 15
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 15 March 2017 at 20:36:27 UTC, H. S. Teoh wrote:
 But if the class declares this()
 and the (single) mixin template also declares this(), then the 
 mixin
 template's version is ignored and the class-declared this() is 
 run upon
 construction.
That's normal mixin template behavior: if the name of a mixed in object already exists in the instantiation scope, the existing one is preferred. This allows you to "override" mixin functions and is quite useful. You can also bring in the others via alias. See my tip here for a related factoid: http://arsdnet.net/this-week-in-d/2016-feb-07.html That's what I *thought* was going on with the destructor too... but I confirmed both do in fact exist and seem to be merged by the compiler. I knew it did that with static dtors, but the spec says "a class can have only one destructor and it is always virtual" so idk what's going on. Bug, perhaps, but I now am inclined to believe it is a hidden feature. Important note though: such behavior is already possible. Struct dtors of members are called when the class dtor runs too...
  However, if there are two mixin templates both declaring
 this(), the compiler dies with an ICE. :-D
eek.
Mar 15
parent "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Wed, Mar 15, 2017 at 08:55:34PM +0000, Adam D. Ruppe via Digitalmars-d-learn
wrote:
[...]
  However, if there are two mixin templates both declaring
 this(), the compiler dies with an ICE. :-D
eek.
Filed a bug for it: https://issues.dlang.org/show_bug.cgi?id=17259 T -- Meat: euphemism for dead animal. -- Flora
Mar 15
prev sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Wednesday, 15 March 2017 at 19:34:32 UTC, Ali Çehreli wrote:
 On 03/15/2017 03:23 AM, Basile B. wrote:

 you can use a mixin template because
 they can introduce destructors that are called automatically
with the
 aggregate destrcutor
Wow! Is this specified anywhere or have you come across this by chance? :) Ali
It's not specified. Found it while trying to understand the difference between __dtor and __xdtor. But this is clearly designed to work this way. Not really an hidden gem.
Mar 16
parent Basile B. <b2.temp gmx.com> writes:
On Thursday, 16 March 2017 at 07:49:19 UTC, Basile B. wrote:
 On Wednesday, 15 March 2017 at 19:34:32 UTC, Ali Çehreli wrote:
 On 03/15/2017 03:23 AM, Basile B. wrote:

 you can use a mixin template because
 they can introduce destructors that are called automatically
with the
 aggregate destrcutor
Wow! Is this specified anywhere or have you come across this by chance? :) Ali
It's not specified. Found it while trying to understand the difference between __dtor and __xdtor.
(which is __dtor doesn't call the other destructors in the aggregate, xdtor does)
Mar 16