www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Question about RAII.

reply "Peter C. Chapin" <pchapin sover.net> writes:
Hello! I'm a C++ programmer who has started to look at D. I appreciate 
many of D's design decisions. However, I'm a little confused about how 
RAII is supported. It seems like it only semi-works. Consider the 
function below ('Example' is some class):

Example g()
{
  auto Example object1 = new Example;
  auto Example object2 = new Example;
  Example object3;

  if (f()) {
    object3 = object1;
  }
  else {
    object3 = object2;
  }
  return object3;
}

The idea is that I want to return one of two local objects depending on 
what f() returns. Assume f() interacts with the user so its return value 
can't be known to the compiler. The object that is not returned must be 
cleaned up. Assume that it holds resources other than memory that need 
to be released.

In the example above it looks like both object1 and object2 are 
destroyed and thus the returned reference is invalid. However, if I 
remove 'auto' from the local declarations than object1 and object2 don't 
get destroyed until garbage collection time... which is too late. It 
looks like I'm left with explicitly deleting the object that I don't 
return, but this is manual resource management and not RAII.

To make this example more concrete, suppose the class in question 
represents an on-screen window. Function g() lets the user select one of 
two newly created windows, returning the window selected and removing 
the other from the screen.

Thanks in advance for any comments.

Peter
Jul 04 2006
next sibling parent "Derek Parnell" <derek psych.ward> writes:
On Tue, 04 Jul 2006 23:05:58 +1000, Peter C. Chapin <pchapin sover.net>  
wrote:

 Hello! I'm a C++ programmer who has started to look at D. I appreciate
 many of D's design decisions. However, I'm a little confused about how
 RAII is supported. It seems like it only semi-works. Consider the
 function below ('Example' is some class):

The 'auto' in this context means "destroy the local member when it goes out of scope". This may or may not correlate to your RAII definition but that's what 'auto' means in D. It doesn't matter if the object is in use or not, if it goes out of scope it is destroyed. So in the situation you describe, you will be required to explictly delete the object you no longer want and do not use 'auto'. Of course, another method is to only create the object when you know which one to create. -- Derek Parnell Melbourne, Australia
Jul 04 2006
prev sibling next sibling parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Peter C. Chapin wrote:
 Hello! I'm a C++ programmer who has started to look at D. I appreciate 
 many of D's design decisions. However, I'm a little confused about how 
 RAII is supported. It seems like it only semi-works. Consider the 
 function below ('Example' is some class):
 
 Example g()
 {
   auto Example object1 = new Example;
   auto Example object2 = new Example;
   Example object3;
 
   if (f()) {
     object3 = object1;
   }
   else {
     object3 = object2;
   }
   return object3;
 }
 
 The idea is that I want to return one of two local objects depending on 
 what f() returns. Assume f() interacts with the user so its return value 
 can't be known to the compiler. The object that is not returned must be 
 cleaned up. Assume that it holds resources other than memory that need 
 to be released.
 
 In the example above it looks like both object1 and object2 are 
 destroyed and thus the returned reference is invalid. However, if I 
 remove 'auto' from the local declarations than object1 and object2 don't 
 get destroyed until garbage collection time... which is too late. It 
 looks like I'm left with explicitly deleting the object that I don't 
 return, but this is manual resource management and not RAII.
 
 To make this example more concrete, suppose the class in question 
 represents an on-screen window. Function g() lets the user select one of 
 two newly created windows, returning the window selected and removing 
 the other from the screen.
 
 Thanks in advance for any comments.
 
 Peter

An auto referance such as those above is guaranteed to be destroyed at the end of the scope in which it is declared. Returning such a referance is a no-no, as the object on the other side no longer exists when the function has ended! (If you had directly returned object1 or object2, you should have gotten a compile-time error I believe.) -- Chris Nicholson-Sauls
Jul 04 2006
prev sibling next sibling parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Peter C. Chapin skrev:
 Hello! I'm a C++ programmer who has started to look at D. I appreciate 
 many of D's design decisions. However, I'm a little confused about how 
 RAII is supported. It seems like it only semi-works. Consider the 
 function below ('Example' is some class):
 
 Example g()
 {
   auto Example object1 = new Example;
   auto Example object2 = new Example;
   Example object3;
 
   if (f()) {
     object3 = object1;
   }
   else {
     object3 = object2;
   }
   return object3;
 }

