www.digitalmars.com         C & C++   DMDScript  

D - RAII, take 3

reply "Walter" <walter digitalmars.com> writes:
This is an ongoing great discussion here, so I'm ready for a new strawman
proposal!

First of all, while structs with destructors will work, they don't generate
much enthusiasm. Making struct wrappers for class references is a lot of
typing, and kludgy looking. It's hard to credibly explain why structs have
destructors and not constructors. It just isn't good enough.

Auto classes have problems - needing two versions, one with auto and one
without. Auto storage class doesn't fit well with classes designed to be
used solely with auto.

So how about this:

1) Support an auto class characteristic, as in:
    auto class Foo { ... }

2) Support an auto storage class, as in:
    int foo()
    {    auto Bar b;
    }

An auto class:

a) always gets auto storage class, whether or not it is explicitly listed as
auto.
b) sets a class to be auto and all classes derived from it
c) cannot have its reference copied, passed as a function parameter,
returned from a function,
or the target of an inout or out parameter.
d) cannot be a member of a struct or class, and cannot be statically
allocated

An auto storage class for an ordinary non-auto class:

a) the reference can be copied, passed, assigned, and reassigned like a
non-auto reference; it is
up to the programmer to not mess it up

In addition, for auto references, when they go out of scope, the ~this()
function is called. However, a delete is not done. This means that the
memory and the object remain instantiated. Hence, it is possible that the
destructor may get run twice, so it should be written to do that safely:

    class File
    {
        FILE *fp;
        ~this()
        {    if (fp)
            { fclose(fp);
               fp = NULL;
            }
        }
    }

This should make most dangling references clean up harmlessly.
Sep 06 2002
next sibling parent reply Pavel Minayev <evilone omen.ru> writes:
Walter wrote:

 So how about this:
 
 1) Support an auto class characteristic, as in:
     auto class Foo { ... }
 
 2) Support an auto storage class, as in:
     int foo()
     {    auto Bar b;
     }

Great!
 An auto class:
 
 a) always gets auto storage class, whether or not it is explicitly listed as
 auto.

I would prefer it to require "auto" (and stop with an error if it isn't there), but it isn't really important... I would be happy in any case =)
 c) cannot have its reference copied, passed as a function parameter,
 returned from a function,
 or the target of an inout or out parameter.

This one I don't like. I understand the reasons, but it still sounds too limiting... a proposal: allow to pass it to functions, and store it in local variables, but not in static, globals and members. This should make sure that no dangling reference ever exists for an auto class object.
 d) cannot be a member of a struct or class, and cannot be statically
 allocated

Ehm... okay.
 An auto storage class for an ordinary non-auto class:
 
 a) the reference can be copied, passed, assigned, and reassigned like a
 non-auto reference; it is
 up to the programmer to not mess it up

YES!!!
 In addition, for auto references, when they go out of scope, the ~this()
 function is called. However, a delete is not done. This means that the
 memory and the object remain instantiated. Hence, it is possible that the
 destructor may get run twice, so it should be written to do that safely:

Maybe let the compiler track this? Something like a hidden flag field in Object, checked before the destructor is run, and cleared afterwards? I think destructor running two times is just too weird for most C++ programmers, including me. It could be a source of many, many bugs!
Sep 07 2002
parent "Walter" <walter digitalmars.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:ald4ql$1get$1 digitaldaemon.com...
 c) cannot have its reference copied, passed as a function parameter,
 returned from a function,
 or the target of an inout or out parameter.

This one I don't like. I understand the reasons, but it still sounds too limiting... a proposal: allow to pass it to functions, and store it in local variables, but not in static, globals and members. This should make sure that no dangling reference ever exists for an auto class object.

I think you're right.
Sep 08 2002
prev sibling next sibling parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
I can see huge benefits of keeping auto instances as class members.  I know
you want to keep D simple but without that capability you really have only
halfway implemented RAII.

When an auto instance goes out of scope, after calling the destructor, it
can notify the GC that even though it still needs to manage the memory
involved, it should not call the destructor.  Just treat it as raw memory
from then on.

Sean

