Thread overview
Templatized delegates
May 31, 2022
Andrey Zherikov
May 31, 2022
Paul Backus
May 31, 2022
Andrey Zherikov
Jun 01, 2022
Andrey Zherikov
May 31, 2022
Christian Köstlin
May 31, 2022
Andrey Zherikov
May 31, 2022

I have tightly coupled code which I'd like to decouple but I'm a bit stuck.
For simplicity, I reduced the amount of code to something simple to understand. So I have a struct S that has templated member function that does something. On the other side I have delegate holder R - this delegate takes S object as an argument and calls that function. My goal is to remove dependency from R to S.

Here is code example:

import std.stdio: writeln;

struct S
{
    // function that should be called from delegate
    void doSomething(T)(T value) { value.writeln; }
}

alias DG = void delegate (ref S s);

auto createDG(T)(T value)
{
    return delegate (ref S s) { s.doSomething(value); };
}

struct R
{
    DG dg;

    this(int value)
    {
        dg = createDG(value);
    }
}

void main()
{
    auto r = R(5);

    S s;
    r.dg(s);
}

An obvious way is to add a type template parameter to R, DG and createDG but I would like to avoid this. Is there another way to do so?

I think ideal solution would be having templatized delegate void delegate (S)(ref S s) and then call r.dg!S(s) but it's not available: alias DG = void delegate (S) (ref S s) gives unclear Error: function declaration without return type. (Note that constructors are always named 'this') message.

May 31, 2022

On Tuesday, 31 May 2022 at 21:15:24 UTC, Andrey Zherikov wrote:

>

I have tightly coupled code which I'd like to decouple but I'm a bit stuck.
For simplicity, I reduced the amount of code to something simple to understand. So I have a struct S that has templated member function that does something. On the other side I have delegate holder R - this delegate takes S object as an argument and calls that function. My goal is to remove dependency from R to S.
[...]
An obvious way is to add a type template parameter to R, DG and createDG but I would like to avoid this. Is there another way to do so?

If you want compile-time polymorphism, three's no other way. If you're ok with runtime polymorphism, you could replace S with something like an interface or a sum type. For example:

import std.stdio;

struct S
{
    // function that should be called from delegate
    void doSomething(T)(T value) { value.writeln; }
}

interface I
{
    void doSomething(int value);
}

class Adapter : I
{
    S s;
    this(S s) { this.s = s; }
    void doSomething(int value) { s.doSomething(value); }
}

alias DG = void delegate (I i);

auto createDG(int value)
{
    return delegate (I i) { i.doSomething(value); };
}

struct R
{
    DG dg;

    this(int value)
    {
        dg = createDG(value);
    }
}

void main()
{
    auto r = R(5);

    S s;
    r.dg(new Adapter(s));
}
June 01, 2022

On 2022-05-31 23:15, Andrey Zherikov wrote:

>

I have tightly coupled code which I'd like to decouple but I'm a bit stuck.
For simplicity, I reduced the amount of code to something simple to understand. So I have a struct S that has templated member function that does something. On the other side I have delegate holder R - this delegate takes S object as an argument and calls that function. My goal is to remove dependency from R to S.

Here is code example:

import std.stdio: writeln;

struct S
{
     // function that should be called from delegate
     void doSomething(T)(T value) { value.writeln; }
}

alias DG = void delegate (ref S s);

auto createDG(T)(T value)
{
     return delegate (ref S s) { s.doSomething(value); };
}

struct R
{
     DG dg;

     this(int value)
     {
         dg = createDG(value);
     }
}

void main()
{
     auto r = R(5);

     S s;
     r.dg(s);
}

An obvious way is to add a type template parameter to R, DG and createDG but I would like to avoid this. Is there another way to do so?

I think ideal solution would be having templatized delegate void delegate (S)(ref S s) and then call r.dg!S(s) but it's not available: alias DG = void delegate (S) (ref S s) gives unclear Error: function declaration without return type. (Note that constructors are always named 'this') message.Would it help to not create the delegate in R's constructor, but feed
the delegate into it (and create the delegate outside). This would also resemble your description (R is a delegate holder) more.

May 31, 2022

On Tuesday, 31 May 2022 at 21:53:17 UTC, Paul Backus wrote:

>

If you want compile-time polymorphism, three's no other way.

Yes, I want compile-time polymorphism.

>
import std.stdio;

struct S
{
    // function that should be called from delegate
    void doSomething(T)(T value) { value.writeln; }
}

interface I
{
    void doSomething(int value);
}

class Adapter : I
{
    S s;
    this(S s) { this.s = s; }
    void doSomething(int value) { s.doSomething(value); }
}

alias DG = void delegate (I i);

auto createDG(int value)
{
    return delegate (I i) { i.doSomething(value); };
}

struct R
{
    DG dg;

    this(int value)
    {
        dg = createDG(value);
    }
}

void main()
{
    auto r = R(5);

    S s;
    r.dg(new Adapter(s));
}

Unfortunately this solution looses important feature: there is no more templated call to S.doSomething as I.doSomething is not template and adding override for every type is not maintainable.

May 31, 2022

On Tuesday, 31 May 2022 at 22:34:41 UTC, Christian Köstlin wrote:

>

Would it help to not create the delegate in R's constructor, but feed
the delegate into it (and create the delegate outside). This would also resemble your description (R is a delegate holder) more.

That's possible but the problem is that S is part of DG.

June 01, 2022

On Tuesday, 31 May 2022 at 23:15:24 UTC, Andrey Zherikov wrote:

>

On Tuesday, 31 May 2022 at 21:53:17 UTC, Paul Backus wrote:

>

If you want compile-time polymorphism, three's no other way.

Yes, I want compile-time polymorphism.

In case if S.doSomething is NOT template function then the problem can be solved easily by wrapping access to S into another delegate:

import std.stdio: writeln;

struct S
{
    void doSomething(int value) { value.writeln; }
}

alias DG = void delegate (void delegate(int) doSomething);

auto createDG(T)(T value)
{
    return delegate (void delegate(int) doSomething) { doSomething(value); };
}

struct R
{
    DG dg;

    this(int value)
    {
        dg = createDG(value);
    }
}

void main()
{
    auto r = R(5);

    S s;
    r.dg(_ => s.doSomething(_));
}

How to do the same for templated S.doSomething?