It looks like you want some kind of transfer semantics. You can do that manually: if (f()) { object3 = object1; object1 = null; } else { object3 = object2; object2 = null; } D lacks the necessary(*) provisions that would be needed to implement such behavior automatically. (like the c++ auto_ptr)
 In the example above it looks like both object1 and object2 are 
 destroyed and thus the returned reference is invalid. However, if I 
 remove 'auto' from the local declarations than object1 and object2 don't 
 get destroyed until garbage collection time... which is too late. It 
 looks like I'm left with explicitly deleting the object that I don't 
 return, but this is manual resource management and not RAII.

The both raii variables have no way of knowing if other live references to the same object exists. They are not cleared when assigned to any other reference holding variable or passed to functions etc. /Oskar *) The obvious implementations would be a pointer wrapping struct, but for this D lacks: - assignment operator (and possibly copy) overloading for structs - destructors for structs
Jul 04 2006
parent reply "Peter C. Chapin" <pchapin sover.net> writes:
Oskar Linde <oskar.lindeREM OVEgmail.com> wrote in
news:e8dqj5$29ns$1 digitaldaemon.com: 

 It looks like you want some kind of transfer semantics. You can do
 that manually:
 
   if (f()) {
      object3 = object1;
      object1 = null;
    }
    else {
      object3 = object2;
      object2 = null;
    }

That's a neat trick. Thanks. I can see that in many cases specifying which of the autos I don't want destroyed (in effect) would be easier than explicitly destroying everything else. Peter
Jul 04 2006
parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Tue, 4 Jul 2006 17:35:55 +0000 (UTC), Peter C. Chapin wrote:

 Oskar Linde <oskar.lindeREM OVEgmail.com> wrote in
 news:e8dqj5$29ns$1 digitaldaemon.com: 
 
 It looks like you want some kind of transfer semantics. You can do
 that manually:
 
   if (f()) {
      object3 = object1;
      object1 = null;
    }
    else {
      object3 = object2;
      object2 = null;
    }

That's a neat trick. Thanks. I can see that in many cases specifying which of the autos I don't want destroyed (in effect) would be easier than explicitly destroying everything else. Peter

Just checking, but doesn't setting an object reference to null just flag it as *available* for garbage collection, and isn't there no absolute guarantee that an object will actually be collected by the GC? In which case, you can't be certain that object1 or object2 will really have their destructor called. Whereas using the delete statement ensures the destructor call. So I think that the code should be more like ... MyClass object1 = new MyClass( stuff1 ); MyClass object2 = new MyClass( stuff2 ); MyClass object3; if (f()) { object3 = object1; delete object2; } else { object3 = object2; delete object1; } Though given this simplistic example I'd actually code it more like ... MyClass object3; if (f()) { object3 = new MyClass( stuff1 ); } else { object3 = new MyClass( stuff2 ); } -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocrity!" 5/07/2006 9:24:10 AM
Jul 04 2006
parent reply Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Derek Parnell wrote:
 On Tue, 4 Jul 2006 17:35:55 +0000 (UTC), Peter C. Chapin wrote:
 
 Oskar Linde <oskar.lindeREM OVEgmail.com> wrote in
 news:e8dqj5$29ns$1 digitaldaemon.com: 

 It looks like you want some kind of transfer semantics. You can do
 that manually:

   if (f()) {
      object3 = object1;
      object1 = null;
    }
    else {
      object3 = object2;
      object2 = null;
    }

of the autos I don't want destroyed (in effect) would be easier than explicitly destroying everything else. Peter

Just checking, but doesn't setting an object reference to null just flag it as *available* for garbage collection, and isn't there no absolute guarantee that an object will actually be collected by the GC? In which case, you can't be certain that object1 or object2 will really have their destructor called. Whereas using the delete statement ensures the destructor call.

