www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - templatized delegate

reply Alex <sascha.orlov gmail.com> writes:
Hi, all
I have a question, how to handle a templated delegate. However, 
I'm not sure, if I'm going in the right direction, so I have 
three examples, and my question is about the third.

1. Here, a struct with an alias is defined and on its creation 
the delegate get known to the struct. Everything is ok.

struct A(alias dg)
{
	auto fun(int i, string s)
	{
		return dg(i, s);
	}
}

void main()
{
	
	auto dlg(int i, string s)
	{
		import std.stdio;
		writeln(i, " ", s);
	}
	auto a = A!dlg();
	a.fun(5, "a");
}

2. Now, I want to store the delegate in another struct. If I want 
to do this, I have to define the pointer as static. This is not 
intended at the beginning, but it's ok, as I know, that the 
delegate would be the same across all instances of B.

struct A(alias dg)
{
	auto fun(int i, string s)
	{
		return dg(i, s);
	}
}

struct B
{
	A!dlgptr a;
	static void delegate(int, string) dlgptr; // here, without the 
"static" "need 'this' to access member dlgptr" rises
	this(void delegate(int, string) dlg)
	{
		dlgptr = dlg;
	}
	void fun(int i, string s)
	{
		a.fun(i, s);
	}
}

void main()
{
	auto dlg(int i, string s)
	{
		import std.stdio;
		writeln(i, " ", s);
	}
	auto b = B(&dlg);
	b.fun(5, "a");
}

3. Now the hard stuff comes. I want to templatize my delegate.
struct A(alias dg)
{
	auto fun(T, U...)(T t, U u)
	{
		return dg!(T, U)(t, u);
	}
}

struct C
{
	A!dlgptr a;
	
	/* static? */ template dlgptr(T, U...)
	{
		/* static? */ void delegate(T, U) dlgptr;
	}

	this(???)
	{
		???
	}
	
	void fun(T, U...)(T t, U args)
	{
		dlgptr!(T, U)(t, args);
	}
}

void main()
{
	auto dlg(T, U...)(T t, U u)
	{
		import std.stdio;
		writeln(t, " ", u);
	}
	
	auto c = C(???);
	c.fun(5, "a"); // exception, obviously, as C is not initialized 
properly
	
	A!dlg a;
	a.fun(5, "a"); //Error: function test92.A!(dlg).A.fun!(int, 
string).fun cannot get frame pointer to test92.main.dlg!(int, 
string).dlg
}

Here, nothing works any more... I have no idea, what to pass to 
the struct C, as a template is not an lvalue. But even the direct 
initialization of A doesn't work...

By the way, I found
http://forum.dlang.org/post/xiycyjndqzbohjtjfjvh forum.dlang.org
and
http://www.digitalmars.com/d/archives/digitalmars/D/learn/delegate_template_and_alias_31092.html
these both go into the right direction I think... but don't see, 
how to reformulate them...
Thanks in advance :)
May 22
next sibling parent reply Dukc <ajieskola gmail.com> writes:
On Monday, 22 May 2017 at 09:04:15 UTC, Alex wrote:
 2. Now, I want to store the delegate in another struct. If I 
 want to do this, I have to define the pointer as static. This 
 is not intended at the beginning, but it's ok, as I know, that 
 the delegate would be the same across all instances of B.
The reason it does not work with a non-static member is that A generated has no way of knowing where B that contains the delegate to be called resides. But it works when done this way: struct A(alias dg) { auto fun(T...)(T params) { return dg(params); } } struct B { A!((B b, int i, string a) => b.dlgptr(i, a)) a; void delegate(int, string) dlgptr; this(void delegate(int, string) dlg) { dlgptr = dlg; } void fun(int i, string s) { a.fun(this, i, s); } } void main() { auto dlg(int i, string s) { import std.stdio; writeln(i, " ", s); } auto b = B(&dlg); b.fun(5, "a"); } I wasn't in mood to look at C though, so I don't know if this helps.
May 22
parent Alex <sascha.orlov gmail.com> writes:
On Monday, 22 May 2017 at 20:38:27 UTC, Dukc wrote:
 On Monday, 22 May 2017 at 09:04:15 UTC, Alex wrote:
 2. Now, I want to store the delegate in another struct. If I 
 want to do this, I have to define the pointer as static. This 
 is not intended at the beginning, but it's ok, as I know, that 
 the delegate would be the same across all instances of B.
