digitalmars.D - class scope virtual template functions
- BCS (25/25) May 29 2008 Somewhere IIRC the spec says that classes can't have template methods th...
- Walter Bright (8/15) May 29 2008 True, but now you have the issue that minor, seemingly innocuous changes...
- BCS (6/25) May 29 2008 in my case, I need this syntax
- Walter Bright (3/11) May 29 2008 You could try parameterizing the class enclosing the template, rather
- BCS (23/37) May 29 2008 I need to be able to have more than one version of the template
- Bruno Medeiros (35/86) Jun 14 2008 How about creating normal methods, and normal method override
- BCS (9/24) Jun 15 2008 I think that might work in some cases (maybe even mine). However In my c...
- janderson (9/41) May 29 2008 You could use a functor/proxy type thing. That is return a separate
- Chris Wright (7/12) May 30 2008 I don't understand this. You seem to be suggesting moving the template
- janderson (27/40) May 30 2008 Something like:
- Chris Wright (7/45) May 30 2008 If TemplateMaker is templated, then you can't return it, just an
- janderson (12/60) May 30 2008 Sorry, I haven't actually tried this example. Its an C++ trick I've
- Chris Wright (8/22) May 31 2008 Delegates can work. But you have to know the arguments to the template
- janderson (4/28) May 31 2008 I agree that having virtual templates in the language would be nice. I
- BCS (5/16) May 30 2008 It would be simpler to use a string mixin to generate functions with the...
- Jason House (2/8) May 29 2008 I'd prefer the compiler to require non-virtual functions to be declared ...
- BCS (2/7) May 29 2008 I agree with the suggestion for both reasons
- Jason House (1/2) May 29 2008 Much to my frustration, D's enums do not represent a completely enumerat...
- BCS (5/12) May 29 2008 Just because /you and I/ can't get the full set, doesn't say the compile...
- Chris Wright (49/80) May 29 2008 You also have to be sure to instantiate all overrides of the template.
- BCS (3/8) May 30 2008 Offten, yes (to both parts), but if the return types and or thenumber an...
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
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: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.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
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: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)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
BCS wrote:Reply to Walter,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#DBCS wrote: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)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.
Jun 14 2008
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
BCS wrote:Reply to Walter,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. -JoelBCS 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.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
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
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. -JoelYou 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
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. -JoelIf 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
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. -JoelSomething 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. -JoelIf 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
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. -JoelDelegates 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
Chris Wright wrote:janderson wrote:I agree that having virtual templates in the language would be nice. I don't like using workaround because they are less intuitive. -JoelSorry, 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. -JoelDelegates 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
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. -JoelIt 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
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,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.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
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
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