I'm not sure if you're just asking that without regards to the previous code, you're if asking that because you may have misunderstood the previous code. The point of setting those objects to null is not to collect it (if it was, indeed we couldn't be sure when the GC would collect), but indeed the opposite (to *prevent* collection). Because they are auto objects, the only to way to prevent (auto) collection is to setting them as null. Your example does work as well, of course.
 So I think that the code should be more like ...
 
    MyClass object1 = new MyClass( stuff1 );
    MyClass object2 = new MyClass( stuff2 );
    MyClass object3;
 
    if (f()) {
       object3 = object1;
       delete object2;
     }
     else {
       object3 = object2;
       delete object1;
     }
 
 Though given this simplistic example I'd actually code it more like ...
 
    MyClass object3;
 
    if (f()) {
       object3 = new MyClass( stuff1 );
     }
     else {
       object3 = new MyClass( stuff2 );
     }
 

-- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 04 2006
parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Wed, 05 Jul 2006 01:36:54 +0100, Bruno Medeiros wrote:

 Derek Parnell wrote:
 
 I'm not sure if you're just asking that without regards to the previous 
 code, you're if asking that because you may have misunderstood the 
 previous code.

Yep. I did misunderstand. My mistake, but I can see the 'trick' now. Is this method endorsed by Walter or is this just an artifact of the RAII implementation? -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocrity!" 5/07/2006 10:43:07 AM
Jul 04 2006
next sibling parent Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Derek Parnell wrote:
 On Wed, 05 Jul 2006 01:36:54 +0100, Bruno Medeiros wrote:
 
 Derek Parnell wrote:

 I'm not sure if you're just asking that without regards to the previous 
 code, you're if asking that because you may have misunderstood the 
 previous code.

Yep. I did misunderstand. My mistake, but I can see the 'trick' now. Is this method endorsed by Walter or is this just an artifact of the RAII implementation?

Well, actually, now that I recall the spec, that behavior isn't valid: "Assignment to an auto, other than initialization, is not allowed." But since the auto mechanism is under consideration to have some changes, the future behavior might be different and allow something like this. Or not at all. For instance, if we have an auto that instead of being applied to variables, is applied to values, like this: auto Bar(); Foo foo = auto Foo(); then how would one cancel the Foo() destruction? A new mechanism would be required, but then it might become a little awkward. -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jul 05 2006
prev sibling parent Sean Kelly <sean f4.ca> writes:
Derek Parnell wrote:
 On Wed, 05 Jul 2006 01:36:54 +0100, Bruno Medeiros wrote:
 
 Derek Parnell wrote:

 I'm not sure if you're just asking that without regards to the previous 
 code, you're if asking that because you may have misunderstood the 
 previous code.

Yep. I did misunderstand. My mistake, but I can see the 'trick' now. Is this method endorsed by Walter or is this just an artifact of the RAII implementation?

The latter I think. Walter has said in the past that the compiler is free to allocate auto variables on the stack, it just isn't required. Sean
Jul 05 2006
prev sibling next sibling parent reply Robert.Atkinson NOSPAM.gmail.com.NOSPAM writes:
Everyone's already told you that returning an auto-reference is impossible as
the reference gets destroyed by exiting the scope.  

Couldn't the compiler detect this and throw an compile-error?  From simple
inspection it seems to be a pretty easy addition to the compiler, the syntax is
already quite clear.

In article <Xns97F65C90D676pchapinsovernet 63.105.9.61>, Peter C. Chapin says...
Hello! I'm a C++ programmer who has started to look at D. I appreciate 
many of D's design decisions. However, I'm a little confused about how 
RAII is supported. It seems like it only semi-works. Consider the 
function below ('Example' is some class):

Example g()
{
  auto Example object1 = new Example;
  auto Example object2 = new Example;
  Example object3;

  if (f()) {
    object3 = object1;
  }
  else {
    object3 = object2;
  }
  return object3;
}

The idea is that I want to return one of two local objects depending on 
what f() returns. Assume f() interacts with the user so its return value 
can't be known to the compiler. The object that is not returned must be 
cleaned up. Assume that it holds resources other than memory that need 
to be released.

In the example above it looks like both object1 and object2 are 
destroyed and thus the returned reference is invalid. However, if I 
remove 'auto' from the local declarations than object1 and object2 don't 
get destroyed until garbage collection time... which is too late. It 
looks like I'm left with explicitly deleting the object that I don't 
return, but this is manual resource management and not RAII.