"Walter" <walter digitalmars.com> wrote in message
news:alb8e9$1o1u$1 digitaldaemon.com...
 This is an ongoing great discussion here, so I'm ready for a new strawman
 proposal!

 First of all, while structs with destructors will work, they don't

 much enthusiasm. Making struct wrappers for class references is a lot of
 typing, and kludgy looking. It's hard to credibly explain why structs have
 destructors and not constructors. It just isn't good enough.

 Auto classes have problems - needing two versions, one with auto and one
 without. Auto storage class doesn't fit well with classes designed to be
 used solely with auto.

 So how about this:

 1) Support an auto class characteristic, as in:
     auto class Foo { ... }

 2) Support an auto storage class, as in:
     int foo()
     {    auto Bar b;
     }

 An auto class:

 a) always gets auto storage class, whether or not it is explicitly listed

 auto.
 b) sets a class to be auto and all classes derived from it
 c) cannot have its reference copied, passed as a function parameter,
 returned from a function,
 or the target of an inout or out parameter.
 d) cannot be a member of a struct or class, and cannot be statically
 allocated

 An auto storage class for an ordinary non-auto class:

 a) the reference can be copied, passed, assigned, and reassigned like a
 non-auto reference; it is
 up to the programmer to not mess it up

 In addition, for auto references, when they go out of scope, the ~this()
 function is called. However, a delete is not done. This means that the
 memory and the object remain instantiated. Hence, it is possible that the
 destructor may get run twice, so it should be written to do that safely:

     class File
     {
         FILE *fp;
         ~this()
         {    if (fp)
             { fclose(fp);
                fp = NULL;
             }
         }
     }

 This should make most dangling references clean up harmlessly.

Sep 08 2002
parent "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:algc3h$2jli$1 digitaldaemon.com...
 I can see huge benefits of keeping auto instances as class members.  I

 you want to keep D simple but without that capability you really have only
 halfway implemented RAII.

Perhaps, but I bet it is more like 95% <g>. But if it becomes obvious that this is a critical shortcoming, it can be added later without wrecking any existing code.
 When an auto instance goes out of scope, after calling the destructor, it
 can notify the GC that even though it still needs to manage the memory
 involved, it should not call the destructor.  Just treat it as raw memory
 from then on.

My intent with that is so that dangling references have a chance to fail gracefully rather than disastrously.
Sep 08 2002
prev sibling next sibling parent reply "Sandor Hojtsy" <hojtsy index.hu> writes:
"Walter" <walter digitalmars.com> wrote in message
news:alb8e9$1o1u$1 digitaldaemon.com...
 This is an ongoing great discussion here, so I'm ready for a new strawman
 proposal!

 First of all, while structs with destructors will work, they don't

 much enthusiasm. Making struct wrappers for class references is a lot of
 typing, and kludgy looking. It's hard to credibly explain why structs have
 destructors and not constructors. It just isn't good enough.

Yes. Not for raii. But struct constructors and destructors are good on their own right.
 Auto classes have problems - needing two versions, one with auto and one
 without. Auto storage class doesn't fit well with classes designed to be
 used solely with auto.

 So how about this:

 1) Support an auto class characteristic, as in:
     auto class Foo { ... }

 2) Support an auto storage class, as in:
     int foo()
     {    auto Bar b;
     }

 An auto class:

 a) always gets auto storage class, whether or not it is explicitly listed

 auto.
 b) sets a class to be auto and all classes derived from it
 c) cannot have its reference copied, passed as a function parameter,
 returned from a function,

What about calling member functions of an auto class, and messing with the "this"?
 or the target of an inout or out parameter.
 d) cannot be a member of a struct or class, and cannot be statically
 allocated

Theoretically yes. But too restrictive for any real case. Most uses of raii classes need non-destructing references to them. So this feature would be used in very few cases.
 An auto storage class for an ordinary non-auto class:

 a) the reference can be copied, passed, assigned, and reassigned like a
 non-auto reference; it is
 up to the programmer to not mess it up

