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?

'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

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

'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

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.

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

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

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

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

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

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.

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

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?

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);

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