Temporaries / struct RAII patterns

Witold witold.baryluk+dlang at gmail.com
Mon Oct 16 00:04:59 UTC 2023


Not sure how to name the problem, but here it is.

I want to create a struct instance on a stack using a helper 
function, manipulate it Fluent-like style, then destructor be 
invoked, that will do something with its state.

I do not want to use `Unique`, because that will allocate actual 
data on heap. And unique will have pointer to it. This is because 
one can do release and move on Unique. I do not need (or want) 
release or move.

I want something that can be done on a stack, but can be 
manipulated and passed to functions, and destructor called once I 
either go out of scope, or it is not used at all, and expression 
/ statement ends.


```d
struct X {
   string message;
   string file;
   int line;
   string[string] kvs;

   ref X F(string fmt, Args...)(const Args args) {
     return this;
     ...
   }
   ref X KV(string k, string v) {
     kvs[k] = v;
     return this;
   }
   ~this() {
      DoSomething(&this, message, kvs);
   }
}

X Foo(const string message, string file = __FILE__, int line = 
__LINE__) {
   return X(message, file, line)
}

X Foo(string fmt, Args...)(lazy Args args, string file = 
__FILE__, int line = __LINE__) {
   return X(message, file, line).F!(fmt, Args)(args);
}


void main() {
    Foo("a");
    Foo("b").kv("k1", "v1").kv("k2", "v2");
    Foo!"c %d %s"(1, "ble").kv("k3", "v3");
}
```


I know I can do this:

```d
void main() {
    {
      scope x = Foo("a");
    }
    {
      scope x = Foo("b");
      x.KV("k1", "v1");
      x.KV("k2", "v2");
    }
}
```

But this is not going to scale (I will have many of these 
statements in each file and function), and now requires me to 
remember to put `scope` in front of each variable (`scope` 
constraint keyword on struct type itself is deprecated), which is 
error prone.

How do I ensure that `X` is not copied (i.e. on return from `f`), 
or when doing `kvs` (and similar functions), and that `X` is 
allocated on the stack, and is destroyed properly.

I also do want to be able to d the second form (with explicit 
scope and calls to `kv`, i.e. when doing `kv` calls in a loop 
from some other array or associative array).


I cannot use `ref` return on the `Foo`, because it the thing I am 
returning either is not an lvalue, or any lvalue it could 
reference would be on stack of `Foo`, so it will be returning 
invalid reference after `Foo` returns.

Cheers.


More information about the Digitalmars-d mailing list