www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Unusual template specialisation behavior

reply Matthias Walter <walter mail.math.uni-magdeburg.de> writes:
Hello guys,

Here's a (minimal) code snippet for my problem:

| struct Expression (R, E) { }
| 
| struct ResolveReference (T)
| {
|     alias T reference_type;
| }
| 
| struct ResolveReference (T : Expression !(R, E))
| {
|     alias T* reference_type;
| }
| 
| void printRefType (T) ()
| {
|     Stdout (ResolveReference !(Expression !(T,
T)).reference_type.stringof).newline;
| }
| 
int main (char[][] args)
| {
|     printRefType !(uint);
|     printRefType !(int);
|     printRefType !(long);
|     printRefType !(char);
|     printRefType !(float);
| 
|     return 0;
| }

The output is:

| Expression!(uint,uint) 
| Expression!(int,int) *
| Expression!(long,long) 
| Expression!(char,char) 
| Expression!(float,float) 

If my understanding of template specialisation is correct, only the second call
instanciates the template "ResolveReference (T : Expression !(R, E))" and the
other ones choose "ResolveReference (T)". Why does this happen only for ints?

My intended behavior would be, that ResolveReference.reference_type is T*, if T
is an Expression and T if not. Thus, I expected, that this code returns 

| Expression!(uint,uint) *
| Expression!(int,int) *
| Expression!(long,long) * 
| Expression!(char,char) *
| Expression!(float,float) *

btw, this is tested on Linux, Tango rev 3152, Digital Mars D Compiler v1.021.
Hopefully, I did not miss a bug-fix.

best regards
Matthias Walter
Feb 08 2008
parent reply Christopher Wright <dhasenan gmail.com> writes:
Matthias Walter wrote:
 Hello guys,
 
 Here's a (minimal) code snippet for my problem:
 
 | struct Expression (R, E) { }
 | 
 | struct ResolveReference (T)
 | {
 |     alias T reference_type;
 | }
 | 
 | struct ResolveReference (T : Expression !(R, E))

The compiler should have caught this, I think. You have R and E, but they aren't defined. They default to int (which is why you saw that behavior), but not being defined, they should be a compile-time error. The proper way to do this, unfortunately, involves static if rather than overloading. At least if you want your users to supply a single argument in all cases.
Feb 08 2008
next sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Christopher Wright <dhasenan gmail.com> wrote:
 Matthias Walter wrote:
 Hello guys,
 
 Here's a (minimal) code snippet for my problem:
 
 | struct Expression (R, E) { }
 | 
 | struct ResolveReference (T)
 | {
 |     alias T reference_type;
 | }
 | 
 | struct ResolveReference (T : Expression !(R, E))

The compiler should have caught this, I think. You have R and E, but they aren't defined. They default to int (which is why you saw that behavior), but not being defined, they should be a compile-time error. The proper way to do this, unfortunately, involves static if rather than overloading. At least if you want your users to supply a single argument in all cases.

struct ResolveReference (T : Expression !(R, E), R, E) { alias T* reference_type; } works just fine: void main (char[][] args) { printRefType !(uint); printRefType !(int); printRefType !(long); printRefType !(char); printRefType !(float); Stdout (ResolveReference !(uint).reference_type.stringof).newline; } Expression!(uint,uint)* Expression!(int,int)* Expression!(long,long)* Expression!(char,char)* Expression!(float,float)* uint -- SnakE
Feb 08 2008
parent Matthias Walter <walter mail.math.uni-magdeburg.de> writes:
Sergey Gromov Wrote:

 Christopher Wright <dhasenan gmail.com> wrote:
 Matthias Walter wrote:
 Hello guys,
 
 Here's a (minimal) code snippet for my problem:
 
 | struct Expression (R, E) { }
 | 
 | struct ResolveReference (T)
 | {
 |     alias T reference_type;
 | }
 | 
 | struct ResolveReference (T : Expression !(R, E))

The compiler should have caught this, I think. You have R and E, but they aren't defined. They default to int (which is why you saw that behavior), but not being defined, they should be a compile-time error. The proper way to do this, unfortunately, involves static if rather than overloading. At least if you want your users to supply a single argument in all cases.

struct ResolveReference (T : Expression !(R, E), R, E) { alias T* reference_type; } works just fine: void main (char[][] args) { printRefType !(uint); printRefType !(int); printRefType !(long); printRefType !(char); printRefType !(float); Stdout (ResolveReference !(uint).reference_type.stringof).newline; } Expression!(uint,uint)* Expression!(int,int)* Expression!(long,long)* Expression!(char,char)* Expression!(float,float)* uint -- SnakE

Thank you both! I guess, this works, because when passing an Expression !(R, E), the 2nd and 3rd specialisation parameters R and E are deduced and everything works fine, right? Thanks for your help! best regards Matthias Walter
Feb 08 2008
prev sibling parent reply Matthias Walter <walter mail.math.uni-magdeburg.de> writes:
Christopher Wright Wrote:

 Matthias Walter wrote:
 Hello guys,
 
 Here's a (minimal) code snippet for my problem:
 
 | struct Expression (R, E) { }
 | 
 | struct ResolveReference (T)
 | {
 |     alias T reference_type;
 | }
 | 
 | struct ResolveReference (T : Expression !(R, E))

The compiler should have caught this, I think. You have R and E, but they aren't defined. They default to int (which is why you saw that behavior), but not being defined, they should be a compile-time error. The proper way to do this, unfortunately, involves static if rather than overloading. At least if you want your users to supply a single argument in all cases.

Thanks for that hint. How would such a static if look like? I currently have | static if (is (T == struct)). This works for distinguishing native datatypes like int from my Expression-Templates. But is there a native way to statically check, whether T is some specialisation like "T == Expression !(R, E)" (for all R, E). best regards Matthias Walter
Feb 08 2008
parent Christopher Wright <dhasenan gmail.com> writes:
Matthias Walter wrote:
 Christopher Wright Wrote:
 
 Matthias Walter wrote:
 Hello guys,

 Here's a (minimal) code snippet for my problem:

 | struct Expression (R, E) { }
 | 
 | struct ResolveReference (T)
 | {
 |     alias T reference_type;
 | }
 | 
 | struct ResolveReference (T : Expression !(R, E))

they aren't defined. They default to int (which is why you saw that behavior), but not being defined, they should be a compile-time error. The proper way to do this, unfortunately, involves static if rather than overloading. At least if you want your users to supply a single argument in all cases.

Thanks for that hint. How would such a static if look like?

template ResolveReference (T) { static if (is (T == Expression!(R, E))) { // or `static if (is (T == Expression!(R, E), R, E)) {` alias T* ResolveReference; } else { alias T ResolveReference; } } Except that doesn't work, right now. There's a bug listed for it.
Feb 08 2008