Thread overview
RAII, take 3
Sep 06, 2002
Walter
Sep 07, 2002
Pavel Minayev
Sep 08, 2002
Walter
Sep 08, 2002
Sean L. Palmer
Sep 08, 2002
Walter
Sep 09, 2002
Sandor Hojtsy
Sep 09, 2002
Walter
Sep 09, 2002
Patrick Down
Sep 09, 2002
Russell Lewis
September 06, 2002
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.


September 07, 2002
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!



September 08, 2002
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
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.
>
>


September 08, 2002
"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
know
> 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.


September 08, 2002
"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.


September 09, 2002
"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
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.

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
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 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.



September 09, 2002
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
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.
>
> 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
> 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
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.
>
>
>


September 09, 2002
I like this proposal.  I think it goes far enough
at this time.
September 09, 2002
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...