To make this example more concrete, suppose the class in question 
represents an on-screen window. Function g() lets the user select one of 
two newly created windows, returning the window selected and removing 
the other from the screen.

Thanks in advance for any comments.

Peter

Jul 04 2006
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
<Robert.Atkinson NOSPAM.gmail.com.NOSPAM> wrote in message 
news:e8dtj3$2dc8$1 digitaldaemon.com...

 Couldn't the compiler detect this and throw an compile-error?  From simple
 inspection it seems to be a pretty easy addition to the compiler, the 
 syntax is
 already quite clear.

You'll notice the auto reference is being assigned to a non-auto reference, which makes it all but impossible to detect. What if it were stuck into an array? What if the auto ref were passed into a function and then returned as non-auto? What if..? If you try to directly return an auto reference, the compiler will gladly whine: class Foo{} Foo foo() { auto Foo f = new Foo; return f; } dtest.d(781): escaping reference to auto local f
Jul 04 2006
prev sibling next sibling parent reply Walter Bright <newshound digitalmars.com> writes:
Peter C. Chapin wrote:
 Hello! I'm a C++ programmer who has started to look at D. I appreciate 
 many of D's design decisions. However, I'm a little confused about how 
 RAII is supported. It seems like it only semi-works. Consider the 
 function below ('Example' is some class):
 
 Example g()
 {
   auto Example object1 = new Example;
   auto Example object2 = new Example;
   Example object3;
 
   if (f()) {
     object3 = object1;
   }
   else {
     object3 = object2;
   }
   return object3;
 }
 
 The idea is that I want to return one of two local objects depending on 
 what f() returns. Assume f() interacts with the user so its return value 
 can't be known to the compiler. The object that is not returned must be 
 cleaned up. Assume that it holds resources other than memory that need 
 to be released.
 
 In the example above it looks like both object1 and object2 are 
 destroyed and thus the returned reference is invalid. However, if I 
 remove 'auto' from the local declarations than object1 and object2 don't 
 get destroyed until garbage collection time... which is too late. It 
 looks like I'm left with explicitly deleting the object that I don't 
 return, but this is manual resource management and not RAII.
 
 To make this example more concrete, suppose the class in question 
 represents an on-screen window. Function g() lets the user select one of 
 two newly created windows, returning the window selected and removing 
 the other from the screen.
 
 Thanks in advance for any comments.

The reason the example "works" in C++ is not because RAII is any different in D - in D, as well as C++, both object1 and object2 get destroyed at the end of the return. What happens in C++ is the: object3 = object1; makes a *copy* of object1 via the default (or explicit) copy constructor. In D, just a reference to object1's instance is copied. You can make the D version behave like the C++ one as follows: Example g() { auto Example object1 = new Example; auto Example object2 = new Example; Example object3; if (f()) { object3 = new Example(object1); } else { object3 = new Example(object2); } return object3; } and then in the definition of Example, add the constructor: this(Example e) { ...copy members... } To explicitly run the destructor on an object, use: delete object1;
Jul 04 2006
parent "Peter C. Chapin" <pchapin sover.net> writes:
Walter Bright <newshound digitalmars.com> wrote in
news:e8f9b7$17u2$1 digitaldaemon.com: 

 You can make the D version behave like the C++ one as follows:
 
 Example g()
 {
    auto Example object1 = new Example;
    auto Example object2 = new Example;
    Example object3;
 
    if (f()) {
      object3 = new Example(object1);
    }
    else {
      object3 = new Example(object2);
    }
    return object3;
 }

I see what you are saying. Thanks, that is helpful. Peter
Jul 05 2006
prev sibling parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Peter C. Chapin wrote:
<snip>
 In the example above it looks like both object1 and object2 are 
 destroyed and thus the returned reference is invalid. However, if I 
 remove 'auto' from the local declarations than object1 and object2 don't 
 get destroyed until garbage collection time... which is too late. It 
 looks like I'm left with explicitly deleting the object that I don't 
 return, but this is manual resource management and not RAII.
 
 Peter

I always had the impression that RAII indeed *is* manual resource management.
Jul 05 2006