May 03, 2015
On 2015-05-03 06:20, Andrei Alexandrescu wrote:

> Is this a common thing people wanna do? Put in Phobos?

Yes, I would think so. Although, I would prefer a regular template mixin and taking the member as an alias parameter instead of a string, if possible.

-- 
/Jacob Carlborg
May 03, 2015
On 5/3/15 12:18 AM, Meta wrote:
> On Sunday, 3 May 2015 at 05:49:52 UTC, Andrei Alexandrescu wrote:
>> On 5/2/15 10:00 PM, Meta wrote:
>>> It seems like it'd be a lot cheaper and cleaner to just be able to alias
>>> the parent method.
>>
>> Yeh, that's the first solution that comes to mind. alias doesn't work
>> here but of course we could change the language.
>>
>>> Also, it could probably be made a bit simpler with
>>> opDispatch.
>>
>> I'd have to see the code, but my intuition is that things could get
>> quite a bit more hairy.
>>
>>
>> Andrei
>
> IMO, using __traits and opDispatch is a fair bit cleaner, and I prefer
> the syntax of a mixin template to regular mixin.
>
> http://dpaste.dzfl.pl/d60498246577

You're right, that is lovely! I've improved it as follows:

mixin template forwardToMember(alias member, methods...)
{
    import std.algorithm : among;
    import std.traits : ParameterTypeTuple;
    template opDispatch(string sym)
    if ((methods.length == 0 || sym.among(methods)))
    {
        auto ref opDispatch(
            ParameterTypeTuple!(__traits(getMember, member, sym)) args)
        {
            return __traits(getMember, member, sym)(args);
        }
    }
}

