On Friday, 5 October 2018 at 03:27:17 UTC, Jon Degenhardt wrote:
I got the compilation error in the subject line when trying to
create a range via std.range.generate. Turns out this was
caused by trying to create a closure for 'generate' where the
closure was accessing a struct containing a destructor.
The fix was easy enough: write out the loop by hand rather than
using 'generate' with a closure. What I'm wondering/asking is
if there alternate way to do this that would enable the
'generate' approach. This is more curiosity/learning at this
point.
Below is a stripped down version of what I was doing. I have a
struct for output buffering. The destructor writes any data
left in the buffer to the output stream. This gets passed to
routines performing output. It was this context that I created
a generator that wrote to it.
----example.d-----
struct BufferedStdout
{
import std.array : appender;
private auto _outputBuffer = appender!(char[]);
~this()
{
import std.stdio : write;
write(_outputBuffer.data);
_outputBuffer.clear;
}
void appendln(T)(T stuff)
{
import std.range : put;
put(_outputBuffer, stuff);
put(_outputBuffer, "\n");
}
}
void foo(BufferedStdout output)
{
import std.algorithm : each;
import std.conv : to;
import std.range: generate, takeExactly;
import std.random: Random, uniform, unpredictableSeed;
auto randomGenerator = Random(unpredictableSeed);
auto randomNumbers = generate!(() => uniform(0, 1000,
randomGenerator));
auto tenRandomNumbers = randomNumbers.takeExactly(10);
tenRandomNumbers.each!(n => output.appendln(n.to!string));
}
void main(string[] args)
{
foo(BufferedStdout());
}
----End of example.d-----
Compiling the above results in:
$ dmd example.d
example.d(22): Error: variable `example.foo.output` has
scoped destruction, cannot build closure
As mentioned, using a loop rather than 'generate' works fine,
but help with alternatives that would use generate would be
appreciated.
The actual buffered output struct has more behind it than shown
above, but not too much. For anyone interested it's here:
https://github.com/eBay/tsv-utils/blob/master/common/src/tsvutil.d#L358
tenRandomNumbers.each!((n,o) => o.appendln(n.to!string))(output);
or
tenRandomNumbers.each!((n, ref o) =>
o.appendln(n.to!string))(output);
should hopefully do the trick (run.dlang.io seems to be down atm).
The problem is that `output` is captured by the delegate and this
somehow causes problems (idk what or why). If the (perviously
captured) variables are instead passed as parameters then there
is no need to create a closure for the variable, so no problem.
This is also the way to ensure that lambdas are @nogc if you ever
need that.