www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Taking the address of an eponymous template

reply Arafel <er.krali gmail.com> writes:
Hi!

I want to create a delegate out of a method that happens to be an 
eponymous (nested) template, like this:

```
class C {
	void foo() {}
	void bar(string S)() { }
	template baz(string S) {
		void baz()() {
		}
	}
}

void main() {
	C c = new C();
	void delegate() aFoo = &c.foo;
	void delegate() aBar = &c.bar!"a";
	void delegate() aBaz = &c.baz!"a"; // This doesn't compile.
}
```

If I try &c.baz!"a".baz it doesn't work either (I get a different error 
message.

Do you know if this works (and if so, what should I do), or if it's 
supposed to?

Of course in this case I don't need to use an eponymous template at all, 
bit it's just a simplification to try to get everything else out of the 
way...

In case anyone is interested, the real case is something more like this:

```
class C {
	template baz(args...) if (someCondition!args) {
		void baz(this T) {
		}
	}
}
```

As far as I know, that's the only way to combine a "this" template 
parameter with variadic template parameters.

As usual, thanks for the great support, D hast got a great community!

P.S.: When the function inside the eponymous template is not templated 
itself, then it does work:

´´´
class C {
	template baz(string S) {
		void baz() {
		}
	}
}

void main() {
	C c = new C();
	auto aBaZ = &c.baz!"a";
}
´´´
Jul 31 2017
parent reply ag0aep6g <anonymous example.com> writes:
On 07/31/2017 11:44 AM, Arafel wrote:
 ```
 class C {
[...]
      template baz(string S) {
          void baz()() {
          }
      }
 }
 
 void main() {
[...]
      void delegate() aBaz = &c.baz!"a"; // This doesn't compile.
 }
 ```
 
 If I try &c.baz!"a".baz it doesn't work either (I get a different error 
 message.
 
 Do you know if this works (and if so, what should I do), or if it's 
 supposed to?
You'd have to instantiate the inner template, too. Something like `&c.baz!"a".baz!()`, but that doesn't work. I don't know how you could make it work.
 Of course in this case I don't need to use an eponymous template at all, 
 bit it's just a simplification to try to get everything else out of the 
 way...
 
 In case anyone is interested, the real case is something more like this:
 
 ```
 class C {
      template baz(args...) if (someCondition!args) {
          void baz(this T) {
          }
      }
 }
 ```
(Assuming the inner baz is supposed to be `void baz(this T)() {}`.) You'd still have to instantiate the inner baz in order to get a delegate of it. But even if we figured out how to do that, my guess is you don't want to specify `this T` explicitly. So how about a function literal: void delegate() aBaz = () => c.baz!(int, float)();
 As far as I know, that's the only way to combine a "this" template 
 parameter with variadic template parameters.
That's right if you want to pass `args` explicitly, but `this` implicitly. If specifying `args` via IFTI is an option, then this works, too: ---- class C { void baz(this T, args...)(args) {} } void main() { C c = new C(); void delegate() aBaz = () => c.baz(1, 2.3, "four"); } ---- A function literal again, because you have to call baz in order to instantiate it (or you have specify `this T` and `args` explicitly). But you can't get a delegate from a call.
Jul 31 2017
parent reply Arafel <er.krali gmail.com> writes:
On 07/31/2017 12:14 PM, ag0aep6g wrote:

 You'd have to instantiate the inner template, too. Something like
 `&c.baz!"a".baz!()`, but that doesn't work. I don't know how you could
 make it work.
I tried this as well, and couldn't make it work either. Do you know if it's supposed to work? I mean, do the spec mention this? The funny part is that id does work with nested, non-eponymous templates: ``` class A { template foo(string S) { void bar()() { import std.stdio; writeln ("I'm bar"); } void baz(this T)() { import std.stdio; writeln ("I'm baz at class ", typeid(T)); } } } class B : A { } void main() { A a = new A(); B b = new B(); void delegate() aBar = &a.foo!("a").bar!(); aBar(); void delegate() aBaz = &a.foo!("a").baz!(typeof (a)); aBaz(); void delegate() bBaz = &b.foo!("a").baz!(typeof (b)); bBaz(); } ``` OK, not directly with the "this" parameter... those you have to include explicitly. However, this seems to be an unrelated problem: the "this T" parameter seems to be only automatically deducted during function calls. Even this doesn't work: ``` class A { template foo(this T) { void bar() { import std.stdio; writeln(typeid(T)); } } } void main() { A a = new A(); a.foo.bar(); } ``` But I think that's a completely separate issue (would that be a bug, btw?) It's of course a trivial issue here, but it's just an example. What use is it to allow "this T" parameters in raw template declarations if they are not going to be automatically filled?
 (Assuming the inner baz is supposed to be `void baz(this T)() {}`.)
