www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Behavior of "auto"

reply NullTerminator <NullTerminator Hotmail.com> writes:
== Repost the article of NullTerminator (NullTerminator Hotmail.com)
== Posted at 2007/12/06 01:07 to D

A strange effect of using auto:

The following code:
import std.stdio;

class Test {
   this() {
      printf("Created\n");
   }

   ~this() {
      printf("Destroyed\n");
   }
}

int main(char[][] args){
   for (int n = 0; n < 10; n++)
      Test t = new Test();
   return 0;
}

produces the following output:
Created
Created
Created
Created
Created
Created
Created
Created
Created
Created
Destroyed
Destroyed
Destroyed
Destroyed
Destroyed
Destroyed
Destroyed
Destroyed
Destroyed
Destroyed

where as changing the line:
      Test t = new Test();
to:
      auto Test t = new Test();
produces:
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed

As I understand it, one would expect this to be the effect of "scope," not
"auto."  Can someone explain why this happens?

Thanks in advance.
Dec 05 2007
next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Thu, 6 Dec 2007 06:20:53 +0000 (UTC), NullTerminator wrote:

 == Repost the article of NullTerminator (NullTerminator Hotmail.com)
 == Posted at 2007/12/06 01:07 to D
 
 A strange effect of using auto:
 
 The following code:
 import std.stdio;
 
 class Test {
    this() {
       printf("Created\n");
    }
 
    ~this() {
       printf("Destroyed\n");
    }
 }
 
 int main(char[][] args){
    for (int n = 0; n < 10; n++)
       Test t = new Test();
    return 0;
 }
 
 produces the following output:
 Created
 Created
 Created
 Created
 Created
 Created
 Created
 Created
 Created
 Created
 Destroyed
 Destroyed
 Destroyed
 Destroyed
 Destroyed
 Destroyed
 Destroyed
 Destroyed
 Destroyed
 Destroyed
 
 where as changing the line:
       Test t = new Test();
 to:
       auto Test t = new Test();
 produces:
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 
 As I understand it, one would expect this to be the effect of "scope," not
 "auto."  Can someone explain why this happens?
Both 'scope' and 'auto' are poorly documented. However, the variable declared with an 'auto' is scoped to the block it is declared in and is destroyed when going out of scope. In effect 'auto' is shorthand for 'scope <type>' ... That is to say ... auto t = new Test(); is equivalent to ... scope Test t = new Test(); -- Derek (skype: derek.j.parnell) Melbourne, Australia 6/12/2007 5:51:16 PM
Dec 05 2007
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Derek Parnell wrote:
 On Thu, 6 Dec 2007 06:20:53 +0000 (UTC), NullTerminator wrote:
 
 == Repost the article of NullTerminator (NullTerminator Hotmail.com)
 == Posted at 2007/12/06 01:07 to D

 A strange effect of using auto:

 The following code:
 import std.stdio;

 class Test {
    this() {
       printf("Created\n");
    }

    ~this() {
       printf("Destroyed\n");
    }
 }

 int main(char[][] args){
    for (int n = 0; n < 10; n++)
       Test t = new Test();
    return 0;
 }

 produces the following output:
 Created
 Created
 Created
 Created
 Created
 Created
 Created
 Created
 Created
 Created
 Destroyed
 Destroyed
 Destroyed
 Destroyed
 Destroyed
 Destroyed
 Destroyed
 Destroyed
 Destroyed
 Destroyed

 where as changing the line:
       Test t = new Test();
 to:
       auto Test t = new Test();
 produces:
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed
 Created
 Destroyed

 As I understand it, one would expect this to be the effect of "scope," not
 "auto."  Can someone explain why this happens?
Both 'scope' and 'auto' are poorly documented. However, the variable declared with an 'auto' is scoped to the block it is declared in and is destroyed when going out of scope. In effect 'auto' is shorthand for 'scope <type>' ... That is to say ... auto t = new Test(); is equivalent to ... scope Test t = new Test();
No no no nononono! auto Test t = new Test(); is equivalent to scope Test t = new Test(); but auto t = new Test(); is a regular local (aka "automatic") variable who's type is deduced because none is explicitly specified. The reason "auto Test t" compiles at all is because that was the syntax for scoped allocation before everyone and their dog complained about how confusing and misleading it was. Walter changed it to 'scope' but unfortunately the old syntax still works for some reason. It really should only work with -d set. --bb
Dec 05 2007
prev sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Derek Parnell wrote:
 On Thu, 6 Dec 2007 06:20:53 +0000 (UTC), NullTerminator wrote:
 
 
== Repost the article of NullTerminator (NullTerminator Hotmail.com)
== Posted at 2007/12/06 01:07 to D

A strange effect of using auto:

The following code:
import std.stdio;

class Test {
   this() {
      printf("Created\n");
   }

   ~this() {
      printf("Destroyed\n");
   }
}

int main(char[][] args){
   for (int n = 0; n < 10; n++)
      Test t = new Test();
   return 0;
}

produces the following output:
Created
Created
Created
Created
Created
Created
Created
Created
Created
Created
Destroyed
Destroyed
Destroyed
Destroyed
Destroyed
Destroyed
Destroyed
Destroyed
Destroyed
Destroyed

where as changing the line:
      Test t = new Test();
to:
      auto Test t = new Test();
