InputRange help: (1) repeated dtor calls and (2) managing resources needing free()
James Blachly
james.blachly at gmail.com
Thu Jun 14 00:42:25 UTC 2018
Hi all,
I now really appreciate the power Ranges provide and am an avid
consumer, but am only slowly becoming accustomed to implementing
my own.
In the present problem, I am writing a binding to a C library
(htslib) that provides many functions related to high-throughput
sequencing files. One of these functions is for rapid indexed
lookup into multi-GB files. The library provides a handle to an
iterator which must be supplied to a "get next matching row" type
function, which overall seems perfect for implementation as a
range. You can see my naive implementation here:
https://github.com/blachlylab/dhtslib/blob/master/source/dhtslib/tabix.d
Note that TabixIndexedFile::region returns an InputRange; in the
original implementation, this Range preloaded the first record
(the ctor called popFirst()), but ultimately I realized this was
not workable because copies of the object would always be
non-empty. In some ways, this problem is generalizable to all
InputRanges that represent a file or record stream.
My problems now are at least twofold.
1. If I use the range, the destructor seems to be called many,
many times. This is directly related to problem 2, below, but I
would be interested to understand why this is happening
generally. For example, see:
https://github.com/blachlylab/dhtslib/blob/master/test/tabix_gffreader.d
Here, when I create the range but do not consume it, the ctor and
dtor are called once each, as expected. However, if I
foreach(line; r) { } the destructor is called twice. If I reason
through this, it is because use of the range created a copy to
consume. (?) However, if instead, I writeln( r ), the destructor
is called *five* times. I cannot understand the reason for this,
unless it is black magic required by writeln().
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
More information about the Digitalmars-d-learn
mailing list