The reason it does not work with a non-static member is that A generated has no way of knowing where B that contains the delegate to be called resides. But it works when done this way: struct A(alias dg) { auto fun(T...)(T params) { return dg(params); } } struct B { A!((B b, int i, string a) => b.dlgptr(i, a)) a; void delegate(int, string) dlgptr; this(void delegate(int, string) dlg) { dlgptr = dlg; } void fun(int i, string s) { a.fun(this, i, s); } } void main() { auto dlg(int i, string s) { import std.stdio; writeln(i, " ", s); } auto b = B(&dlg); b.fun(5, "a"); } I wasn't in mood to look at C though, so I don't know if this helps.
Yes, it does :) The step to the C variant is, how to rewrite the lines A!((B b, int i, string a) => b.dlgptr(i, a)) a; and this(void delegate(int, string) dlg) if I want to define my delegate as a template: template dlgptr(T, U...) { void delegate(T, U) dlgptr; }
May 22
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
On 05/22/2017 11:04 AM, Alex wrote:
 3. Now the hard stuff comes. I want to templatize my delegate.
 struct A(alias dg)
 {
      auto fun(T, U...)(T t, U u)
      {
          return dg!(T, U)(t, u);
      }
 }
 
 struct C
 {
      A!dlgptr a;
 
      /* static? */ template dlgptr(T, U...)
      {
          /* static? */ void delegate(T, U) dlgptr;
      }
 
      this(???)
      {
          ???
      }
 
      void fun(T, U...)(T t, U args)
      {
          dlgptr!(T, U)(t, args);
      }
 }
 
 void main()
 {
      auto dlg(T, U...)(T t, U u)
      {
          import std.stdio;
          writeln(t, " ", u);
      }
 
      auto c = C(???);
      c.fun(5, "a"); // exception, obviously, as C is not initialized 
 properly
 
      A!dlg a;
      a.fun(5, "a"); //Error: function test92.A!(dlg).A.fun!(int, 
 string).fun cannot get frame pointer to test92.main.dlg!(int, string).dlg
 }
 
 Here, nothing works any more... I have no idea, what to pass to the 
 struct C, as a template is not an lvalue.
Not only is a template not an lvalue, it's not any kind of value at all. It doesn't have a type. You can't have a variable holding a template. You can't pass it as an argument. But a template is a symbol. You can pass it in an alias parameter. So to pass dlg to C, you have to make C a template with an alias parameter, like A is.
 But even the direct 
 initialization of A doesn't work...
`A!dlg a;` works. Calling `fun` doesn't. A.fun instantiates dlg, resulting in a delegate that should be able to access main's stuff. But it's not guaranteed that main is active when A.fun is called. You could have returned `a` from main before calling fun. For an actual delegate, a closure would be made in that case. But dlg isn't a delegate, it's a template. I guess it's not possible (or not feasible, or not implemented) to create a closure a template like this. If you don't actually need dlg to access main's stuff, you can make it static. It's a function then and the delegate weirdness doesn't apply. For another approach to your problem, maybe have a look at run-time variadic arguments: https://dlang.org/spec/function.html#d_style_variadic_functions With that kind of variadics, you're not dealing with a template. A (run-time) variadic delegate is an actual delegate, i.e. a value that can be passed around. But the variadic stuff is a bit weird to use, and probably affects performance.
May 22
next sibling parent Alex <sascha.orlov gmail.com> writes:
On Monday, 22 May 2017 at 21:44:17 UTC, ag0aep6g wrote:
 On 05/22/2017 11:04 AM, Alex wrote:
 [...]
Not only is a template not an lvalue, it's not any kind of value at all. It doesn't have a type. You can't have a variable holding a template. You can't pass it as an argument. But a template is a symbol. You can pass it in an alias parameter. So to pass dlg to C, you have to make C a template with an alias parameter, like A is.
Aha... ok, I see...
 [...]
`A!dlg a;` works. Calling `fun` doesn't. A.fun instantiates dlg, resulting in a delegate that should be able to access main's stuff. But it's not guaranteed that main is active when A.fun is called. You could have returned `a` from main before calling fun. For an actual delegate, a closure would be made in that case. But dlg isn't a delegate, it's a template. I guess it's not possible (or not feasible, or not implemented) to create a closure a template like this.
ok, I see the point, I think...
 If you don't actually need dlg to access main's stuff, you can 
 make it static. It's a function then and the delegate weirdness 
 doesn't apply.
yeah... no :) the function inside the main has to have access to the main stuff. Its the pointer inside C which could be static, if this would help. So long, I go for the enhanced second variant...
 For another approach to your problem, maybe have a look at 
 run-time variadic arguments:

 https://dlang.org/spec/function.html#d_style_variadic_functions

 With that kind of variadics, you're not dealing with a 
 template. A (run-time) variadic delegate is an actual delegate, 
 i.e. a value that can be passed around. But the variadic stuff 
 is a bit weird to use, and probably affects performance.
