On Monday, 27 January 2014 at 02:27:08 UTC, Steven Schveighoffer
wrote:
On Sun, 26 Jan 2014 18:41:00 -0500, Nicolas Sicard
<dran...@gmail.com> wrote:
Running a piece of code that can be reduced to:
---
import std.stdio;
void main()
{
import std.range;
foreach(item; iota(0, 10).transform(2))
writeln(item);
}
auto transform(T)(T list, real x)
{
auto t = /* new */ Transformer(x); // line 12
return t.applyTo(list);
}
struct Transformer
{
real delegate(real) fun;
this(real x)
{
fun = (real r) => r * x;
}
auto applyTo(T)(T list)
{
import std.algorithm;
return list.map!(x => fun(x));
}
}
---
the program segfaults. I guess it's because fun is destroyed
when 't' goes out of scope in 'transform'. I would have
thought that the MapResult struct returned by 'applyTo' still
holds a valid copy of fun, but I'm wrong... Is there a way to
do it?
No. Just don't do that. The runtime is permitted to move
structs bit-for-bit, so you are not allowed to store a pointer
that references 'this'. Unless you prevent copying via @disable
this(this), your struct could be moved if someone, for
instance, passed it as a parameter on the stack, or returned it.
A delegate using 'this' as the context pointer is the same
thing.
The only way to solve this is to put the struct on the heap.
But why even use a struct? You could just use a closure (which
automatically goes on the heap if needed):
auto Transformer(real x)
{
return (real r) => r * x;
}
auto applyTo(X, T)(X fun, T list)
{
import std.algorithm;
return list.map!(x => fun(x));
}
auto transform(T)(T list, real x)
{
auto t = Transformer(x);
return t.applyTo(list);
}
-Steve
Actually I used a struct because the code is more complex, and it
builds an array of delegates, which are returned from global
functions, like:
---
struct Transformer
{
real delegate(real)[] funs;
addFun(real x)
{
fun ~= makeFun(x);
}
// etc.
}
real delegate(real) makeFun(real x)
{
return (real r) => r * x;
}
---
This means my design was bad in the first place.
Thanks for the explanation.
Nicolas