InputRange help: (1) repeated dtor calls and (2) managing resources needing free()

Seb seb at wilzba.ch
Mon Aug 13 13:20:25 UTC 2018


On Monday, 13 August 2018 at 04:23:49 UTC, James Blachly wrote:
> On Thursday, 14 June 2018 at 00:42:25 UTC, James Blachly wrote:
>> ...
>> I assume the (apparent) lack of parity between ctor and dtor 
>> is because the "default postblit" (which I figured out for a 
>> struct means an empty `this(this)` ctor) is called when a copy 
>> is made. My understanding is that I cannot disable the default 
>> postblit and still act as a range, correct? Should I be 
>> overloading this?
>>
>> 2. Directly related to the above, I need, when the range is 
>> consumed, to free() the underlying library's iterator handle. 
>> Naively, I had the destructor do this, but obviously with 
>> multiple calls to ~this I end up with an error free()'ing a 
>> pointer that is no longer alloc'd.  What is the correct way to 
>> handle this situation in D?
>>
>> Other Range and destructor advice generally (e.g., "You should 
>> totally change your design or approach to X instead") is 
>> always welcomed.
>>
>> James
>
> I think I have a handle on #1 (copy of the range is made for 
> consumption which is why dtor is called more often than ctor), 
> but would still be interested in advice regarding #2 (as well 
> as general Range and dtor advice).
>
> Here: 
> https://github.com/blachlylab/dhtslib/blob/master/source/dhtslib/tabix.d#L98 I need to free the library's iterator, but the Range's destructor is the wrong place to do this, otherwise memory is freed more than once.
>
> Is it a better approach to (a) somehow guard the call to 
> tbx_itr_destroy or (b) create a postblit that creates a new 
> iterator and pointer? (or (c), None of the above)

I would "guard" the call to tbx_itr_destroy by means of reference 
counting (see below).

> As above, my understanding is that disabling the default 
> posblit prohibits acting as a Range.

That's not true. It just makes the range harder to be used.
Last year, for example, it was proposed to make the ranges in 
std.random non-copyable because you don't want to accidentally 
copy your random state and it was only that bigger refactorings 
were planned for std.random which sadly never materialized that 
this didn't happen.

BTW it's very uncommon for empty to do work, it's much more 
common to do such lazy initialization in `.front`.

> If I use the range, the destructor seems to be called many, 
> many times.

Then you probably make many copies.

> In some ways, this problem is generalizable to all InputRanges 
> that represent a file or record stream.

Yep, and that's why I recommend to have a look at e.g. 
std.stdio.File:

- it does its initialization in the constructor [1]
- it uses reference-counting for its allocated space and pointers 
[2, 3] (File is often shared by default, that's why atomic 
reference counting is necessary here)

Have a look at this minimal example of reference-counting:

https://run.dlang.io/is/GF5vbC

The copies you see go away when the struct is passed by reference:

https://run.dlang.io/is/Uhs5Bt

[1] 
https://github.com/dlang/phobos/blob/565a51f8c6e8b703c0b625568a6f14473345f5d8/std/stdio.d#L394
[2] 
https://github.com/dlang/phobos/blob/565a51f8c6e8b703c0b625568a6f14473345f5d8/std/stdio.d#L474
[3] 
https://github.com/dlang/phobos/blob/565a51f8c6e8b703c0b625568a6f14473345f5d8/std/stdio.d#L835


More information about the Digitalmars-d-learn mailing list