www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Make sure lifetime of helper structs is less than owning struct

reply WebFreak001 <d.forum webfreak.org> writes:
I have an API with some struct like a file reader. I want to add 
byChunks-like functionality to it, so I'm trying to implement it 
with a helper struct that implements opApply. I have disabled 
copying the file reader struct because it cleans up the resources 
once it goes out of scope, however now I need to temporarily save 
the resources in the helper struct to be able to read from it.

How can I make sure that the foreach helper struct (and with that 
the copies of the resources) cannot be used once the owning 
struct goes out of scope?

```d
ByChunk helper;
{
     auto file = FileReader(x);
     helper = file.byChunk;
}
helper.popFront; // crash - I want the compiler to disallow this
```

is this currently possible or maybe possible with DIP1000?
Nov 15 2021
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 11/15/21 10:56 AM, WebFreak001 wrote:
 I have an API with some struct like a file reader. I want to add 
 byChunks-like functionality to it, so I'm trying to implement it with a 
 helper struct that implements opApply. I have disabled copying the file 
 reader struct because it cleans up the resources once it goes out of 
 scope, however now I need to temporarily save the resources in the 
 helper struct to be able to read from it.
 
 How can I make sure that the foreach helper struct (and with that the 
 copies of the resources) cannot be used once the owning struct goes out 
 of scope?
 
 ```d
 ByChunk helper;
 {
      auto file = FileReader(x);
      helper = file.byChunk;
 }
 helper.popFront; // crash - I want the compiler to disallow this
 ```
 
 is this currently possible or maybe possible with DIP1000?
Or maybe just use reference counting to avoid the problem altogether. That's what iopipe/std.io does. -Steve
Nov 15 2021
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Nov 15, 2021 at 03:56:57PM +0000, WebFreak001 via Digitalmars-d-learn
wrote:
 I have an API with some struct like a file reader. I want to add
 byChunks-like functionality to it, so I'm trying to implement it with
 a helper struct that implements opApply. I have disabled copying the
 file reader struct because it cleans up the resources once it goes out
 of scope, however now I need to temporarily save the resources in the
 helper struct to be able to read from it.
 
 How can I make sure that the foreach helper struct (and with that the
 copies of the resources) cannot be used once the owning struct goes
 out of scope?
 
 ```d
 ByChunk helper;
 {
     auto file = FileReader(x);
     helper = file.byChunk;
 }
 helper.popFront; // crash - I want the compiler to disallow this
 ```
 
 is this currently possible or maybe possible with DIP1000?
What about make ByChunk do the construction of the File in its ctor instead? The problem with constructing it separately is that you can't tie the two lifetimes together. But if you create the File while initializing the object, you ensure that the two lifetimes are tied together, and with disable this() you can make sure that ByChunk is not constructible unless the code that opens the File also runs. T -- Computers shouldn't beep through the keyhole.
Nov 15 2021
prev sibling parent reply Sebastiaan Koppe <mail skoppe.eu> writes:
On Monday, 15 November 2021 at 15:56:57 UTC, WebFreak001 wrote:
 is this currently possible or maybe possible with DIP1000?
Yes it is. But besides `-dip1000` and ` safe`, it requires the use of a pointer: ```D safe: struct ByChunk { FileReader* r; void popFront() {} } struct FileReader { ByChunk byChunk() return scope { return ByChunk(&this); } } void main() { ByChunk helper; { auto file = FileReader(); helper = file.byChunk; // Error: address of variable `file` assigned to `helper` with longer lifetime } helper.popFront; } ```
Nov 15 2021
next sibling parent reply WebFreak001 <d.forum webfreak.org> writes:
On Monday, 15 November 2021 at 19:24:56 UTC, Sebastiaan Koppe 
wrote:
 On Monday, 15 November 2021 at 15:56:57 UTC, WebFreak001 wrote:
 is this currently possible or maybe possible with DIP1000?
Yes it is. But besides `-dip1000` and ` safe`, it requires the use of a pointer: ```D safe: struct ByChunk { FileReader* r; void popFront() {} } struct FileReader { ByChunk byChunk() return scope { return ByChunk(&this); } } void main() { ByChunk helper; { auto file = FileReader(); helper = file.byChunk; // Error: address of variable `file` assigned to `helper` with longer lifetime } helper.popFront; } ```
awesome, such a simple solution! Also saves me the pain of copying the struct data and avoiding copy constructor and stuff by using a pointer.
Nov 15 2021
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 11/15/21 1:58 PM, WebFreak001 wrote:
 On Monday, 15 November 2021 at 19:24:56 UTC, Sebastiaan Koppe wrote:
 On Monday, 15 November 2021 at 15:56:57 UTC, WebFreak001 wrote:
 is this currently possible or maybe possible with DIP1000?
