www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 10822] New: Need a way to get the address of a lambda function from within its body

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=10822

           Summary: Need a way to get the address of a lambda function
                    from within its body
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: DMD
        AssignedTo: nobody puremagic.com
        ReportedBy: andrej.mitrovich gmail.com



15:33:58 PDT ---
Currently there doesn't seem to be a way to retrieve the address of a lambda
function. 

If for any reason you need to know the address of the lambda function /within/
the body of the lambda, you can't currently do this without assigning it to
another variable.

The use-case are signals. Here's a contrived example:

-----
module test;

import std.stdio;

struct Signal
{
    void connect(void delegate(int) func)
    {
        funcs ~= func;
    }

    void disconnect(void delegate(int) target)
    {
        foreach (idx, func; funcs)
        {
            if (func is target)
            {
                funcs = funcs[0 .. idx] ~ funcs[idx + 1 .. $];
                break;
            }
        }
    }

    void emit(int i)
    {
        foreach (func; funcs)
            func(i);
    }

    void delegate(int)[] funcs;
}

void main()
{
    Signal signal;

    void delegate(int) handler;
    handler =
        (int i)
        {
            stderr.writefln("handler called with %s", i);

            if (i == 2)
            {
                signal.disconnect(handler);  // ok
            }
        };

    signal.connect(handler);

    signal.emit(1);
    signal.emit(2);
    signal.emit(3);
}
-----

In 'main', the lambda function is assigned to a 'handler' variable. The
lambda's body can then reference this variable if it needs to pass its address
around. It uses this address to disconnect itself from a list of signal
handlers. 

Do note however that using this code is only possible because the lambda is a
delegate and not a function type! IOW, calling "signal.disconnect(handler)"
would not be possible if the lambda was a function. If it was a function it
wouldn't have access to the outer frame to get the value of 'handler', even
though it is de-facto an address of itself. Of course using the 'signal'
variable also forces the lambda to be a delegate, but that's not important.

If we had a way to retrieve the address of a lambda from within the body of the
lambda, we could both:

1) Avoid having to assign the lambda to a variable in order to retrieve the
address from within the labmda body


This would allow using code such as the following:

-----
import std.stdio;

// note1: changed to accept functions
// note2: now passing a signal reference explicitly
struct Signal
{
    void connect(void function(ref Signal, int) func)
    {
        funcs ~= func;
    }

    void disconnect(void function(ref Signal, int) target)
    {
        foreach (idx, func; funcs)
        {
            if (func is target)
            {
                funcs = funcs[0 .. idx] ~ funcs[idx + 1 .. $];
                break;
            }
        }
    }

    void emit(int i)
    {
        foreach (func; funcs)
            func(this, i);
    }

    void function(ref Signal, int)[] funcs;
}

void main()
{
    Signal signal;

    // connect to anonymous function
    signal.connect((ref Signal theSignal, int i)
        {
            stderr.writefln("handler called with %s", i);

            if (i == 2)
            {
                // new trait: get the address of this lambda
                theSignal.disconnect(__traits(addressOfThis));
            }
        });

    signal.emit(1);
    signal.emit(2);
    signal.emit(3);  // no longer calls the lambda
}
-----

I suppose __traits(addressOfThis) could also be useful in classes, to avoid
having to use "cast(void*)this" or even the opCast-safe "*cast(void**)&this".

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Aug 14 2013
parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=10822




15:38:16 PDT ---

 theSignal.disconnect(__traits(addressOfThis));
I did just realize that addressOfThis would have to return a typed pointer (e.g. void delegate(...) or void function(...)) rather than a vanilla void*, so addressOfThis is likely a bad name. A better concept name might be a getThis trait, where the trait returns the typed reference. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Aug 14 2013