April 26, 2022

As long as the Voldemort type lives on, does int x, y legally live on?

auto getPoint()
{
  int x, y; // major issue

  enum type = "2D";
  struct Point(string Id)
  {
    static int _x = 1024;
    static int _y = 768;
    string toString() {
      import std.format;
      return format("Limit: %dx%d px.",
                               _x, _y);
    }
    void set(int a, int b) { x = a; y = b; }
    uint area() { return x * y; }
    auto get() { return this; }
  }
  auto px = Point!type();
  return px.get;
}

For test:

  auto p1 = getPoint();
  p1.set(2, 21);
  p1.area.writeln(" MP, ", p1);
  typeof(p1).stringof
  .writefln!"%s @%s"(&p1);

  p1.set(10, 100);
  p1.area.writeln(" MP, ", p1);


  auto p2 = getPoint();
  p2.area.writeln(" MP, ", p2);
  p2.set(3, 21);
  typeof(p2).stringof
  .writefln!"%s @%s"(&p2);

  p1.area.writeln(" MP, ", p1);
  p2.area.writeln(" MP, ", p2);

Outputs:

>

42 MP, Limit: 1024x768 px.
Point!"2D" @7FFD328DB770
1000 MP, Limit: 1024x768 px.
0 MP, Limit: 1024x768 px.
Point!"2D" @7FFD328DB788
1000 MP, Limit: 1024x768 px.
63 MP, Limit: 1024x768 px.

SDB@79

April 26, 2022
On Tue, Apr 26, 2022 at 06:20:50PM +0000, Salih Dincer via Digitalmars-d wrote:
> As long as the Voldemort type lives on, does int x, y legally live on?
> 
> ```d
> auto getPoint()
> {
>   int x, y; // major issue
> 
>   enum type = "2D";
>   struct Point(string Id)
>   {
>     static int _x = 1024;
>     static int _y = 768;
>     string toString() {
>       import std.format;
>       return format("Limit: %dx%d px.",
>                                _x, _y);
>     }
>     void set(int a, int b) { x = a; y = b; }
>     uint area() { return x * y; }
>     auto get() { return this; }
>   }
>   auto px = Point!type();
>   return px.get;
> }
> ```

That's a closure over x and y, so yes, they will live on.  The compiler will detect that a closure is required, so it will allocate them on the heap instead of the stack, and the struct Point will have a hidden context pointer that points to the heap allocation that contains the values of x and y.  As long as a Point instance still exists that points to the allocated block, the GC will not collect it.

In general, though, I don't recommend closing over local variables in a struct, unless you mean to share those variables across copies of the struct.  In the above example, if you call getPoint twice, you will get two Point structs pointing to different copies of x and y.  But if you make a copy of either struct, you will end up with two structs that share the *same* set of x and y values (so changes to one will be seen by the other), which may or may not be what you want.  This can make for rather confusing behaviour if these structs get passed around to other code.

My usual practice is to avoid such struct closures, and to store independent copies of x and y inside the struct itself instead (just make them private and move it into a separate module if you're paranoid).


T

-- 
"A one-question geek test. If you get the joke, you're a geek: Seen on a California license plate on a VW Beetle: 'FEATURE'..." -- Joshua D. Wachs - Natural Intelligence, Inc.