www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Templatized delegates

reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
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:

```d
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
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
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: ```d 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)); } ```
May 31 2022
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
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.
 ```d
 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
parent Andrey Zherikov <andrey.zherikov gmail.com> writes:
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: ```d 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`?
May 31 2022
prev sibling parent reply =?UTF-8?Q?Christian_K=c3=b6stlin?= <christian.koestlin gmail.com> writes:
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:
 
 ```d
 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
parent Andrey Zherikov <andrey.zherikov gmail.com> writes:
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`.
May 31 2022