The idea you suggested the first time. I would like to get some help from the compiler here. As I have already told: - Assignment to an auto reference should dispose the old object (compiler can build in this call) - Assignment from an auto to an other auto, should set the source auto reference to null. Both seems feasible.
 In addition, for auto references, when they go out of scope, the ~this()
 function is called. However, a delete is not done. This means that the
 memory and the object remain instantiated. Hence, it is possible that the
 destructor may get run twice, so it should be written to do that safely:

     class File
     {
         FILE *fp;
         ~this()
         {    if (fp)
             { fclose(fp);
                fp = NULL;
             }
         }
     }

Since it it not a real destructor (which absolutely cannot be called twice), I don't think we should call it ~this(). Why don't do this in the finalyze(), or create a new method: dispose() ?
 This should make most dangling references clean up harmlessly.

Sep 09 2002
parent "Walter" <walter digitalmars.com> writes:
I think Pavel's rationale for allowing auto classes to be passed as function
parameters should resolve most of the issues here. -Walter

"Sandor Hojtsy" <hojtsy index.hu> wrote in message
news:alhl23$2mmq$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:alb8e9$1o1u$1 digitaldaemon.com...
 This is an ongoing great discussion here, so I'm ready for a new


 proposal!

 First of all, while structs with destructors will work, they don't

 much enthusiasm. Making struct wrappers for class references is a lot of
 typing, and kludgy looking. It's hard to credibly explain why structs


 destructors and not constructors. It just isn't good enough.

Yes. Not for raii. But struct constructors and destructors are good on

 own right.

 Auto classes have problems - needing two versions, one with auto and one
 without. Auto storage class doesn't fit well with classes designed to be
 used solely with auto.

 So how about this:

 1) Support an auto class characteristic, as in:
     auto class Foo { ... }

 2) Support an auto storage class, as in:
     int foo()
     {    auto Bar b;
     }

 An auto class:

 a) always gets auto storage class, whether or not it is explicitly


 as
 auto.
 b) sets a class to be auto and all classes derived from it
 c) cannot have its reference copied, passed as a function parameter,
 returned from a function,

What about calling member functions of an auto class, and messing with the "this"?
 or the target of an inout or out parameter.
 d) cannot be a member of a struct or class, and cannot be statically
 allocated

Theoretically yes. But too restrictive for any real case. Most uses of raii classes need non-destructing references to them. So this feature would be used in very few cases.
 An auto storage class for an ordinary non-auto class:

 a) the reference can be copied, passed, assigned, and reassigned like a
 non-auto reference; it is
 up to the programmer to not mess it up

The idea you suggested the first time. I would like to get some help from the compiler here. As I have already told: - Assignment to an auto reference should dispose the old object (compiler can build in this call) - Assignment from an auto to an other auto, should set the source auto reference to null. Both seems feasible.
 In addition, for auto references, when they go out of scope, the ~this()
 function is called. However, a delete is not done. This means that the
 memory and the object remain instantiated. Hence, it is possible that


 destructor may get run twice, so it should be written to do that safely:

     class File
     {
         FILE *fp;
         ~this()
         {    if (fp)
             { fclose(fp);
                fp = NULL;
             }
         }
     }

Since it it not a real destructor (which absolutely cannot be called

 I don't think we should call it ~this().
 Why don't do this in the finalyze(), or create a new method: dispose() ?

 This should make most dangling references clean up harmlessly.


Sep 09 2002
prev sibling next sibling parent Patrick Down <pat codemoon.com> writes:
I like this proposal.  I think it goes far enough
at this time.
Sep 09 2002
prev sibling parent Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
Perhaps we should be able to pass auto references (and references to 
auto classes) to functions, but only if the function reference is also 
'auto':
     void func(auto MyClass foo) {...};

auto parameters are like auto references (can't be modified, can't be 
passed as a argument, except as an auto argument), except that it is NOT 
cleaned up when the function exits.  The semantics mean more like 
"safe-for-auto" than really "auto".

You could even return an auto reference (or have it as an out parameter) 
if the calling function was required to either
   1) Save the return to an auto reference
     or
   2) Immediately (and implicitly) finalize the object if it DOESN'T 
store the reference.




I also vote with Pavel's mantra...'auto' classes should require the 
'auto' keyword on their instance declarations, as well...
Sep 09 2002