Thread overview
Taking the address of an eponymous template
Jul 31, 2017
Arafel
Jul 31, 2017
ag0aep6g
Jul 31, 2017
Arafel
Jul 31, 2017
ag0aep6g
July 31, 2017
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";
}
´´´

July 31, 2017
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.
July 31, 2017
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!!
July 31, 2017
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