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

Reply via email to