Yes it is. But besides `-dip1000` and ` safe`, it requires the use of a pointer: ```D safe: struct ByChunk { FileReader* r; void popFront() {} } struct FileReader { ByChunk byChunk() return scope { return ByChunk(&this); } } void main() { ByChunk helper; { auto file = FileReader(); helper = file.byChunk; // Error: address of variable `file` assigned to `helper` with longer lifetime } helper.popFront; } ```
awesome, such a simple solution! Also saves me the pain of copying the struct data and avoiding copy constructor and stuff by using a pointer.
I don't see how it solves the problem. Sebastiaan Koppe might have intended to allocate the object dynamically with 'new' as well? import std.stdio; safe: struct ByChunk { FileReader* r; ~this() { writeln(__FUNCTION__); } void popFront() { writeln("popFront on ", r); } } struct FileReader { ~this() { writeln(__FUNCTION__, " on ", &this); } ByChunk byChunk() return scope { return ByChunk(&this); } } void main() { ByChunk helper; { auto file = new FileReader(); // <-- NOTE new helper = file.byChunk; } writeln("back in main"); helper.popFront; } This is the current output: deneme.ByChunk.~this back in main popFront on 7F12B377E000 deneme.ByChunk.~this deneme.FileReader.~this on 7F12B377E000 But without that 'new', FileReader is destroyed before popFront: deneme.ByChunk.~this deneme.FileReader.~this on 7FFD8231D4B8 <-- BAD back in main popFront on 7FFD8231D4B8 deneme.ByChunk.~this I think I am misunderstanding something here. :) Ali
Nov 15 2021
parent reply Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Monday, 15 November 2021 at 22:27:24 UTC, Ali Çehreli wrote:
 On 11/15/21 1:58 PM, WebFreak001 wrote:
 [...]
wrote:
 [...]
wrote:
 [...]
the use of
         [...]
variable `file`
 [...]
copying the
 [...]
a pointer. I don't see how it solves the problem. Sebastiaan Koppe might have intended to allocate the object dynamically with 'new' as well? [...]
Are you compiling with preview=dip1000?
Nov 15 2021
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 11/15/21 2:33 PM, Imperatorn wrote:
 On Monday, 15 November 2021 at 22:27:24 UTC, Ali =C3=87ehreli wrote:
 On 11/15/21 1:58 PM, WebFreak001 wrote:
 [...]
wrote:
 [...]
wrote:
 [...]
the use of
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 [...]
variable `file`
 [...]
copying the
 [...]
a pointer. I don't see how it solves the problem. Sebastiaan Koppe might have=20 intended to allocate the object dynamically with 'new' as well? [...]
=20 Are you compiling with preview=3Ddip1000?
Trying with it produces an error when 'new' is not used: Error: reference to local variable `file` assigned to non-scope=20 parameter `p` calling deneme.ByChunk.opAssign So, I either misunderstand or understand very well :) that a different=20 way of lifetime management (like adding 'new') is needed there. Ali
Nov 15 2021
parent reply Sebastiaan Koppe <mail skoppe.eu> writes:
On Monday, 15 November 2021 at 22:49:12 UTC, Ali Çehreli wrote:
 Trying with it produces an error when 'new' is not used:

 Error: reference to local variable `file` assigned to non-scope 
 parameter `p` calling deneme.ByChunk.opAssign
The error is what the OP wanted, so that is expected. Although, he did ask for it to be on the next line, but this is better since it points exactly to the line where he was escaping a reference to the scoped object.
 I don't see how it solves the problem. Sebastiaan Koppe might 
 have intended to allocate the object dynamically with 'new' as 
 well?
No I didn't. Anything created with new has automatic lifetime. Here the OP wanted to (have the compiler) destroy the FileReader when it left the scope, while disallowing any use after free.
Nov 16 2021
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 11/16/21 12:35 AM, Sebastiaan Koppe wrote:

 Here the
 OP wanted to (have the compiler) destroy the FileReader when it left the
 scope, while disallowing any use after free.
Thanks! That's what I missed. Ali
Nov 16 2021
prev sibling parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Monday, 15 November 2021 at 19:24:56 UTC, Sebastiaan Koppe 
wrote:
 On Monday, 15 November 2021 at 15:56:57 UTC, WebFreak001 wrote:
 is this currently possible or maybe possible with DIP1000?
Yes it is. But besides `-dip1000` and ` safe`, it requires the use of a pointer: ```D safe: struct ByChunk { FileReader* r; void popFront() {} } struct FileReader { ByChunk byChunk() return scope { return ByChunk(&this); } } void main() { ByChunk helper; { auto file = FileReader(); helper = file.byChunk; // Error: address of variable `file` assigned to `helper` with longer lifetime } helper.popFront; } ```
Maybe a candidate for the "d idioms" page
Nov 15 2021