digitalmars.D - class template specialization and inheritance
- mki (61/61) May 13 2008 Hello!
- Sean Kelly (26/87) May 13 2008 I believe that template expansion like the above requires an exact match...
- mki (29/120) May 13 2008 In the article
- Sean Kelly (20/114) May 14 2008 Oops, you're right. Shows how often I specialize templates on classes
- Fawzi Mohamed (7/88) May 14 2008 It looks like a bug to me, I would write a report on
- mki (4/96) May 24 2008 Since I got no further answer, I decided to do so and filed a bug report...
- Edward Diener (3/14) May 14 2008 Huh ! What is T above ? I do not think that your use of T should be
- Bill Baxter (14/30) May 14 2008 I think it's supposed to be legal using:
- Edward Diener (9/44) May 14 2008 This makes sense since T is another template parameter. In the original,...
- Bill Baxter (5/53) May 14 2008 Right, I was just pointing out what I thought might have been the
- mki (23/49) May 18 2008 I don't see why the original
- Bill Baxter (24/52) May 18 2008 From http://www.digitalmars.com/d/1.0/template.html
- mki (35/41) May 18 2008 Thanks for your answer, Bill.
- Bruno Medeiros (9/65) Jun 13 2008 Seem my comments to your bug report. I don't think it's a bug, but
Hello! I just discovered the template syntax of D. I am very exited about its simplicity compared to C++. Now I ran into a template behavior I do not understand. This code: *** begin code 1 *** import std.stdio; class A { } class B : A { } class C(T:A) { static void tellMe() { writefln("derived from A."); } } class C(T) { static void tellMe() { writefln("generic."); } } void main() { C!(A).tellMe(); C!(B).tellMe(); C!(int).tellMe(); } *** end code 1 *** as expected produces the output: derived from A. derived from A. generic. But this code *** begin code 2 *** import std.stdio; class A(T) { } class B(T) : A!(T) { } class C(TT:A!(T)) { static void tellMe() { writefln("derived from A!(T)."); } } class C(T) { static void tellMe() { writefln("generic."); } } void main() { C!(A!(int)).tellMe(); C!(B!(int)).tellMe(); C!(int).tellMe(); } *** end code 2 *** gives the output: derived from A!(T). generic. generic. In the second line I would expect the output "derived from A!(T)", like in the example of code 1. My feeling is that for C!(B!(int)) 'class C(TT:A!(T))' with TT=B!(T) and T=int should be a better specialization than 'class C(T)' with T=B!(T). Why is 'class C(T)' chosen here? Thanks! PS: BTW, I thought hard about the question if there is a way to do similar things in C++, that is to have a specialization of a *class* template which is valid exactly for all classes of a given type A and all classes *derived* from A. For function templates one can use a SFINAE-trick (the 'enable_if' template in boost) to achieve this, but I don't see a way to do this for class templates in C++. Does anyone have an idea?
May 13 2008
== Quote from mki (none none.com)'s articleHello! I just discovered the template syntax of D. I am very exited about its simplicity compared to C++. Now I ran into a template behavior I do not understand. This code: *** begin code 1 *** import std.stdio; class A { } class B : A { } class C(T:A) { static void tellMe() { writefln("derived from A."); } } class C(T) { static void tellMe() { writefln("generic."); } } void main() { C!(A).tellMe(); C!(B).tellMe(); C!(int).tellMe(); } *** end code 1 *** as expected produces the output: derived from A. derived from A. generic. But this code *** begin code 2 *** import std.stdio; class A(T) { } class B(T) : A!(T) { } class C(TT:A!(T)) { static void tellMe() { writefln("derived from A!(T)."); } } class C(T) { static void tellMe() { writefln("generic."); } } void main() { C!(A!(int)).tellMe(); C!(B!(int)).tellMe(); C!(int).tellMe(); } *** end code 2 *** gives the output: derived from A!(T). generic. generic. In the second line I would expect the output "derived from A!(T)", like in the example of code 1. My feeling is thatfor C!(B!(int))'class C(TT:A!(T))' with TT=B!(T) and T=int should be a better specialization than 'class C(T)' with T=B!(T). Why is 'class C(T)' chosen here?I believe that template expansion like the above requires an exact match for specialization, so it's basically the same as C++ in this respect. However, I do think that this is confusing in light of the "is" syntax where a colon means something different.Thanks! PS: BTW, I thought hard about the question if there is a way to do similar things in C++, that is to have aspecialization of a *class* template which is valid exactly for all classes of a given type A and all classes *derived* from A. For function templates one can use a SFINAE-trick (the 'enable_if' template in boost) to achieve this, but I don't see a way to do this for class templates in C++. Does anyone have an idea? Concept checking can be done in D using the following method: template isDerivedFromA( T ) { const isDerivedFromA = is( T : A ); } class C( T, bool derivedFromA : false = isDerivedFromA!(T) ) {} class C( T, bool derivedFromA : true = isDerivedFromA!(T) ) {} Or if a boolean isn't enough, represent the result using a type: template getInfoOn( T ) { static if( foo ) alias Type1 getInfoOn; else static if( bar ) alias Type2 getInfoOn; else alias Type3 getInfoOn; } class C( T, I : Type1 = getInfoOn!(T) ) {} class C( T, I : Type2 = getInfoOn!(T) ) {} class C( T, I : Type3 = getInfoOn!(T) ) {} I believe a similar approach will work in C++, though you have to be a bit more clever about implementing the concept checking code. Sean
May 13 2008
Sean Kelly Wrote:== Quote from mki (none none.com)'s articleIn the article http://www.digitalmars.com/d/2.0/templates-revisited.html I found the example class Foo( R, // R can be any type P:P*, // P must be a pointer type T:int, // T must be int type S:T*, // S must be pointer to T C:B, // C must be of class B or derived // from B U:I, // U must be a class that // implements interface I string str = "hello", // string literal, // default is "hello" alias A = B // A is any symbol // (including template symbols), // defaulting to B ) So according to the comment in the 6th line the colon includes inherited classes. Also, in my example "code 1" the colon works this way: Since the second line of the output is "derived from A." C!(B) matches the expression C(T:A) with T=B It seems that the additional template parameter T of class A somehow makes a difference in "code 2". But I still don't see why this should be.Hello! I just discovered the template syntax of D. I am very exited about its simplicity compared to C++. Now I ran into a template behavior I do not understand. This code: *** begin code 1 *** import std.stdio; class A { } class B : A { } class C(T:A) { static void tellMe() { writefln("derived from A."); } } class C(T) { static void tellMe() { writefln("generic."); } } void main() { C!(A).tellMe(); C!(B).tellMe(); C!(int).tellMe(); } *** end code 1 *** as expected produces the output: derived from A. derived from A. generic. But this code *** begin code 2 *** import std.stdio; class A(T) { } class B(T) : A!(T) { } class C(TT:A!(T)) { static void tellMe() { writefln("derived from A!(T)."); } } class C(T) { static void tellMe() { writefln("generic."); } } void main() { C!(A!(int)).tellMe(); C!(B!(int)).tellMe(); C!(int).tellMe(); } *** end code 2 *** gives the output: derived from A!(T). generic. generic. In the second line I would expect the output "derived from A!(T)", like in the example of code 1. My feeling is thatfor C!(B!(int))'class C(TT:A!(T))' with TT=B!(T) and T=int should be a better specialization than 'class C(T)' with T=B!(T). Why is 'class C(T)' chosen here?I believe that template expansion like the above requires an exact match for specialization, so it's basically the same as C++ in this respect. However, I do think that this is confusing in light of the "is" syntax where a colon means something different.Up to here this can also be done in C++, for the getInfoOn template there should be some substitute in the boost library. But my problem ist that I need to end up with a class template C(T) depending only on *one* template parameter, the class T (which might depend on a template parameter itself). Currently I have this problem in a programming project in C++, and I did not find a solution. I consider switching to D, but at the moment I don't see a solution in D, either.PS: BTW, I thought hard about the question if there is a way to do similar things in C++, that is to have aspecialization of a *class* template which is valid exactly for all classes of a given type A and all classes *derived* from A. For function templates one can use a SFINAE-trick (the 'enable_if' template in boost) to achieve this, but I don't see a way to do this for class templates in C++. Does anyone have an idea? Concept checking can be done in D using the following method: template isDerivedFromA( T ) { const isDerivedFromA = is( T : A ); } class C( T, bool derivedFromA : false = isDerivedFromA!(T) ) {} class C( T, bool derivedFromA : true = isDerivedFromA!(T) ) {} Or if a boolean isn't enough, represent the result using a type: template getInfoOn( T ) { static if( foo ) alias Type1 getInfoOn; else static if( bar ) alias Type2 getInfoOn; else alias Type3 getInfoOn; } class C( T, I : Type1 = getInfoOn!(T) ) {} class C( T, I : Type2 = getInfoOn!(T) ) {} class C( T, I : Type3 = getInfoOn!(T) ) {} I believe a similar approach will work in C++, though you have to be a bit more clever about implementing the concept checking code.
May 13 2008
mki wrote:Sean Kelly Wrote:Oops, you're right. Shows how often I specialize templates on classes :-p In any case, I think the current compiler simply treats the "T" in "C(TT : A!(T))" as an unknown specific type rather than a placeholder for "any" type. I tried converting the declaration to "C(TT:A!(int))" and it worked as desired. And unfortunately I don't think D supports template template parameters so you couldn't do something like "C(T!(U):A!(U))" either, even if you knew that the type being passed only had one template parameter. Personally, I think this is a bit of a hole in D's template mechanism. It would be nice if there were a way to break apart template parameter types and determine the component types and such easily. Something like: class C( T1!(x = ...), int n : 1 = x.length, T2 : int = x[0] ) {} Where 'x' becomes a tuple holding all the template parameters of T1. This wouldn't exactly resolve your specific situation however. The easiest thing there would be to simply allow templated types to be supplied without the template parameters for specialization checking: class C( T : A ) {} I haven't thought this through however so there may be problems with it. Sean== Quote from mki (none none.com)'s articleIn the article http://www.digitalmars.com/d/2.0/templates-revisited.html I found the example class Foo( R, // R can be any type P:P*, // P must be a pointer type T:int, // T must be int type S:T*, // S must be pointer to T C:B, // C must be of class B or derived // from B U:I, // U must be a class that // implements interface I string str = "hello", // string literal, // default is "hello" alias A = B // A is any symbol // (including template symbols), // defaulting to B ) So according to the comment in the 6th line the colon includes inherited classes. Also, in my example "code 1" the colon works this way: Since the second line of the output is "derived from A." C!(B) matches the expression C(T:A) with T=B It seems that the additional template parameter T of class A somehow makes a difference in "code 2". But I still don't see why this should be.Hello! I just discovered the template syntax of D. I am very exited about its simplicity compared to C++. Now I ran into a template behavior I do not understand. This code: *** begin code 1 *** import std.stdio; class A { } class B : A { } class C(T:A) { static void tellMe() { writefln("derived from A."); } } class C(T) { static void tellMe() { writefln("generic."); } } void main() { C!(A).tellMe(); C!(B).tellMe(); C!(int).tellMe(); } *** end code 1 *** as expected produces the output: derived from A. derived from A. generic. But this code *** begin code 2 *** import std.stdio; class A(T) { } class B(T) : A!(T) { } class C(TT:A!(T)) { static void tellMe() { writefln("derived from A!(T)."); } } class C(T) { static void tellMe() { writefln("generic."); } } void main() { C!(A!(int)).tellMe(); C!(B!(int)).tellMe(); C!(int).tellMe(); } *** end code 2 *** gives the output: derived from A!(T). generic. generic. In the second line I would expect the output "derived from A!(T)", like in the example of code 1. My feeling is thatfor C!(B!(int))'class C(TT:A!(T))' with TT=B!(T) and T=int should be a better specialization than 'class C(T)' with T=B!(T). Why is 'class C(T)' chosen here?I believe that template expansion like the above requires an exact match for specialization, so it's basically the same as C++ in this respect. However, I do think that this is confusing in light of the "is" syntax where a colon means something different.
May 14 2008
On 2008-05-14 00:55:41 +0200, mki <none none.com> said:Hello! I just discovered the template syntax of D. I am very exited about its simplicity compared to C++. Now I ran into a template behavior I do not understand. This code: *** begin code 1 *** import std.stdio; class A { } class B : A { } class C(T:A) { static void tellMe() { writefln("derived from A."); } } class C(T) { static void tellMe() { writefln("generic."); } } void main() { C!(A).tellMe(); C!(B).tellMe(); C!(int).tellMe(); } *** end code 1 *** as expected produces the output: derived from A. derived from A. generic. But this code *** begin code 2 *** import std.stdio; class A(T) { } class B(T) : A!(T) { } class C(TT:A!(T)) { static void tellMe() { writefln("derived from A!(T)."); } } class C(T) { static void tellMe() { writefln("generic."); } } void main() { C!(A!(int)).tellMe(); C!(B!(int)).tellMe(); C!(int).tellMe(); } *** end code 2 *** gives the output: derived from A!(T). generic. generic. In the second line I would expect the output "derived from A!(T)", like in the example of code 1. My feeling is that for C!(B!(int)) 'class C(TT:A!(T))' with TT=B!(T) and T=int should be a better specialization than 'class C(T)' with T=B!(T). Why is 'class C(T)' chosen here?It looks like a bug to me, I would write a report on http://d.puremagic.com/issues/ maybe you should rewrite the messages to "T is derived from ...", because C is not derived from those classes, and the message is misleading. Fawzi
May 14 2008
Since I got no further answer, I decided to do so and filed a bug report: http://d.puremagic.com/issues/show_bug.cgi?id=2126 ~mki Fawzi Mohamed Wrote:On 2008-05-14 00:55:41 +0200, mki <none none.com> said:Hello! I just discovered the template syntax of D. I am very exited about its simplicity compared to C++. Now I ran into a template behavior I do not understand. This code: *** begin code 1 *** import std.stdio; class A { } class B : A { } class C(T:A) { static void tellMe() { writefln("derived from A."); } } class C(T) { static void tellMe() { writefln("generic."); } } void main() { C!(A).tellMe(); C!(B).tellMe(); C!(int).tellMe(); } *** end code 1 *** as expected produces the output: derived from A. derived from A. generic. But this code *** begin code 2 *** import std.stdio; class A(T) { } class B(T) : A!(T) { } class C(TT:A!(T)) { static void tellMe() { writefln("derived from A!(T)."); } } class C(T) { static void tellMe() { writefln("generic."); } } void main() { C!(A!(int)).tellMe(); C!(B!(int)).tellMe(); C!(int).tellMe(); } *** end code 2 *** gives the output: derived from A!(T). generic. generic. In the second line I would expect the output "derived from A!(T)", like in the example of code 1. My feeling is that for C!(B!(int)) 'class C(TT:A!(T))' with TT=B!(T) and T=int should be a better specialization than 'class C(T)' with T=B!(T). Why is 'class C(T)' chosen here?It looks like a bug to me, I would write a report on http://d.puremagic.com/issues/ maybe you should rewrite the messages to "T is derived from ...", because C is not derived from those classes, and the message is misleading. Fawzi
May 24 2008
mki wrote:Hello! I just discovered the template syntax of D. I am very exited about its simplicity compared to C++. Now I ran into a template behavior I do not understand. This code: snip... class C(TT:A!(T)) { static void tellMe() { writefln("derived from A!(T)."); } }Huh ! What is T above ? I do not think that your use of T should be legal. Are you sure you did not mean 'class C(TT:A!(TT)) { etc.' ?
May 14 2008
Edward Diener wrote:mki wrote:I think it's supposed to be legal using: class C(TT:A!(T), T) That is, one template parameter can depend on another in a non-trivial way in theory. But I think the compiler has trouble with such things right now. From what I understand that is the intended way to do C++ things like this in D: template <typename Z> template class C { ... } // specialization for all Z == A<T> for template A and some T template <typename T> template class C< A<T> > { ... } --bbHello! I just discovered the template syntax of D. I am very exited about its simplicity compared to C++. Now I ran into a template behavior I do not understand. This code:> snip...class C(TT:A!(T)) { static void tellMe() { writefln("derived from A!(T)."); } }Huh ! What is T above ? I do not think that your use of T should be legal. Are you sure you did not mean 'class C(TT:A!(TT)) { etc.' ?
May 14 2008
Bill Baxter wrote:Edward Diener wrote:This makes sense since T is another template parameter. In the original, quoted further above, there was no second template parameter of T, which should generate a compiler error.mki wrote:I think it's supposed to be legal using: class C(TT:A!(T), T)Hello! I just discovered the template syntax of D. I am very exited about its simplicity compared to C++. Now I ran into a template behavior I do not understand. This code:> snip...class C(TT:A!(T)) { static void tellMe() { writefln("derived from A!(T)."); } }Huh ! What is T above ? I do not think that your use of T should be legal. Are you sure you did not mean 'class C(TT:A!(TT)) { etc.' ?That is, one template parameter can depend on another in a non-trivial way in theory. But I think the compiler has trouble with such things right now. From what I understand that is the intended way to do C++ things like this in D: template <typename Z> template class C { ... } // specialization for all Z == A<T> for template A and some T template <typename T> template class C< A<T> > { ... }If this is meant as C++ the second use of 'template' each time is incorrect. Otherwise it is correct partial specialization syntax as you mention. But notice that T is a template parameter in your C++ equivalent example while in the OP's original which I cited as erroneous, there is no T as a template parameter.
May 14 2008
Edward Diener wrote:Bill Baxter wrote:Yeh, my C++ is a bit rusty from too much D. :-)Edward Diener wrote:This makes sense since T is another template parameter. In the original, quoted further above, there was no second template parameter of T, which should generate a compiler error.mki wrote:I think it's supposed to be legal using: class C(TT:A!(T), T)Hello! I just discovered the template syntax of D. I am very exited about its simplicity compared to C++. Now I ran into a template behavior I do not understand. This code:> snip...class C(TT:A!(T)) { static void tellMe() { writefln("derived from A!(T)."); } }Huh ! What is T above ? I do not think that your use of T should be legal. Are you sure you did not mean 'class C(TT:A!(TT)) { etc.' ?That is, one template parameter can depend on another in a non-trivial way in theory. But I think the compiler has trouble with such things right now. From what I understand that is the intended way to do C++ things like this in D: template <typename Z> template class C { ... } // specialization for all Z == A<T> for template A and some T template <typename T> template class C< A<T> > { ... }If this is meant as C++ the second use of 'template' each time is incorrect.Otherwise it is correct partial specialization syntax as you mention. But notice that T is a template parameter in your C++ equivalent example while in the OP's original which I cited as erroneous, there is no T as a template parameter.Right, I was just pointing out what I thought might have been the original poster's intention. --bb
May 14 2008
Thanks for your answers! Edward Diener Wrote:Bill Baxter wrote:I don't see why the original class C(TT:A!(T)) shouldn't be legal. in C++ style syntax I would have //primary template template<typename T> class C {}; //template specialization of C template<typename TT, typename T> class C<TT:A<T> > ... ; (Of course this isn't legal either, because C++ doesn't have the colon-syntax.) Now D does away with primary templates, so the first part can be skipped completely in D. Furthermore, D does not need the line template<typename TT, typename T> because the template parameters of the specialization can be completely deduced from the expression C<TT:A<T> >. All undeclared symbols are template parameters. So to my understanding, C(TT:A(T)) should be legal D and should have the sense I indicated with the C++ style syntax above. Also notice that there is no compiler error message on this expression. For my programming purpose, the important thing is that C(TT:A(T)) is a specialization of C(T), but the suggested C(TT:A(T),T) is _not_. For this reason, I really need the first variant, and not the second one. I would like to hear further opinions on this. I my understanding correct, or not?Edward Diener wrote:This makes sense since T is another template parameter. In the original, quoted further above, there was no second template parameter of T, which should generate a compiler error.mki wrote:I think it's supposed to be legal using: class C(TT:A!(T), T)Hello! I just discovered the template syntax of D. I am very exited about its simplicity compared to C++. Now I ran into a template behavior I do not understand. This code:> snip...class C(TT:A!(T)) { static void tellMe() { writefln("derived from A!(T)."); } }Huh ! What is T above ? I do not think that your use of T should be legal. Are you sure you did not mean 'class C(TT:A!(TT)) { etc.' ?
May 18 2008
mki wrote:I don't see why the original class C(TT:A!(T)) shouldn't be legal. in C++ style syntax I would have //primary template template<typename T> class C {}; //template specialization of C template<typename TT, typename T> class C<TT:A<T> > ... ; (Of course this isn't legal either, because C++ doesn't have the colon-syntax.) Now D does away with primary templates, so the first part can be skipped completely in D. Furthermore, D does not need the line template<typename TT, typename T> because the template parameters of the specialization can be completely deduced from the expression C<TT:A<T> >. All undeclared symbols are template parameters. So to my understanding, C(TT:A(T)) should be legal D and should have the sense I indicated with the C++ style syntax above. Also notice that there is no compiler error message on this expression.From http://www.digitalmars.com/d/1.0/template.html """ Deduction from a specialization can provide values for more than one parameter: template Foo(T: T[U], U) { ... } Foo!(int[long]) // instantiates Foo with T set to int, U set to long """ But (for D1 at least) that doesn't work with argument deduction: void fBaz(T: T[U], U)(T[U] x) { writefln("typeof(T)=",typeid(T)," typeof(U)=",typeid(U)); } ... int[long] z; fBaz(z);specialization not allowed for deduced parameter Tpartialifti.d(24): template partialifti.fBaz(T : T[U],U)For my programming purpose, the important thing is that C(TT:A(T)) is a specialization of C(T), but the suggested C(TT:A(T),T) is _not_.According to the documentation D thinks it is. Do you have evidence to the contrary?For this reason, I really need the first variant, and not the second one. I would like to hear further opinions on this. I my understanding correct, or not?Sadly the argument deduction (IFTI) implementation is not very complete. It only works in very specific situations. --bb
May 18 2008
Bill Baxter Wrote:Thanks for your answer, Bill. I am quite new to D, and it seems that I got this quite wrong. I believed that for a template to be a specialization of another, the number of template parameters must be the same. With this new insight the code of my original problem changes to //begin code example import std.stdio; class A(T) { } class B(T) : A!(T) { } class C(T) { static void tellMe() { writefln("generic."); } } class C(TT:A!(T),T) { static void tellMe() { writefln("TT is the same as or derived from A!(T)."); } } void main() { C!(A!(int)).tellMe(); C!(B!(int)).tellMe(); C!(int).tellMe(); } //end code example which still gives (using DMD 2.008) T is the same as or derived from A!(TT). generic. generic. where the second line should be "T is the same as or derived from A!(TT)." in my opinion. Am I still doing something wrong, or is this a bug?For my programming purpose, the important thing is that C(TT:A(T)) is a specialization of C(T), but the suggested C(TT:A(T),T) is _not_.According to the documentation D thinks it is. Do you have evidence to the contrary?Sadly the argument deduction (IFTI) implementation is not very complete. It only works in very specific situations.Thats bad. I'm writing heavily templated generic code, currently in C++ with its sometimes very painful template syntax. I hoped that I could switch to D. But an incomplete argument deduction implementation is worse than the C++ template syntax. ~mki
May 18 2008
mki wrote:Bill Baxter Wrote:Seem my comments to your bug report. I don't think it's a bug, but rather an enhancement request, but it's hard to say for sure since (IMO) the spec is somewhat murky on matters of template specialization. There is a workaround there which should work for what you want, unless you have some other requisite you didn't mention. -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#DThanks for your answer, Bill. I am quite new to D, and it seems that I got this quite wrong. I believed that for a template to be a specialization of another, the number of template parameters must be the same. With this new insight the code of my original problem changes to //begin code example import std.stdio; class A(T) { } class B(T) : A!(T) { } class C(T) { static void tellMe() { writefln("generic."); } } class C(TT:A!(T),T) { static void tellMe() { writefln("TT is the same as or derived from A!(T)."); } } void main() { C!(A!(int)).tellMe(); C!(B!(int)).tellMe(); C!(int).tellMe(); } //end code example which still gives (using DMD 2.008) T is the same as or derived from A!(TT). generic. generic. where the second line should be "T is the same as or derived from A!(TT)." in my opinion. Am I still doing something wrong, or is this a bug?For my programming purpose, the important thing is that C(TT:A(T)) is a specialization of C(T), but the suggested C(TT:A(T),T) is _not_.According to the documentation D thinks it is. Do you have evidence to the contrary?Sadly the argument deduction (IFTI) implementation is not very complete. It only works in very specific situations.Thats bad. I'm writing heavily templated generic code, currently in C++ with its sometimes very painful template syntax. I hoped that I could switch to D. But an incomplete argument deduction implementation is worse than the C++ template syntax. ~mki
Jun 13 2008