Jump to page: 1 2 3
Thread overview
class scope virtual template functions
May 29, 2008
BCS
May 29, 2008
Walter Bright
May 29, 2008
BCS
May 29, 2008
Walter Bright
May 29, 2008
BCS
Jun 14, 2008
Bruno Medeiros
Jun 15, 2008
BCS
May 30, 2008
janderson
May 30, 2008
Chris Wright
May 30, 2008
janderson
May 31, 2008
Chris Wright
May 31, 2008
janderson
May 31, 2008
Chris Wright
Jun 01, 2008
janderson
May 30, 2008
BCS
May 29, 2008
Jason House
May 29, 2008
BCS
May 29, 2008
Jason House
May 29, 2008
BCS
May 29, 2008
Chris Wright
May 30, 2008
BCS
May 29, 2008
Somewhere IIRC the spec says that classes can't have template methods that operate as virtual functions. This is because it would quickly become a nightmare trying to put together the v-table because only the linker would really know how many different function are in it.

However in highly limited cases, this is not an issue. 

1) the template members are enumerations (just build the whole set)
2) the function has no parameters (only one version can be built)
3) no general form of the template exists, only explicit specializations (ditto enum)
4) class scope aliases of member templates are used. (instance the template and stuff it in as a normal function)

There is also an interesting possibility suggested by #2: allow a template with no args that is penalized for the actual type of the specific object

Example:

class A
{
 char[] Name(this)()  // "this" is a pseudo arg
 { return typeof(this).stringof; }
}
class B : A {}

writef("%s\n", (new A).Name); // outputs "A"
writef("%s\n", (new B).Name); // outputs "B"

Thoughts?

I have some thoughts on how to use this functionality, but it's to ill-formed at this time to fit in this margin.


May 29, 2008
BCS wrote:
> However in highly limited cases, this is not an issue.
> 1) the template members are enumerations (just build the whole set)
> 2) the function has no parameters (only one version can be built)
> 3) no general form of the template exists, only explicit specializations (ditto enum)
> 4) class scope aliases of member templates are used. (instance the template and stuff it in as a normal function)

True, but now you have the issue that minor, seemingly innocuous changes to a template function can have dramatic changes to its behavior. It's a lot easier to understand "template functions are not virtual" than "template functions are not virtual except in these rather complex scenarios."

If you need virtual behavior from a template function, the best way is to wrap a virtual function call within it.
May 29, 2008
Reply to Walter,

> BCS wrote:
> 
>> However in highly limited cases, this is not an issue.
>> 1) the template members are enumerations (just build the whole set)
>> 2) the function has no parameters (only one version can be built)
>> 3) no general form of the template exists, only explicit
>> specializations
>> (ditto enum)
>> 4) class scope aliases of member templates are used. (instance the
>> template and stuff it in as a normal function)
> True, but now you have the issue that minor, seemingly innocuous
> changes to a template function can have dramatic changes to its
> behavior. It's a lot easier to understand "template functions are not
> virtual" than "template functions are not virtual except in these
> rather complex scenarios."
> 
> If you need virtual behavior from a template function, the best way is
> to wrap a virtual function call within it.
> 

in my case, I need this syntax

this.Templet!("foo")(arg)

to be virtual.

The use case is inside of dparse where I have template code calling template code.


May 29, 2008
BCS wrote:
> in my case, I need this syntax
> 
> this.Templet!("foo")(arg)
> 
> to be virtual.
> 
> The use case is inside of dparse where I have template code calling template code.

You could try parameterizing the class enclosing the template, rather than the template.
May 29, 2008
Reply to Walter,

> BCS wrote:
> 
>> in my case, I need this syntax
>> 
>> this.Templet!("foo")(arg)
>> 
>> to be virtual.
>> 
>> The use case is inside of dparse where I have template code calling
>> template code.
>> 
> You could try parameterizing the class enclosing the template, rather
> than the template.
> 

I need to be able to have more than one version of the template

for a fuller example

class A
{
 void Template(char[] s: "foo")(int i) {writef("Hello %d\n", i);}
 void Template(char[] s: "bob")(int i) {writef("See ya %d\n", i);}
}

class B : A
{
 void Template(char[] s: "foo")(int i) {for(int j=0;j<i;j++) writef("Hello\n");}
 void Template(char[] s: "bob")(int i) {for(int j=0;j<i;j++) writef("See ya\n");}}
}

