Thread overview
Forwarding calls to objects of another type
Apr 10, 2017
Johan Fjeldtvedt
Apr 10, 2017
Basile B.
Apr 11, 2017
Nicholas Wilson
Apr 19, 2017
Johan Fjeldtvedt
Apr 19, 2017
Johan Fjeldtvedt
April 10, 2017
I have a couple of questions related to the following code: https://gist.github.com/Jaffe-/b027287a884fc2e173a65601ec242676

1) This is a very simplified example, but what I'm trying to do here is to call `foo` on each object in `Container.ss` contains when `foo` is called, and likewise for `bar`. To do this without having to repeat the foreach loop in every member function, I made the `call_for_each` template. However, I found no other way to pull this off (without using string mixins) than to also add the `call` template in the S struct. At first I thought it would be possible to write `s.func(args)` in `call_for_each`, but that will try to look up func as a member in the struct.

Is there a more common / idiomatic way of doing this? In C++ this would be solved by using a member function pointer as a template argument, but as far as I understand D uses delegates (which are already bound to an instance) instead?

2) This is about the reduce templates. As I've commented, I can't use a template lambda with reduce, but I can use a lambda taking ints as arguments. Why is this? The error message I get when using the template lambda is:

"template instance reduce!((a, b) => a + b) cannot use local '__lambda1' as parameter to non-global template reduce(alias fun)()"

April 10, 2017
On Monday, 10 April 2017 at 21:04:10 UTC, Johan Fjeldtvedt wrote:
> I have a couple of questions related to the following code: https://gist.github.com/Jaffe-/b027287a884fc2e173a65601ec242676
>
> 1) This is a very simplified example, but what I'm trying to do here is to call `foo` on each object in `Container.ss` contains when `foo` is called, and likewise for `bar`. To do this without having to repeat the foreach loop in every member function, I made the `call_for_each` template. However, I found no other way to pull this off (without using string mixins) than to also add the `call` template in the S struct. At first I thought it would be possible to write `s.func(args)` in `call_for_each`, but that will try to look up func as a member in the struct.
>
> Is there a more common / idiomatic way of doing this? In C++ this would be solved by using a member function pointer as a template argument, but as far as I understand D uses delegates (which are already bound to an instance) instead?

One way:

struct S {
    int x[];

    void foo(int x) {
        this.x ~= x;
    }

    void bar() {
        writeln("Contains: ", x);
    }

    auto call(alias func, T...)(T args) {
        return func(args);
    }

    auto reduce(alias func)() {
        return x.reduce!(func);
    }
}

class Container {
    S[10] ss;

    void dispatch(string func, T...)(T args) {
        foreach(ref s; ss) {
            __traits(getMember, s, func)(args);
        }
    }

    auto reduce(alias func)() {
        return ss[]
            .map!(t => t.reduce!func)
            .reduce!(func);
    }

}

void main() {
    auto test = new Container();

    test.dispatch!"foo"(10);
    test.dispatch!"foo"(20);
    test.dispatch!"foo"(30);
    test.dispatch!"bar"();
    //auto x = test.reduce!((a, b) => a + b);
    auto x = test.reduce!((int a, int b) => a + b);  // Compiles
    writeln(x);
}

Another way would be to take the delegate of one instance and to patch the pointer to the instance for each instance (because the member of a delegate can be changed: .ptr -> instance, .funcptr -> static address of the function). But it's less clean.

> 2) This is about the reduce templates. As I've commented, I can't use a template lambda with reduce, but I can use a lambda taking ints as arguments. Why is this? The error message I get when using the template lambda is:
>
> "template instance reduce!((a, b) => a + b) cannot use local '__lambda1' as parameter to non-global template reduce(alias fun)()"

No idea for this.


April 11, 2017
On Monday, 10 April 2017 at 21:27:34 UTC, Basile B. wrote:
>> 2) This is about the reduce templates. As I've commented, I can't use a template lambda with reduce, but I can use a lambda taking ints as arguments. Why is this? The error message I get when using the template lambda is:
>>
>> "template instance reduce!((a, b) => a + b) cannot use local '__lambda1' as parameter to non-global template reduce(alias fun)()"
>
> No idea for this.

The use of the global identity template will fix this:

see https://blog.thecybershadow.net/2015/04/28/the-amazing-template-that-does-nothing/
April 19, 2017
On Monday, 10 April 2017 at 21:27:34 UTC, Basile B. wrote:
> On Monday, 10 April 2017 at 21:04:10 UTC, Johan Fjeldtvedt wrote:
>> [...]
>
> One way:
>
> [...]

Thanks for the reply. The traits way of doing it seems to be what I want. :)

>> [...]
>
> [...]

April 19, 2017
On Tuesday, 11 April 2017 at 02:01:19 UTC, Nicholas Wilson wrote:
> On Monday, 10 April 2017 at 21:27:34 UTC, Basile B. wrote:
>>> 2) This is about the reduce templates. As I've commented, I can't use a template lambda with reduce, but I can use a lambda taking ints as arguments. Why is this? The error message I get when using the template lambda is:
>>>
>>> "template instance reduce!((a, b) => a + b) cannot use local '__lambda1' as parameter to non-global template reduce(alias fun)()"
>>
>> No idea for this.
>
> The use of the global identity template will fix this:
>
> see https://blog.thecybershadow.net/2015/04/28/the-amazing-template-that-does-nothing/

Thanks, that did work. I think I understand the point about UFCS lookup rules, but it still seems strange (it was at least surprising) that I couldn't use the template (a, b) => a + b in place of (int a, int b) => a + b.