www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Copying a delegate

reply Jonathan M Davis <jmdavisProg gmx.com> writes:
Is it in any way possible to copy a delegate? Take this program for example:

import std.stdio;

void main()
{
    int a = 0;

    int func()
    {
        return a++;
    }

    int delegate() b = &func;
    int delegate() c = b;

    writeln(b());
    writeln(c());
    writeln(b());
}


It will print

0
1
2

But what if I wanted it to print

0
0
1


That doesn't work because you're just copying the pointer. Is there a way to 
actually do a deep copy of the delegate? I can see why this would be
problematic 
if the delegate had reference types in its scope (since presumably, they'd have 
to be shallow copied unless you somehow had the equivalent of a copy
constructor 
or postblit constructor for delegates), but even the ability to a do a shallow 
copy would be better than nothing. Is there any way to do that, or am I out of 
luck?

- Jonathan M Davis
Sep 18 2010
next sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
On 19/09/2010 04:35, Jonathan M Davis wrote:
<snip>
 That doesn't work because you're just copying the pointer. Is there a way to
 actually do a deep copy of the delegate? I can see why this would be
problematic
 if the delegate had reference types in its scope (since presumably, they'd have
 to be shallow copied unless you somehow had the equivalent of a copy
constructor
 or postblit constructor for delegates), but even the ability to a do a shallow
 copy would be better than nothing. Is there any way to do that, or am I out of
 luck?

You mean to duplicate whatever the delegate's context pointer points to? The trouble is that the context pointer could point to either an object or a stack frame. Any code to duplicate a delegate on this basis would need to consider both possibilities. Moreover, a class may or may not provide a dup method, and there's no predictable vtbl slot for it. Things get even more complicated when you realise that occasionally there may be an object allocated on the stack. As such, this would be a complex feature, and IMO unlikely to be implemented. I can't even think of any real practical use for it. Can you? Stewart.
Sep 19 2010
parent reply Kagamin <spam here.lot> writes:
Jonathan M Davis Wrote:

 delegate by definition has context which is not going to be copied. So, any 
 functon pointer or delegate that you have must refer to a function which is 
 logically pure, otherwise any algorithm that relies on save is not going to
work 
 correctly.

If phobos causes subtle bugs, it's a bug worth filing.
Sep 20 2010
parent Kagamin <spam here.lot> writes:
Jonathan M Davis Wrote:

 I don't think that there's anything Phobos can do 
 about it. It's a limitation of delegates.

If the range doesn't support copying, it shouldn't pretend to support it, and algorithms relying on copy won't compile.
Sep 20 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 19 September 2010 17:13:14 Stewart Gordon wrote:
 On 19/09/2010 04:35, Jonathan M Davis wrote:
 <snip>
 
 That doesn't work because you're just copying the pointer. Is there a way
 to actually do a deep copy of the delegate? I can see why this would be
 problematic if the delegate had reference types in its scope (since
 presumably, they'd have to be shallow copied unless you somehow had the
 equivalent of a copy constructor or postblit constructor for delegates),
 but even the ability to a do a shallow copy would be better than
 nothing. Is there any way to do that, or am I out of luck?

You mean to duplicate whatever the delegate's context pointer points to? The trouble is that the context pointer could point to either an object or a stack frame. Any code to duplicate a delegate on this basis would need to consider both possibilities. Moreover, a class may or may not provide a dup method, and there's no predictable vtbl slot for it. Things get even more complicated when you realise that occasionally there may be an object allocated on the stack. As such, this would be a complex feature, and IMO unlikely to be implemented. I can't even think of any real practical use for it. Can you? Stewart.

Most definitely yes, I can think of a purpose for it, though I can certainly see why it may be unreasonable to have it. The issue is the save property for forward ranges. For it to work properly, it must be able to copy all of the state related to iteration. So, any reference types must either be unrelated to saving the iteration state, or they must be deep copied. It can be quite useful to use functions are delegates to generate a range. So, when you call popFront(), the internal function pointer or delegate is used to generate the next element in the range. The problem is that there is no way to do a deep copy of a function pointer or a delegate. The function pointed to could access globals or static state unless it's pure (and it can be quite hard to have a pure function which is useful enough to be used in a such a situation). The delegate by definition has context which is not going to be copied. So, any functon pointer or delegate that you have must refer to a function which is logically pure, otherwise any algorithm that relies on save is not going to work correctly. This makes it impossible to use delegates which use their own internal state or global state to help determine what the next element would be. Even being able to do a shallow copy of a delegate (so, doing a shallow copy of its context) would increase what you could do with such a delegate, though a deep copy is what you'd really need. You can get around the problem by using functors instead of delegates, but functors are way more unweildy and much less pleasant to create. With delegates, you can use lambda functions which are far more pleasant. So, I can understand why it's probably infeasible to get a deep copy of a delegate, and why it would be difficult in not totally unreasonable to get a shallow copy of one, but it would definitely be useful if you could. - Jonathan M Davis
Sep 19 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday 20 September 2010 01:08:25 Kagamin wrote:
 Jonathan M Davis Wrote:
 delegate by definition has context which is not going to be copied. So,
 any functon pointer or delegate that you have must refer to a function
 which is logically pure, otherwise any algorithm that relies on save is
 not going to work correctly.

If phobos causes subtle bugs, it's a bug worth filing.

Except that it's not really phobos' fault. The problem is that once part of the state of a struct or class is a delegate, you *can't* copy it. As far as I know, there is no way in the language to do it. A forward range's save property is supposed to give you a copy of the range which copies the state specific to iterating the range such that you can iterate the copy to your heart's content without affecting the original's ability to iterate. But if a delegate is part of the iteration state of a range, and that delegate isn't logically pure, the copy is going to use that delegate and alter the state of the original range, because they share that delegate. I don't think that there's anything Phobos can do about it. It's a limitation of delegates. - Jonathan M Davis
Sep 20 2010
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, September 20, 2010 04:08:11 Kagamin wrote:
 Jonathan M Davis Wrote:
 I don't think that there's anything Phobos can do
 about it. It's a limitation of delegates.

If the range doesn't support copying, it shouldn't pretend to support it, and algorithms relying on copy won't compile.

Except that you don't know what is in a delegate, or it wouldn't be a delegate. Many delegates can be reused just fine and thus "copied" because they are logically pure. Others can't be. If you have a range based on a delegate, unless you know every single function that could be used as the delegate, you have no way of knowing whether the delegate is logically pure. And even then, you'd only know it because you knew what the functions did; the compiler couldn't enforce it. So, you can have a range which properly supports copying as long as its delegate is logically pure, but you can't use the compiler to enforce that the delegate be logically pure. There's no way for Phobos to avoid the problem. The only way to completely avoid the problem is not to have ranges which are generated with delegates, but that would eliminate a lot of useful ranges. - Jonathan M Davis
Sep 20 2010