If you have a template that takes an integer parameter, there appears to be no way to specialize on that integer.
Here are some examples of things that work and things that don't:
/*==========================================================================
* specialize.d
* Written in the D Programming Language (http://www.digitalmars.com/d)
*/
/***************************************************************************
* Test of specialization in D templates
*
* <TODO: Description>
*
* Author: William V. Baxter III
* Date: 13 Nov 2007
* License: Public Domain
*/
//===========================================================================
module specialize;
import std.stdio;
import std.string;
struct Container(Scalar,int N)
{
Scalar[N] values_;
}
template Container2(T) { alias Container!(T,2) Container2; }
struct Simple(T) {
T[] data;
}
struct Number(int N) {
const int value = N;
}
void test_specialize(T)() {
writefln("Not so special: ", typeid(T));
}
void test_specialize(T : float)() {
writefln("special - float");
}
void test_specialize(T : T[U], U)() {
writefln("special - Assoc Array of %s -> %s", typeid(U), typeid(T));
}
void test_specialize(T : Container!(T,3))() {
writefln("Ooh special - CONTAINER T,3");
}
void test_specialize(T : Simple!(T))() {
writefln("Ooh special - SIMPLE");
}
void test_specialize(T : Number!(2))() {
writefln("Ooh special - NUMBER = 2");
}
// Cases that don't match
// This doesn't compile
//void test_specialize(T : Number!(int N), int N)() {
// writefln("Ooh special - NUMBER");
//}
void test_specialize(T : Number!(N), int N)() {
writefln("Ooh special - NUMBER N");
}
void test_specialize(T : Container2!(T))() {
writefln("Ooh special - Container2!(T)");
}
void test_specialize(T : Container!(T,N), int N)() {
writefln("Ooh special - Container!(T,N)");
}
void main() {
test_specialize!(int)();
test_specialize!(float)();
test_specialize!(float[int])();
test_specialize!(Simple!(int))();
test_specialize!(Number!(2))();
test_specialize!(Container!(int,3))();
// These don't get matched
test_specialize!(Number!(5))();
test_specialize!(Container!(int,2))();
test_specialize!(Container!(int,4))();
}
Comment #1 by wbaxter — 2007-11-13T01:00:30Z
Actually this should probably be "no way to specialize *around* the integer parameter". I.e. fixing the integer in the specialization works (sometimes, except if the integer has been fixed using a template alias like Container2 above). But what doesn't work is fixing the template and or other template parameters and leaving the number as a parameter.
Comment #2 by bugzilla — 2008-02-20T17:28:48Z
// This doesn't compile
void test_specialize(T : Number!(int N), int N)() {
writefln("Ooh special - NUMBER");
}
That correctly will not compile because Number!(int N) is invalid syntax.
Comment #3 by wbaxter — 2008-02-20T21:31:20Z
(In reply to comment #2)
> // This doesn't compile
> void test_specialize(T : Number!(int N), int N)() {
> writefln("Ooh special - NUMBER");
> }
>
> That correctly will not compile because Number!(int N) is invalid syntax.
>
I think that's part of the problem -- there *is* no valid syntax for it.
For a template that takes a type you can specialize using (T:Container!(T)), but a number is not a type so (T:Container!(T)) doesn't work (at least I'm guessing that's why it doesn't work).
This doesn't look like it should work either because N hasn't been defined anywhere.
void test_specialize(T : Number!(N))() {
writefln("Ooh special - NUMBER N");
}
But it does compile.
I tried the Number!(int N) version just because that looks like a reasonable syntax for saying "hey we're introducing a new integer parameter named N here".
This affects code that uses templates to implement fixed-size vectors and operations on them. It came up when I was trying to implement Vector/Matrix classes in OpenMesh/D:
http://www.dsource.org/projects/openmeshd/browser/trunk/OpenMeshD/OpenMesh/Core/Geometry/MatrixT.dhttp://www.dsource.org/projects/openmeshd/browser/trunk/OpenMeshD/OpenMesh/Core/Geometry/VectorT.d
I don't remember exactly where I ran into it, but if you've got a Vector!(N) it's pretty natural to want to write a template that matches only Vector! but for any N. If you're writing a template _function_ you can work around it by putting the type as one of the function args. But it's not always the case (for instance if you want to make a Traits class specialized on Vector!(N).
Comment #4 by bugzilla — 2008-02-21T01:05:01Z
This is exactly the same issue as 1454, but with values instead of types. It's intractable for the same reasons.
*** This bug has been marked as a duplicate of 1454 ***
Comment #5 by wbaxter — 2008-02-21T02:26:47Z
I seem to have been wrong in using function templates. C++ can't do that either. I think I must have thought it was simpler to demonstrate with functions when I tried to distill a test case from the original C++ to D. But it does work with structs in C++, intractible or not.
Working C++ (save to specialize.cpp and compile with "dmc specialize.cpp"):
#include <stdio.h>
template <int N>
struct Number {
static const int value = N;
};
template <typename T>
struct test_specialize
{
void talk() { printf("Not so special:\n"); }
};
template<int N>
struct test_specialize<Number<N> >
{
void talk() { printf("Ooh special - NUMBER N\n"); }
};
int main(int argc, char* argv[])
{
test_specialize< Number<5> > x;
x.talk();
return 0;
}
-----
Not working D:
module specialize;
import std.stdio;
struct Number(int N)
{
const int value = N;
}
struct test_specialize(T)
{
void talk() { writefln("Not so special:\n"); }
}
struct test_specialize(T : Number!(N))
{
void talk() { writefln("Ooh special - NUMBER N\n"); }
}
int main()
{
test_specialize!(Number!(5)) x;
x.talk();
return 0;
}
---
The C++ version outputs "ooh special", and the d version "Not so special".
Comment #6 by bugzilla — 2008-02-21T14:18:43Z
Ok, I agree that that's a bug. I'll fix it.
Comment #7 by wbaxter — 2008-02-29T07:59:47Z
Am I right in thinking this is the actual syntax that should work?:
struct test_specialize(T : Number!(N), int N)
{
void talk() { writefln("Ooh special - NUMBER N\n"); }
}
test_specialize!(Number!(5)) x;
That's a minor variation on an example in the docs:
template Foo(T: T[U], U)
{
...
}
Foo!(int[long]) // instantiates Foo with T set to int, U set to long
(But the int-arg version still doesn't work, I'm just checking that the above is what *should* work.)
Comment #8 by onlystupidspamhere — 2008-02-29T14:07:45Z
(In reply to comment #7)
> Am I right in thinking this is the actual syntax that should work?:
>
> struct test_specialize(T : Number!(N), int N)
> {
> void talk() { writefln("Ooh special - NUMBER N\n"); }
> }
> test_specialize!(Number!(5)) x;
I would expect it to be. But this issue isn't limited to int parameters. Other primitive types don't work either. Also, more complex pattern matching fails:
struct a(S, T) {}
struct b(T : a!(N, a!(N, int)), N) {}
alias a!(float,int) A;
alias a!(float, A) B;
b!(B, float) x; // fails, and so does b!(B) x;
foo.d:5: template instance b!(a!(float,a!(float,int) ) ,float) does not match any template declaration
foo.d:5: Error: b!(a!(float,a!(float,int) ) ,float) is used as a type
foo.d:5: variable foo.x voids have no value
This is a bit problematic for someone creating a new compiler for D since it's not really clear how complex types the language should be able to match. The specification only mentions (http://www.digitalmars.com/d/1.0/template.html) that they should be types. But that's a bit confusing since Number!(N) isn't a valid type. You can't e.g. alias it, there's no parametrized alias syntax:
alias IntNPair N = tuple!(int, N);
One could instantiate the code above with Number(float) or Number!(float), and get tuple!(int, float).