Thread overview
An issue with setting delegates via templates
Oct 26, 2011
Andrej Mitrovic
Oct 27, 2011
Christophe
Oct 27, 2011
Andrej Mitrovic
Nov 02, 2011
Christophe Travert
October 26, 2011
class Foo
{
    void func(double) { }
    void func(int) { }

    void set(T)(T handler) { dg = handler; }
    void delegate(int) dg;
}

void main()
{
    auto foo = new Foo;
    foo.set(&foo.func);  // NG, func(double) is picked up first
}

Error: cannot implicitly convert expression (handler) of type void
delegate(double _param_0) to void delegate(int)

The problem here is that `void func(double)` is declared first, and that's what the address-of operator picks up. If you swap the two overloads this sample will compile.

How could this be worked around /while still using templates/? There can only be one topmost function overload, so with two overloads this can be worked around, but with more overloads this workaround can't be used.

I've almost found a workaround via the getOverloads trait:

import std.traits;

class Foo
{
    void func(double) { }
    void func(int) { }

    void set(T)(T handler)
    {
        dg = handler;
    }

    void delegate(int) dg;
}

void main()
{
    auto foo = new Foo;
    foo.set(&__traits(getOverloads, foo, "func")[1]);
}

This works, but I can't use this from within a template. IOW, I can't pass a function pointer to a template and then figure out if that function is actually an overload of some class. I'd need more powerful compile-time features for that. If I were able to do that then I could enumerate all the overloads and pick one if it matches the type of 'dg'. It's kind of overkill but it could work, at least theoretically.

Otherwise I'd really need a way to explicitly specify which function overload to pass when I use the address-of operator, but that would probably have to be a language feature.
October 27, 2011
Andrej Mitrovic , dans le message (digitalmars.D.learn:30286), a écrit :
> class Foo
> {
>     void func(double) { }
>     void func(int) { }
> 
>     void set(T)(T handler) { dg = handler; }
>     void delegate(int) dg;
> }
> 
> void main()
> {
>     auto foo = new Foo;
>     foo.set(&foo.func);  // NG, func(double) is picked up first
> }
> 
> Error: cannot implicitly convert expression (handler) of type void
> delegate(double _param_0) to void delegate(int)
> 
> The problem here is that `void func(double)` is declared first, and that's what the address-of operator picks up. If you swap the two overloads this sample will compile.
> 
> How could this be worked around /while still using templates/? There can only be one topmost function overload, so with two overloads this can be worked around, but with more overloads this workaround can't be used.
> 
> I've almost found a workaround via the getOverloads trait:
> 
> import std.traits;
> 
> class Foo
> {
>     void func(double) { }
>     void func(int) { }
> 
>     void set(T)(T handler)
>     {
>         dg = handler;
>     }
> 
>     void delegate(int) dg;
> }
> 
> void main()
> {
>     auto foo = new Foo;
>     foo.set(&__traits(getOverloads, foo, "func")[1]);
> }
> 
> This works, but I can't use this from within a template. IOW, I can't pass a function pointer to a template and then figure out if that function is actually an overload of some class. I'd need more powerful compile-time features for that. If I were able to do that then I could enumerate all the overloads and pick one if it matches the type of 'dg'. It's kind of overkill but it could work, at least theoretically.
> 
> Otherwise I'd really need a way to explicitly specify which function overload to pass when I use the address-of operator, but that would probably have to be a language feature.


Once you are inside set, handler is just a function and context pointer pair. Finding out what are the overloads of handler just with it's adress will be very complicated for a very limitted usage. Maybe an approach would be to make handler an alias rather than a delegate (but this won't work here because you have to pass foo along with &Foo.func...).

Why does Foo.set has to be a template ?
The obvious workarround is to make set expect a void delegate(int),
instead of an undefined type, since this is the only type you can assign
to dg.

I think I need a better example to understand why set is a template, and how to work arround that.

October 27, 2011
So it seems this is an issue with a newer signals implementation.

The old one works:

import std.signals;

struct Foo
{
    mixin Signal!(int) sig;
}

class Bar
{
   void x(string) { }
   void x(int) { }
}

void main()
{
    Foo foo;
    auto bar = new Bar;

    foo.sig.connect(&bar.x);
    foo.sig.emit(1);
}

But the new reimplementation (not in Phobos) doesn't:

import signalsnew;

struct Foo
{
    Signal!(int) sig;  // no need for mixin in new signals
}

class Bar
{
   void x(string) { }
   void x(int) { }
}

void main()
{
    Foo foo;
    auto bar = new Bar;

    foo.sig.connect(&bar.x);
    foo.sig.emit(1);
}

The new one: https://gist.github.com/1194497

I'll investigate this further then.
November 02, 2011
Andrej Mitrovic , dans le message (digitalmars.D.learn:30315), a écrit :
> The new one: https://gist.github.com/1194497
> 
> I'll investigate this further then.

OK, so there is indeed a filter to find a suitable delegate to load:

@trusted T connect(T)(T handler) if(isHandler!(T, Types));

But only the first Bar.x function is picked and given to isHandler!(T, int) to check it can be called. Bar.x(double) do not pass this filter.

I guess if the signature were:

T connect(void handler(Types));
T connect(bool handler(Types));
etc...

or maybe
T connect(T : void handler(Types)) (T handler);
T connect(T : bool handler(Types)) (T handler);
etc...

Then the compiler should be able to select the right Bar.x method. But with the isHandler filter,it is not able to find it. I don't see a way to improve isHandler to make it able to find the right Bar.x method, without modification of the compiler*, but I am not an expert here.

* the compiler should allow all overloads of a function given as a template argument to be tested against the filter to choose the right one, and complain if several overloads match the filter.

-- 
Christophe