Hm... You are probably right... and variadic functions do not really match the idea...
May 22
prev sibling parent reply Alex <sascha.orlov gmail.com> writes:
On Monday, 22 May 2017 at 21:44:17 UTC, ag0aep6g wrote:
 With that kind of variadics, you're not dealing with a 
 template. A (run-time) variadic delegate is an actual delegate, 
 i.e. a value that can be passed around. But the variadic stuff 
 is a bit weird to use, and probably affects performance.
By the way, I'm not even sure, if variadics work in my case. I have a strange struct of a random generator, which cannot be copied, and I have no idea how to pass it to a variadic function: import std.stdio; import mir.random; void main() { Random rndGen = Random(unpredictableSeed); fun(rndGen); } void fun(...) { } Yields "... is not copyable because it is annotated with disable" :)
May 23
next sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Tuesday, 23 May 2017 at 10:30:56 UTC, Alex wrote:
 On Monday, 22 May 2017 at 21:44:17 UTC, ag0aep6g wrote:
 With that kind of variadics, you're not dealing with a 
 template. A (run-time) variadic delegate is an actual 
 delegate, i.e. a value that can be passed around. But the 
 variadic stuff is a bit weird to use, and probably affects 
 performance.
By the way, I'm not even sure, if variadics work in my case. I have a strange struct of a random generator, which cannot be copied, and I have no idea how to pass it to a variadic function: import std.stdio; import mir.random; void main() { Random rndGen = Random(unpredictableSeed); fun(rndGen); } void fun(...) { } Yields "... is not copyable because it is annotated with disable" :)
Random is copy disabled to prevent incorrect use. You need to pass it by ref or pointer. I dont know if you can pass variables as ref to a variadic, but you should be able to pass it by address. fun(&rndGen);
May 23
next sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Tuesday, 23 May 2017 at 10:42:54 UTC, Nicholas Wilson wrote:
 On Tuesday, 23 May 2017 at 10:30:56 UTC, Alex wrote:
 On Monday, 22 May 2017 at 21:44:17 UTC, ag0aep6g wrote:
 With that kind of variadics, you're not dealing with a 
 template. A (run-time) variadic delegate is an actual 
 delegate, i.e. a value that can be passed around. But the 
 variadic stuff is a bit weird to use, and probably affects 
 performance.
By the way, I'm not even sure, if variadics work in my case. I have a strange struct of a random generator, which cannot be copied, and I have no idea how to pass it to a variadic function: import std.stdio; import mir.random; void main() { Random rndGen = Random(unpredictableSeed); fun(rndGen); } void fun(...) { } Yields "... is not copyable because it is annotated with disable" :)
Random is copy disabled to prevent incorrect use. You need to pass it by ref or pointer. I dont know if you can pass variables as ref to a variadic, but you should be able to pass it by address. fun(&rndGen);
void variadic(Args...)(auto ref Args args) { /* ... */ } This infers whether you pass lvalues or rvalues. If passing further down the chain of such calls is needed, one can use std.functional : fowrard : void variadic(Args...)(auto ref Args args) { import std.functional : forward; doStuff(forward!args); } void doStuff(Args...)(auto ref Args args) { /* ... */ } 'forward' aliases ref arguments (i.e. passed lvalues) and moves value arguments (i.e. passed rvalues). If a value is not copyable, it may be move-able (check the docs though, it may not be that either). void fun(Args...)(auto ref Args args) { /*...*/ } import std.algorithm : move; auto a = NonCopyable(42); fun(move(a)); // or: func(NonCopyable(42));
May 23
parent reply Alex <sascha.orlov gmail.com> writes:
On Tuesday, 23 May 2017 at 11:05:09 UTC, Stanislav Blinov wrote:
 void variadic(Args...)(auto ref Args args) { /* ... */ }

 This infers whether you pass lvalues or rvalues. If passing 
 further down the chain of such calls is needed, one can use 
 std.functional : fowrard :
yes...
 void variadic(Args...)(auto ref Args args) {
     import std.functional : forward;
     doStuff(forward!args);
 }

 void doStuff(Args...)(auto ref Args args) {
     /* ... */
 }

 'forward' aliases ref arguments (i.e. passed lvalues) and moves 
 value arguments (i.e. passed rvalues).

 If a value is not copyable, it may be move-able (check the docs 
 though, it may not be that either).

 void fun(Args...)(auto ref Args args) { /*...*/ }
yes...
 import std.algorithm : move;

 auto a = NonCopyable(42);

 fun(move(a));
 // or:
 func(NonCopyable(42));