Sure :-)
 You'd still have to instantiate the inner baz in order to get a delegate
 of it. But even if we figured out how to do that, my guess is you don't
 want to specify `this T` explicitly.

 So how about a function literal:

      void delegate() aBaz = () => c.baz!(int, float)();
Yeah, that's the solution I was thinking about, but I don't know how much of a performance hit the extra function call would be... would the function literal extra indirection layer be eventually optimised out?
 That's right if you want to pass `args` explicitly, but `this`
 implicitly. If specifying `args` via IFTI is an option, then this works,
 too:

 ----
 class C {
      void baz(this T, args...)(args) {}
 }

 void main() {
      C c = new C();
      void delegate() aBaz = () => c.baz(1, 2.3, "four");
 }
 ----

 A function literal again, because you have to call baz in order to
 instantiate it (or you have specify `this T` and `args` explicitly). But
 you can't get a delegate from a call.
This wouldn't work in my case because the arguments ("args") are string themselves, so the function call needs to look like: c.baz!("one","two"); and not: c.baz("one","two"); The reasons for that are a bit more complex, but let's say that in this case I need the strings to be in the template parameters, I use those strings to create an AliasSeq of values of different types that is then sent to a "proper" variadic templated function. Off topic, if anyone knows how to create a va_list dynamically, that would save me a lot of problems!!
Jul 31 2017
parent ag0aep6g <anonymous example.com> writes:
On 07/31/2017 01:59 PM, Arafel wrote:
 On 07/31/2017 12:14 PM, ag0aep6g wrote:
[...]
  > You'd have to instantiate the inner template, too. Something like
  > `&c.baz!"a".baz!()`, but that doesn't work. I don't know how you could
  > make it work.
  >
 I tried this as well, and couldn't make it work either. Do you know if 
 it's supposed to work? I mean, do the spec mention this?
I don't think that exact syntax is supposed to work. When you instantiate a non-eponymous template, you get a template instantiation with members which you can access. With an eponymous template, you get the eponymous member, nothing else. There's no way to get the members of the template by name then. Ideally, I'd say this would work: `&c.baz!"a"!()`. But that's not valid syntax. With a free template, you can make an alias of the first instantiation and then use that to instantiate the second time: ---- template baz(string S) { void baz()() {} } alias baz1 = baz!"a"; void function() aBaz = &baz1!(); ---- Could this work when the template is a class member? ---- class C { template baz(string S) { void baz()() {} } } void main() { C c = new C(); alias baz1 = c.baz!"a"; // void delegate() aBaz = &baz1!(); // doesn't work pragma(msg, typeof(&baz1!())); // void function() pure nothrow nogc safe } ---- We see that `&baz!()` is a function pointer, not a delegate. Looks like `this` got lost along the way. No idea if this is a bug or expected. It might be possible to hack around the issue by building the delegate manually: ---- C c = new C(); alias baz1 = c.baz!"a"; void delegate() aBaz; aBaz.funcptr = &baz1!(); aBaz.ptr = cast(void*) c; ---- That *seems* to work, but I'm not at all sure if it's actually correct. [...]
 OK, not directly with the "this" parameter... those you have to include 
 explicitly. However, this seems to be an unrelated problem: the "this T" 
 parameter seems to be only automatically deducted during function calls. 
 Even this doesn't work:
 
 ```
 class A {
      template foo(this T) {
          void bar() {
              import std.stdio;
              writeln(typeid(T));
          }
      }
 }
 
 void main() {
      A a = new A();
      a.foo.bar();
 }
 ```
 
 But I think that's a completely separate issue (would that be a bug, 
 btw?) It's of course a trivial issue here, but it's just an example.
 
 What use is it to allow "this T" parameters in raw template declarations 
 if they are not going to be automatically filled?
I don't think it's a bug. The spec says that "TemplateThisParameters are used in member function templates" [1]. And it only shows the feature working with calls. So that's apparently the only case that has been considered and is expected to work. It might be worthwhile to explore if the feature can be expanded beyond calls. No idea what the difficulties would be. [...]
  > So how about a function literal:
  >
  >      void delegate() aBaz = () => c.baz!(int, float)();
  >
 Yeah, that's the solution I was thinking about, but I don't know how 
 much of a performance hit the extra function call would be... would the 
 function literal extra indirection layer be eventually optimised out?
I don't know. [1] https://dlang.org/spec/template.html#template_this_parameter
Jul 31 2017