So now ref returns are preserved and the mixin is self-contained (doesn't require imports from the outside).

Compared to my solution, this has the advantage that if the child defines a method, it will take precedence over the formatted one. So that allowed me to add a feature: if no methods are specified, all are forwarded.

There are a couple of ways in which this could and should be improved, most notably overloads control. Even as is it's pretty darn awesome, Meta could you please make it into a pull request? I think it should go in std.functional.


Andrei

May 03, 2015
On Sunday, 3 May 2015 at 18:54:45 UTC, Andrei Alexandrescu wrote:
> I think it should go in std.functional.

All similar utilities are currently in std.typecons
May 03, 2015
On 5/3/15 12:04 PM, Dicebot wrote:
> On Sunday, 3 May 2015 at 18:54:45 UTC, Andrei Alexandrescu wrote:
>> I think it should go in std.functional.
>
> All similar utilities are currently in std.typecons

worksforme -- Andrei
May 03, 2015
On 5/3/15 11:54 AM, Andrei Alexandrescu wrote:
> On 5/3/15 12:18 AM, Meta wrote:
>> On Sunday, 3 May 2015 at 05:49:52 UTC, Andrei Alexandrescu wrote:
>>> On 5/2/15 10:00 PM, Meta wrote:
>>>> It seems like it'd be a lot cheaper and cleaner to just be able to
>>>> alias
>>>> the parent method.
>>>
>>> Yeh, that's the first solution that comes to mind. alias doesn't work
>>> here but of course we could change the language.
>>>
>>>> Also, it could probably be made a bit simpler with
>>>> opDispatch.
>>>
>>> I'd have to see the code, but my intuition is that things could get
>>> quite a bit more hairy.
>>>
>>>
>>> Andrei
>>
>> IMO, using __traits and opDispatch is a fair bit cleaner, and I prefer
>> the syntax of a mixin template to regular mixin.
>>
>> http://dpaste.dzfl.pl/d60498246577
>
> You're right, that is lovely! I've improved it as follows:
>
> mixin template forwardToMember(alias member, methods...)
> {
>      import std.algorithm : among;
>      import std.traits : ParameterTypeTuple;
>      template opDispatch(string sym)
>      if ((methods.length == 0 || sym.among(methods)))
>      {
>          auto ref opDispatch(
>              ParameterTypeTuple!(__traits(getMember, member, sym)) args)
>          {
>              return __traits(getMember, member, sym)(args);
>          }
>      }
> }
>
> So now ref returns are preserved and the mixin is self-contained
> (doesn't require imports from the outside).
>
> Compared to my solution, this has the advantage that if the child
> defines a method, it will take precedence over the formatted one. So
> that allowed me to add a feature: if no methods are specified, all are
> forwarded.
>
> There are a couple of ways in which this could and should be improved,
> most notably overloads control. Even as is it's pretty darn awesome,
> Meta could you please make it into a pull request? I think it should go
> in std.functional.

Take that back, the opDispatch-based solution has a fatal flaw: doesn't compose properly. For example, in std.allocator it's frequent that allocators stack on top of one another, so one forwards a method call to another one which in turn forwards to another.

With code generation this obviously works out of the box because the generated code is identical to what one would write by hand to achieve the same. But the opDispatch-based solution only works one level.


Andrei

May 03, 2015
OK, here's what I have now - two templates that are self contained and work well:

The first uses opDispatch to dispatch to a member. The second is a simple string function that generates the appropriate code. As discussed the latter composes but the former doesn't.

mixin template dispatchToMember(alias member, methods...)
{
    import std.algorithm : among;
    import std.traits : ParameterTypeTuple;
    template opDispatch(string sym)
    if ((methods.length == 0 || sym.among(methods)))
    {
        auto ref opDispatch(ParameterTypeTuple!(__traits(getMember, member, sym)) args)
        {
            return __traits(getMember, member, sym)(args);
        }
    }
}

string forwardToMember(string member, string[] funs...)
{
    string result = "    import std.traits : hasMember, ParameterTypeTuple;\n";
    foreach (fun; funs)
    {
        result ~= "
    static if (hasMember!(typeof("~member~"), `"~fun~"`))
    auto "~fun~"(ParameterTypeTuple!(typeof("~member~"."~fun~")) args)
    {
        return "~member~"."~fun~"(args);
    }\n";
    }
    return result;
}


Andrei
May 03, 2015
On Sunday, 3 May 2015 at 20:25:45 UTC, Andrei Alexandrescu wrote:
> OK, here's what I have now - two templates that are self contained and work well:
>
> The first uses opDispatch to dispatch to a member. The second is a simple string function that generates the appropriate code. As discussed the latter composes but the former doesn't.

I can't picture what you're talking about. Could you post an example?
May 03, 2015
On 5/3/15 4:20 PM, Meta wrote:
> On Sunday, 3 May 2015 at 20:25:45 UTC, Andrei Alexandrescu wrote:
>> OK, here's what I have now - two templates that are self contained and
>> work well:
>>
>> The first uses opDispatch to dispatch to a member. The second is a
>> simple string function that generates the appropriate code. As
>> discussed the latter composes but the former doesn't.
>
> I can't picture what you're talking about. Could you post an example?

struct A
{
   void fun(int);
}

struct B
{
    A a;
    mixin forwardToMember!(a, "fun");
}

struct C
{
    B b;
    mixin forwardToMember!(b, "fun");
}


Andrei
May 04, 2015
On Sunday, 3 May 2015 at 23:54:49 UTC, Andrei Alexandrescu wrote:
> On 5/3/15 4:20 PM, Meta wrote:
>> On Sunday, 3 May 2015 at 20:25:45 UTC, Andrei Alexandrescu wrote:
>>> OK, here's what I have now - two templates that are self contained and
>>> work well:
>>>
>>> The first uses opDispatch to dispatch to a member. The second is a
>>> simple string function that generates the appropriate code. As
>>> discussed the latter composes but the former doesn't.
>>
>> I can't picture what you're talking about. Could you post an example?
>
> struct A
> {
>    void fun(int);
> }
>
> struct B
> {
>     A a;
>     mixin forwardToMember!(a, "fun");
> }
>
> struct C
> {
>     B b;
>     mixin forwardToMember!(b, "fun");
> }
>
>
> Andrei

I still don't get it. Your example works with both dispatchToMember and forwardToMember. What is the difference between these two exactly?

auto opDispatch(string sym: foo)(ParemeterTypeTuple!(/*etc*/) args)
{
    return parent.foo(args);
}

//Automatically generated
auto foo(ParameterTypeTuple!(/*etc*/) args)
{
    return parent.foo(args);
}


May 04, 2015
On 5/3/15 5:29 PM, Meta wrote:
> On Sunday, 3 May 2015 at 23:54:49 UTC, Andrei Alexandrescu wrote:
>> On 5/3/15 4:20 PM, Meta wrote:
>>> On Sunday, 3 May 2015 at 20:25:45 UTC, Andrei Alexandrescu wrote:
>>>> OK, here's what I have now - two templates that are self contained and
>>>> work well:
>>>>
>>>> The first uses opDispatch to dispatch to a member. The second is a
>>>> simple string function that generates the appropriate code. As
>>>> discussed the latter composes but the former doesn't.
>>>
>>> I can't picture what you're talking about. Could you post an example?
>>
>> struct A
>> {
>>    void fun(int);
>> }
>>
>> struct B
>> {
>>     A a;
>>     mixin forwardToMember!(a, "fun");
>> }
>>
>> struct C
>> {
>>     B b;
>>     mixin forwardToMember!(b, "fun");
>> }
>>
>>
>> Andrei
>
> I still don't get it. Your example works with both dispatchToMember and
> forwardToMember. What is the difference between these two exactly?
>
> auto opDispatch(string sym: foo)(ParemeterTypeTuple!(/*etc*/) args)
> {
>      return parent.foo(args);
> }
>
> //Automatically generated
> auto foo(ParameterTypeTuple!(/*etc*/) args)
> {
>      return parent.foo(args);
> }

Hmm, I didn't try it assuming it won't work (thought __traits(getMember, member, sym) would fail with opDispatch). Tried it just now, it does work like a charm. Thanks!

Andrei