the problem, that I have is, that I would like to use the templated approach, but I don't have the function, but only a delegate, so: template(T, U...) { void delegate(ref T neededInput, ref U ignoredInput) dgPtr; } Not sure, if this is possible to handle at all...
May 23
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Tuesday, 23 May 2017 at 11:45:13 UTC, Alex wrote:
 On Tuesday, 23 May 2017 at 11:05:09 UTC, Stanislav Blinov wrote:
 void variadic(Args...)(auto ref Args args) { /* ... */ }

 This infers whether you pass lvalues or rvalues. If passing 
 further down the chain of such calls is needed, one can use 
 std.functional : fowrard :
yes...
 void variadic(Args...)(auto ref Args args) {
     import std.functional : forward;
     doStuff(forward!args);
 }

 void doStuff(Args...)(auto ref Args args) {
     /* ... */
 }

 'forward' aliases ref arguments (i.e. passed lvalues) and 
 moves value arguments (i.e. passed rvalues).

 If a value is not copyable, it may be move-able (check the 
 docs though, it may not be that either).

 void fun(Args...)(auto ref Args args) { /*...*/ }
yes...
 import std.algorithm : move;

 auto a = NonCopyable(42);

 fun(move(a));
 // or:
 func(NonCopyable(42));
the problem, that I have is, that I would like to use the templated approach, but I don't have the function, but only a delegate, so: template(T, U...) { void delegate(ref T neededInput, ref U ignoredInput) dgPtr; } Not sure, if this is possible to handle at all...
Ah, now I think I get it. You want to store a single delegate that could be called with different sets of arguments? No, you can't do that: you need an actual delegate instance, and for that, you need to know the signature, at least when instantiating C.
May 23
parent Alex <sascha.orlov gmail.com> writes:
On Tuesday, 23 May 2017 at 16:38:14 UTC, Stanislav Blinov wrote:
 Ah, now I think I get it. You want to store a single delegate 
 that could be called with different sets of arguments? No, you 
 can't do that: you need an actual delegate instance, and for 
 that, you need to know the signature, at least when 
 instantiating C.
Yes :) or, just to differentiate between the used and unused parameters, without loosing their types and attributes. But that's a minor problem, the solution with full signature https://forum.dlang.org/post/ekbpjsuyqprusyasmjsm forum.dlang.org#post-svvgcrymplmyondhuogt:40forum.dlang.org works also well, as the effort to name everything is not so big in my case...
May 23
prev sibling parent reply Alex <sascha.orlov gmail.com> writes:
On Tuesday, 23 May 2017 at 10:42:54 UTC, Nicholas Wilson wrote:
 On Tuesday, 23 May 2017 at 10:30:56 UTC, Alex wrote:
 On Monday, 22 May 2017 at 21:44:17 UTC, ag0aep6g wrote:
 With that kind of variadics, you're not dealing with a 
 template. A (run-time) variadic delegate is an actual 
 delegate, i.e. a value that can be passed around. But the 
 variadic stuff is a bit weird to use, and probably affects 
 performance.
By the way, I'm not even sure, if variadics work in my case. I have a strange struct of a random generator, which cannot be copied, and I have no idea how to pass it to a variadic function: import std.stdio; import mir.random; void main() { Random rndGen = Random(unpredictableSeed); fun(rndGen); } void fun(...) { } Yields "... is not copyable because it is annotated with disable" :)
Random is copy disabled to prevent incorrect use.
Yes, I'm aware of this...
 You need to pass it by ref or pointer. I dont know if you can 
 pass variables as ref to a variadic, but you should be able to 
 pass it by address.
 fun(&rndGen);
No, you can't pass a ref into a variadic... if the test above is written correct. And no, I can't pass it by adress, as I don't know apriori, whether the very parameter which gets the random generator is already a part of the variadic parameters, or a well defined ref parameter. Especially, there are some functions for both cases. While the argument list remains the same, the acceptor part is meant to work with it somehow. The other way around would be, to manipulate the argument list, which is shared...
May 23
parent reply ag0aep6g <anonymous example.com> writes:
On 05/23/2017 01:30 PM, Alex wrote:
 And no, I can't pass it by adress, as I don't know apriori, whether the 
 very parameter which gets the random generator is already a part of the 
 variadic parameters, or a well defined ref parameter.
