www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - class scope virtual template functions

reply BCS <ao pathlink.com> writes:
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)


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
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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
next sibling parent reply BCS <ao pathlink.com> writes:
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
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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
parent reply BCS <ao pathlink.com> writes:
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
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
BCS wrote:
 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)
How about creating normal methods, and normal method override hierarchy/lineages, and then creating a template that simply selects the desired method lineage, like this: --- --- module test; class A { void Template_foo(int i) {writef("Hello %d\n", i);} void Template_bob(int i) {writef("See ya %d\n", i);} template Template(string s: "foo") { alias Template_foo Template; } template Template(string s: "bob") { alias Template_bob Template; } } class B : A { override void Template_foo(int i) {for(int j=0;j<i;j++) writef("Hello\n");} override void Template_bob(int i) {for(int j=0;j<i;j++) writef("See ya\n");} } void main() { A a = new B(); a.Template!("foo")(5); a.Template!("bob")(5); // same a, same class different function } --- --- If you have many method choices, a mixin could be created to automatically generate all template specializations, such as: mixin TemplateAliases!("Template", ["foo", "bob", "bar", "xpto"]); which would generate lines like: template Template(string s: "foo") { alias Template_foo Template; } etc. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 14 2008
parent BCS <ao pathlink.com> writes:
Reply to Bruno,

 module test;
 class A
 {
 void Template_foo(int i) {writef("Hello %d\n", i);}
 void Template_bob(int i) {writef("See ya %d\n", i);}
 template Template(string s: "foo") { alias Template_foo Template; }
 template Template(string s: "bob") { alias Template_bob Template; }
 }
 class B : A
 {
 override void Template_foo(int i) {for(int j=0;j<i;j++)
 writef("Hello\n");}
 override void Template_bob(int i) {for(int j=0;j<i;j++) writef("See
 ya\n");}
 }
I think that might work in some cases (maybe even mine). However In my cases the called functions are generated so once I need to generate different names for the functions I might as well go with mixins for the calls and then there is no point in using templates. But even if it can be made to work it seems so klugey... Walter: How restrictive would the rules have to be before templates functions could be virtual? For my case I could tag them with an extra attribute, make sure there are no non virtual ones and a lot of other things.
Jun 15 2008
prev sibling parent reply janderson <askme me.com> writes:
BCS wrote:
 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.
You could use a functor/proxy type thing. That is return a separate object for each class level that has opCall overloaded. The opCall could be templated. The functor object probably could be generalized so that you could use it in any case you needed a virtual function (it could callback its owner by template or maybe delegate). Of course that's more complex then a simple template virtual inheritance however it does provide more functionality as well. -Joel
May 29 2008
next sibling parent reply Chris Wright <dhasenan gmail.com> writes:
janderson wrote:
 You could use a functor/proxy type thing.  That is return a separate 
 object for each class level that has opCall overloaded.  The opCall 
 could be templated.  The functor object probably could be generalized so 
 that you could use it in any case you needed a virtual function (it 
 could callback its owner by template or maybe delegate).
I don't understand this. You seem to be suggesting moving the template to a functor and having inheritors of the original class return a different functor. But unless that functor is virtual, you simply can't do that. You'd need to call a different method for each inheritor, or you'd need inheritance with the functors. The former is not virtual; the latter simply returns us to the original problem.
May 30 2008
parent reply janderson <askme me.com> writes:
Chris Wright wrote:
 janderson wrote:
 You could use a functor/proxy type thing.  That is return a separate 
 object for each class level that has opCall overloaded.  The opCall 
 could be templated.  The functor object probably could be generalized 
 so that you could use it in any case you needed a virtual function (it 
 could callback its owner by template or maybe delegate).
I don't understand this. You seem to be suggesting moving the template to a functor and having inheritors of the original class return a different functor. But unless that functor is virtual, you simply can't do that. You'd need to call a different method for each inheritor, or you'd need inheritance with the functors. The former is not virtual; the latter simply returns us to the original problem.
Something like: class A { templateCallback()() {} //template TemplateMaker Templet() { return new TemplateMaker(this, templateCallbackFunc); } } class A : B { templateCallback2()() {} //template TemplateMaker Templet() { return new TemplateMaker(this, templateCallbackFunc2); } } Then the templateMaker would be: class TemplateMaker(class, callback) //Functor/proxy { TemplateMaker(class, callback) { record of stuff ... } void opCall(T...)(T arg) { recordedclass->recoredcallback(arg); } } Call it like: this.Templet()!("foo")(arg) //Might simplify to due to D's magic property stuff this.Templet!("foo")(arg) Something like that. Haven't tried it so don't know what massaging would be needed and if it would work. -Joel
May 30 2008
parent reply Chris Wright <dhasenan gmail.com> writes:
janderson wrote:
 Something like:
 
 class A
 {
   templateCallback()() {} //template
   TemplateMaker Templet() { return new TemplateMaker(this, 
 templateCallbackFunc); }
 }
 
 
 class A : B
 {
   templateCallback2()() {} //template
   TemplateMaker Templet() { return new TemplateMaker(this, 
 templateCallbackFunc2); }
 }
 
 
 Then the templateMaker would be:
 
 class TemplateMaker(class, callback) //Functor/proxy
 {
    TemplateMaker(class, callback) { record of stuff ... }
 
    void opCall(T...)(T arg) { recordedclass->recoredcallback(arg); }
 }
 
 Call it like:
 
 this.Templet()!("foo")(arg)
 
 //Might simplify to due to D's magic property stuff
 this.Templet!("foo")(arg)
 
 Something like that. Haven't tried it so don't know what massaging would 
 be needed and if it would work.
 
 -Joel
If TemplateMaker is templated, then you can't return it, just an instantiation of it. This only allows you to access one template through it. If TemplateMaker is not templated, then it requires virtual templates in order to work; it's expecting a BaseClass, and even if it got a DerivedClass, it only knows about the template on BaseClass, so that's what it uses.
May 30 2008
parent reply janderson <askme me.com> writes:
Chris Wright wrote:
 janderson wrote:
 Something like:

 class A
 {
   templateCallback()() {} //template
   TemplateMaker Templet() { return new TemplateMaker(this, 
 templateCallbackFunc); }
 }


 class A : B
 {
   templateCallback2()() {} //template
   TemplateMaker Templet() { return new TemplateMaker(this, 
 templateCallbackFunc2); }
 }


 Then the templateMaker would be:

 class TemplateMaker(class, callback) //Functor/proxy
 {
    TemplateMaker(class, callback) { record of stuff ... }

    void opCall(T...)(T arg) { recordedclass->recoredcallback(arg); }
 }

 Call it like:

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

 //Might simplify to due to D's magic property stuff
 this.Templet!("foo")(arg)

 Something like that. Haven't tried it so don't know what massaging 
 would be needed and if it would work.

 -Joel
If TemplateMaker is templated, then you can't return it, just an instantiation of it. This only allows you to access one template through it. If TemplateMaker is not templated, then it requires virtual templates in order to work; it's expecting a BaseClass, and even if it got a DerivedClass, it only knows about the template on BaseClass, so that's what it uses.
Sorry, I haven't actually tried this example. Its an C++ trick I've used for other purposes. I think it looks something like this: class TemplateMaker { TemplateMakera(delegate) { record of stuff ... } //This might be a template too, can't remember. Maybe a reinterpret cast somewhere too. void opCall(T...)(T arg) { delegate->recoredcallback(arg); } } I'm sure given enough time it could be done in D like C++. I don't want to put in the effort though atm. -Joel
May 30 2008
parent reply Chris Wright <dhasenan gmail.com> writes:
janderson wrote:
 Sorry, I haven't actually tried this example.  Its an C++ trick I've 
 used for other purposes.  I think it looks something like this:
 
 class TemplateMaker
 {
    TemplateMakera(delegate) { record of stuff ... } //This might be a 
 template too, can't remember. Maybe a reinterpret cast somewhere too.
    void opCall(T...)(T arg) { delegate->recoredcallback(arg); }
 }
 
 I'm sure given enough time it could be done in D like C++.  I don't want 
 to put in the effort though atm.
 
 -Joel
Delegates can work. But you have to know the arguments to the template in advance. Everything I can think of to implement virtual templates requires virtual templates. With the exception of hacking the compiler, which would provide a means of providing virtual templates. It's a straightforward matter, so it wouldn't require redesign like const, though the initial effort might well be greater.
May 31 2008
parent janderson <askme me.com> writes:
Chris Wright wrote:
 janderson wrote:
 Sorry, I haven't actually tried this example.  Its an C++ trick I've 
 used for other purposes.  I think it looks something like this:

 class TemplateMaker
 {
    TemplateMakera(delegate) { record of stuff ... } //This might be a 
 template too, can't remember. Maybe a reinterpret cast somewhere too.
    void opCall(T...)(T arg) { delegate->recoredcallback(arg); }
 }

 I'm sure given enough time it could be done in D like C++.  I don't 
 want to put in the effort though atm.

 -Joel
Delegates can work. But you have to know the arguments to the template in advance. Everything I can think of to implement virtual templates requires virtual templates. With the exception of hacking the compiler, which would provide a means of providing virtual templates. It's a straightforward matter, so it wouldn't require redesign like const, though the initial effort might well be greater.
I agree that having virtual templates in the language would be nice. I don't like using workaround because they are less intuitive. -Joel
May 31 2008
prev sibling parent BCS <ao pathlink.com> writes:
Reply to janderson,

 You could use a functor/proxy type thing.  That is return a separate
 object for each class level that has opCall overloaded.  The opCall
 could be templated.  The functor object probably could be generalized
 so that you could use it in any case you needed a virtual function (it
 could callback its owner by template or maybe delegate).
 
 Of course that's more complex then a simple template virtual
 inheritance however it does provide more functionality as well.
 
 -Joel
 
It would be simpler to use a string mixin to generate functions with the string encoded into the function name. It could get ugly but a non virtual template function could get around that. But still, the proposed solution is "possible" and a lot cleaner.
May 30 2008
prev sibling parent reply Jason House <jason.james.house gmail.com> writes:
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
parent BCS <ao pathlink.com> writes:
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
prev sibling next sibling parent reply Jason House <jason.james.house gmail.com> writes:
 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
parent BCS <ao pathlink.com> writes:
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
prev sibling parent reply Chris Wright <dhasenan gmail.com> writes:
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.

 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.
May 29 2008
parent BCS <ao pathlink.com> writes:
Reply to Chris,

 BCS wrote:
 
 You can just use a switch statement, but that gets unmaintainable
 before too long.
 
Offten, yes (to both parts), but if the return types and or thenumber and types of the arguments change you are sunk.
May 30 2008