Jump to page: 1 2
Thread overview
class template specialization and inheritance
May 13, 2008
mki
May 14, 2008
Sean Kelly
May 14, 2008
mki
May 14, 2008
Sean Kelly
May 14, 2008
Fawzi Mohamed
May 24, 2008
mki
May 15, 2008
Edward Diener
May 15, 2008
Bill Baxter
May 15, 2008
Edward Diener
May 15, 2008
Bill Baxter
May 18, 2008
mki
May 18, 2008
Bill Baxter
May 13, 2008
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 14, 2008
== Quote from mki (none@none.com)'s article
> 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?

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 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?

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 14, 2008
Sean Kelly Wrote:

> == Quote from mki (none@none.com)'s article
> > 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?
> 
> 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.

In 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.

> > 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?
> 
> 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.

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.
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
mki wrote:
> Sean Kelly Wrote:
> 
>> == Quote from mki (none@none.com)'s article
>>> 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?
>> 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.
> 
> In 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.

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
May 15, 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 15, 2008
Edward Diener wrote:
> 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.' ?

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> > { ... }


--bb
May 15, 2008
Bill Baxter wrote:
> Edward Diener wrote:
>> 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.' ?
> 
> I think it's supposed to be legal using:
> 
> class C(TT:A!(T), T)

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.

> 
> 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 15, 2008
Edward Diener wrote:
> Bill Baxter wrote:
>> Edward Diener wrote:
>>> 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.' ?
>>
>> I think it's supposed to be legal using:
>>
>> class C(TT:A!(T), T)
> 
> 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.
> 
>>
>> 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. 

Yeh, my C++ is a bit rusty from too much D. :-)

> 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 18, 2008
Thanks for your answers!

Edward Diener Wrote:

> Bill Baxter wrote:
> > Edward Diener wrote:
> >> 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.' ?
> > 
> > I think it's supposed to be legal using:
> > 
> > class C(TT:A!(T), T)
> 
> 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.

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?
« First   ‹ Prev
1 2