A (run-time) variadic delegate isn't flexible like that. When you have a `void delegate(...)`, then there are no non-variadic parameters. You know both in the caller and in the callee that passing by ref is not an option. So you can define that uncopyable types are passed by pointer. Going this route means you have to make all your delegates variadic (this might be annoying). A.fun can remain a variadic template. It can also have ref parameters, but then you have to detect uncopyable types and pass pointers to the delegate. Something like this: ---- import core.vararg; import std.meta: AliasSeq, staticMap; import std.traits: isCopyable; struct A { void delegate(...) dg; auto fun(T, U ...)(T t, auto ref U u) { template UncopyableToPointer(T) { static if (!isCopyable!T) alias UncopyableToPointer = T*; else alias UncopyableToPointer = T; } alias U2 = staticMap!(UncopyableToPointer, U); U2 u2; foreach (i, E; U) { static if (!isCopyable!E) u2[i] = &u[i]; else u2[i] = u[i]; } return dg(t, u2); } } struct SomeStructThatIsNotCopyable { disable this(this); } void main() { void dlg(...) { import std.stdio; foreach (i, t; _arguments) { foreach (T; AliasSeq!(int, string, SomeStructThatIsNotCopyable*)) { if (t == typeid(T)) { static if (is(T : U*, U) && !isCopyable!U) { write("uncopyable type"); } else write(_argptr.va_arg!T); } } /* otherwise: don't know how to handle the type */ write(" "); } writeln(); } auto a = A(&dlg); SomeStructThatIsNotCopyable s; a.fun(5, "a", /* by ref: */ s, /* or by pointer: */ &s); } ---- That's not exactly pretty, of course. Both A.fun and the delegate are quite complicated. But it might be workable, if run-time variadics are acceptable. I wouldn't be surprised if the problem can be solved more elegantly. But I don't see how at the moment.
May 23
parent Alex <sascha.orlov gmail.com> writes:
On Tuesday, 23 May 2017 at 18:14:34 UTC, ag0aep6g wrote:
 Something like this:

 ----
 import core.vararg;
 import std.meta: AliasSeq, staticMap;
 import std.traits: isCopyable;

 struct A
 {
     void delegate(...) dg;
     auto fun(T, U ...)(T t, auto ref U u)
     {
         template UncopyableToPointer(T)
         {
             static if (!isCopyable!T) alias UncopyableToPointer 
 = T*;
             else alias UncopyableToPointer = T;
         }
         alias U2 = staticMap!(UncopyableToPointer, U);

         U2 u2;
         foreach (i, E; U)
         {
             static if (!isCopyable!E) u2[i] = &u[i];
             else u2[i] = u[i];
         }

         return dg(t, u2);
     }
 }

 struct SomeStructThatIsNotCopyable
 {
      disable this(this);
 }

 void main()
 {
     void dlg(...)
     {
         import std.stdio;
         foreach (i, t; _arguments)
         {
             foreach (T; AliasSeq!(int, string,
                 SomeStructThatIsNotCopyable*))
             {
                 if (t == typeid(T))
                 {
                     static if (is(T : U*, U) && !isCopyable!U)
                     {
                         write("uncopyable type");
                     }
                     else write(_argptr.va_arg!T);
                 }
             }
             /* otherwise: don't know how to handle the type */
             write(" ");
         }
         writeln();
     }

     auto a = A(&dlg);
     SomeStructThatIsNotCopyable s;
     a.fun(5, "a", /* by ref: */ s, /* or by pointer: */ &s);
 }
 ----

 That's not exactly pretty, of course. Both A.fun and the 
 delegate are quite complicated. But it might be workable, if 
 run-time variadics are acceptable.

 I wouldn't be surprised if the problem can be solved more 
 elegantly. But I don't see how at the moment.
That's cool :) but anyway, even if I have such params, there are not much of them, so it just a minor semantic issue to explicitely name them and their types instead of showing, that they are passed but not used inside dlg. And thanks a lot to all for great ideas :)
May 23
prev sibling parent 9il <ilyayaroshenko gmail.com> writes:
On Tuesday, 23 May 2017 at 10:30:56 UTC, Alex wrote:
 On Monday, 22 May 2017 at 21:44:17 UTC, ag0aep6g wrote:
 With that kind of variadics, you're not dealing with a 
 template. A (run-time) variadic delegate is an actual 
 delegate, i.e. a value that can be passed around. But the 
 variadic stuff is a bit weird to use, and probably affects 
 performance.
By the way, I'm not even sure, if variadics work in my case. I have a strange struct of a random generator, which cannot be copied, and I have no idea how to pass it to a variadic function: import std.stdio; import mir.random; void main() { Random rndGen = Random(unpredictableSeed); fun(rndGen); } void fun(...) { } Yields "... is not copyable because it is annotated with disable" :)
1. Pass its pointer 2. Use variadic template with auto ref: ``` void foo(T...)(auto ref T tup) { } ```
May 23