www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Struct d'tors and destructive assignment of return vals

reply dsimcha <dsimcha yahoo.com> writes:
import std.stdio;

struct RC {
    uint N;

    this(this) {
        writeln("Postblit:  ", N);
    }

    ~this() {
        writeln("D'tor:  ", N);
    }
}

RC fun() {
    writeln("Doing stuff...");
    return RC(3);
}


void main() {
    RC foo = RC(1);
    writeln("Calling fun()...");
    foo = fun();
    writeln("Exiting...");
}

Output:

Calling fun()...
Doing stuff...
D'tor:  1
Exiting...
D'tor:  3

Would it be feasible to require that, when a struct is being destructively
assigned the return value of a function, the d'tor is called for the old
contents before the function that provides the return value is called instead
of calling it after?  This would be useful, for example, for providing COW
semantics when dealing with ranges whose elements are lazily constructed arrays:

struct SomeRange {
    T[] someArray;
    uint* nExternalReferences;  // to someArray.

    T[] popNext() {
        if(*nExternalReferences > 0) {
            someArray = someArray.dup;
            nExternalReferences = new uint;
        }

        // Modify someArray.
        return referenceCounted(someArray, nExternalReferences);
    }
}

Caller's end:

SomeRange s;
RefCounted r;
while(!s.empty) {
    // The underlying array will constantly be dup'd because
    // the d'tor for r is not being called until after popNext()
    // is called.
    r = s.popNext;
}
May 26 2009
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 26 May 2009 21:20:41 -0400, dsimcha <dsimcha yahoo.com> wrote:

 import std.stdio;

 struct RC {
     uint N;

     this(this) {
         writeln("Postblit:  ", N);
     }

     ~this() {
         writeln("D'tor:  ", N);
     }
 }

 RC fun() {
     writeln("Doing stuff...");
     return RC(3);
 }


 void main() {
     RC foo = RC(1);
     writeln("Calling fun()...");
     foo = fun();
     writeln("Exiting...");
 }

 Output:

 Calling fun()...
 Doing stuff...
 D'tor:  1
 Exiting...
 D'tor:  3

 Would it be feasible to require that, when a struct is being  
 destructively
 assigned the return value of a function, the d'tor is called for the old
 contents before the function that provides the return value is called  
 instead
 of calling it after?
What if fun throws an exception? -Steve
May 26 2009
parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Steven Schveighoffer (schveiguy yahoo.com)'s article
 On Tue, 26 May 2009 21:20:41 -0400, dsimcha <dsimcha yahoo.com> wrote:
 import std.stdio;

 struct RC {
     uint N;

     this(this) {
         writeln("Postblit:  ", N);
     }

     ~this() {
         writeln("D'tor:  ", N);
     }
 }

 RC fun() {
     writeln("Doing stuff...");
     return RC(3);
 }


 void main() {
     RC foo = RC(1);
     writeln("Calling fun()...");
     foo = fun();
     writeln("Exiting...");
 }

 Output:

 Calling fun()...
 Doing stuff...
 D'tor:  1
 Exiting...
 D'tor:  3

 Would it be feasible to require that, when a struct is being
 destructively
 assigned the return value of a function, the d'tor is called for the old
 contents before the function that provides the return value is called
 instead
 of calling it after?
What if fun throws an exception? -Steve
Argh good point, didn't think of that. Then I guess you're just screwed. In that case, do you see any other way to get good COW semantics in this situation?
May 26 2009
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 26 May 2009 21:44:51 -0400, dsimcha <dsimcha yahoo.com> wrote:

 == Quote from Steven Schveighoffer (schveiguy yahoo.com)'s article
 What if fun throws an exception?
 -Steve
Argh good point, didn't think of that. Then I guess you're just screwed. In that case, do you see any other way to get good COW semantics in this situation?
opAssign maybe? not sure... -Steve
May 26 2009
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
dsimcha wrote:
 == Quote from Steven Schveighoffer (schveiguy yahoo.com)'s article
 On Tue, 26 May 2009 21:20:41 -0400, dsimcha <dsimcha yahoo.com> wrote:
 import std.stdio;

 struct RC {
     uint N;

     this(this) {
         writeln("Postblit:  ", N);
     }

     ~this() {
         writeln("D'tor:  ", N);
     }
 }

 RC fun() {
     writeln("Doing stuff...");
     return RC(3);
 }


 void main() {
     RC foo = RC(1);
     writeln("Calling fun()...");
     foo = fun();
     writeln("Exiting...");
 }

 Output:

 Calling fun()...
 Doing stuff...
 D'tor:  1
 Exiting...
 D'tor:  3

 Would it be feasible to require that, when a struct is being
 destructively
 assigned the return value of a function, the d'tor is called for the old
 contents before the function that provides the return value is called
 instead
 of calling it after?
What if fun throws an exception? -Steve
Argh good point, didn't think of that. Then I guess you're just screwed. In that case, do you see any other way to get good COW semantics in this situation?
This has been discussed by Bartosz, Walter, and myself quite a lot a couple of years ago. The solution we settled on was to always move the bits directly when a copy is the last access of the source (as is the case in your return expression). Walter hasn't implemented that yet. Andrei
May 26 2009