produces:
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed
Created
Destroyed

As I understand it, one would expect this to be the effect of "scope," not
"auto."  Can someone explain why this happens?
Both 'scope' and 'auto' are poorly documented. However, the variable declared with an 'auto' is scoped to the block it is declared in and is destroyed when going out of scope. In effect 'auto' is shorthand for 'scope <type>' ... That is to say ... auto t = new Test(); is equivalent to ... scope Test t = new Test();
This is not true. The situation is more complex. Prior to DMD 0.174, 'auto' was used for both type inference and scoped destruction. That is to say, this: auto obj = new C; // infer the type of 'obj' Was different than this: auto C obj = new C; // destroy 'obj' at the end of scope This behavior was considered confusing enough that the 'scope' keyword (which was already in the language for scope guards) was allowed in place of auto in the latter case: scope C obj = new C; // destroy 'obj' at the end of scope The use of 'auto' to mean scoped destruction was promptly forgotten as fast as people could. However, as the original poster discovered, this meaning is still allowed. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Dec 05 2007
parent NullTerminator <NullTerminator Hotmail.com> writes:
Thanks to you and Bill Baxter from post 63092.  That makes a lot more sense now.
Dec 06 2007
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
To experiment I have modified your code some (this tread probably belongs to
D.learn):

import std.gc;

class Test {
    int i;
    this(int i) {
        this.i = i;
        printf("%d created\n", this.i); }
    ~this() { printf("%d destroyed\n", this.i); }
}

void main() {
    printf("Part 1 ----------\n");
    for (int i = 0; i < 5; i++)
        auto t = new Test(i);
    printf("Part1 end ----------\n\n");

    printf("Part 2 ----------\n");
    for (int i = 0; i < 5; i++)
        auto scope t = new Test(i+5);
    printf("Part 2 end ----------\n\n");

    printf("Part3 ----------\n");
    foreach(i, _; new byte[5])
        auto scope t = new Test(i+10);
    printf("Part 3 end ----------\n\n");

    printf("Part4 ----------\n");
    foreach(i, _; new byte[5])
        auto t = new Test(i+15);
    printf("Part 4 end ----------\n\n");

    std.gc.fullCollect();
}


And now I too don't fully understand the results (DMD v1.024):

Part 1 ----------
0 created
1 created
2 created
3 created
4 created
Part1 end ----------

Part 2 ----------
5 created
5 destroyed
6 created
6 destroyed
7 created
7 destroyed
8 created
8 destroyed
9 created
9 destroyed
Part 2 end ----------

Part3 ----------
10 created
11 created
12 created
13 created
14 created
Part 3 end ----------

Part4 ----------
15 created
16 created
17 created
18 created
19 created
Part 4 end ----------

18 destroyed
17 destroyed
16 destroyed
15 destroyed
3 destroyed
2 destroyed
1 destroyed
0 destroyed
19 destroyed
4 destroyed

I don't see where the 10-14 objects are deallocated (I have added a
fullCollect, but it just scrambles the order of the those last deallocations).

Bye,
bearophile
Dec 06 2007
parent Sean Kelly <sean f4.ca> writes:
bearophile wrote:
 To experiment I have modified your code some (this tread probably belongs to
D.learn):
 
 import std.gc;
 
 class Test {
     int i;
     this(int i) {
         this.i = i;
         printf("%d created\n", this.i); }
     ~this() { printf("%d destroyed\n", this.i); }
 }
 
 void main() {
     printf("Part 1 ----------\n");
     for (int i = 0; i < 5; i++)
         auto t = new Test(i);
     printf("Part1 end ----------\n\n");
 
     printf("Part 2 ----------\n");
     for (int i = 0; i < 5; i++)
         auto scope t = new Test(i+5);
     printf("Part 2 end ----------\n\n");
 
     printf("Part3 ----------\n");
     foreach(i, _; new byte[5])
         auto scope t = new Test(i+10);
     printf("Part 3 end ----------\n\n");
 
     printf("Part4 ----------\n");
     foreach(i, _; new byte[5])
         auto t = new Test(i+15);
     printf("Part 4 end ----------\n\n");
 
     std.gc.fullCollect();
 }
...
 
 Part3 ----------
 10 created
 11 created
 12 created
 13 created
 14 created
 Part 3 end ----------
...
 
 I don't see where the 10-14 objects are deallocated (I have added a
fullCollect, but it just scrambles the order of the those last deallocations).
I'd guess that this is a bug, and is related to the extra work the compiler does for foreach loops. Sean
Dec 06 2007
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Sean Kelly:
 I'd guess that this is a bug, and is related to the extra work the 
 compiler does for foreach loops.
This is a reduced and simplified version that shows the same thing: class Foo { ~this() { printf("Destroyed\n"); } } void main() { foreach(i; new int[1]) scope Foo f = new Foo(); } Bye, bearophile
Dec 07 2007
parent Sean Kelly <sean f4.ca> writes:
bearophile wrote:
 Sean Kelly:
 I'd guess that this is a bug, and is related to the extra work the 
 compiler does for foreach loops.
This is a reduced and simplified version that shows the same thing: class Foo { ~this() { printf("Destroyed\n"); } } void main() { foreach(i; new int[1]) scope Foo f = new Foo(); }
Please submit a ticket for it if you haven't already :-) Sean
Dec 07 2007