A a = someA();
a.Template!("foo")(5);
a.Template!("bob")(5); // same a, same class different function


the actual used code looks something like this:

void CallIt(T,char[] str)(T it)
{
 it.Template!(str)(arg)
}

(The reason for that form is rather complicated and irrelevant to this thread)


May 29, 2008
Walter Bright Wrote:
> 
> True, but now you have the issue that minor, seemingly innocuous changes to a template function can have dramatic changes to its behavior. It's a lot easier to understand "template functions are not virtual" than "template functions are not virtual except in these rather complex scenarios."

I'd prefer the compiler to require non-virtual functions to be declared final or private. This then allows the compiler to nority the programmer of limitations instead of the spec. It'd also open the door for proposals like this one...
May 29, 2008
Reply to Jason,


> I'd prefer the compiler to require non-virtual functions to be
> declared final or private. This then allows the compiler to nority the
> programmer of limitations instead of the spec. It'd also open the door
> for proposals like this one...
> 

I agree with the suggestion for both reasons


May 29, 2008
 > 1) the template members are enumerations (just build the whole set)

Much to my frustration, D's enums do not represent a completely enumerated set. This causes all kinds of problems such as invalidating your example, preventing a default toString method, and mangling otherwise clean switch statements.
May 29, 2008
Reply to Jason,

>> 1) the template members are enumerations (just build the whole set)
>> 
> Much to my frustration, D's enums do not represent a completely
> enumerated set. This causes all kinds of problems such as invalidating
> your example, preventing a default toString method, and mangling
> otherwise clean switch statements.
> 

Just because /you and I/ can't get the full set, doesn't say the compiler can't. (but point taken)

OTOH using enums as OR'ed bit flags or having more than one element with the same value sinks it just as sunk.


May 29, 2008
BCS wrote:
> Somewhere IIRC the spec says that classes can't have template methods that operate as virtual functions. This is because it would quickly become a nightmare trying to put together the v-table because only the linker would really know how many different function are in it.

You also have to be sure to instantiate all overrides of the template. But these are manageable, even so. You can keep a list of instantiations for each template alongside the object file, plus a list of overrides. Then as a pre-linking step, you instantiate them all.

This would be a HUGE advantage. Want to support user-defined interceptors on whatever your library is doing? If your library does any nontrivial stuff at compiletime, you can't. Not without virtual templates.

> However in highly limited cases, this is not an issue.
> 1) the template members are enumerations (just build the whole set)
> 2) the function has no parameters (only one version can be built)
> 3) no general form of the template exists, only explicit specializations (ditto enum)
> 4) class scope aliases of member templates are used. (instance the template and stuff it in as a normal function)

You can just use a switch statement, but that gets unmaintainable before too long.

> There is also an interesting possibility suggested by #2: allow a template with no args that is penalized for the actual type of the specific object
> 
> Example:
> 
> class A
> {
>  char[] Name(this)()  // "this" is a pseudo arg
>  { return typeof(this).stringof; }
> }
> class B : A {}
> 
> writef("%s\n", (new A).Name); // outputs "A"
> writef("%s\n", (new B).Name); // outputs "B"
> 
> Thoughts?
> 
> I have some thoughts on how to use this functionality, but it's to ill-formed at this time to fit in this margin.

Dconstructor needs this. My dependency injection library has the following basic API:

class Builder
{
	T get(T)();
}

The best way I've seen to allow user-defined interceptors is:
class Builder (TInterceptors...)
{
	// Actually working with this is quite a pain.
	private TInterceptors _interceptors;
	
	T get (T) ()
	{
		foreach (interceptor; _interceptors)
		{
			if (T t = interceptor.handle!(T)(this))
			{
				return t;
			}
		}
		// ...
	}
}

This isn't very good; you can't add or remove interceptors at runtime, and you need to add all the interceptors at the same place. Plus it's really ugly to work with a type tuple variable.

I'd rather an interface like:
interface IBuildInterceptor
{
	T handle(T)(IBuilder builder);
}

interface IBuilder
{
	T get(T)();
	void addInterceptor(IBuildInterceptor interceptor);
}

In .NET or Java, you wouldn't think twice about how to create such an interface. In D, you don't even think once; you know it's impossible.
« First   ‹ Prev
1 2 3