Hi Martin, > I'm implementing a generic Vector!(uint d, T) struct (wrapping a T[d] array). > For readability: > alias Vector!(4u,float) float4; > alias Vector!(4u,int) int4; > > I'd like to be able to cast a float4 variable explicitly to an int4 > (component-wise casting): > auto f4 = float4(1,2,3,4); > auto i4 = cast(int4) f4; > > So when implementing opCast!NewType() in Vector!(d,T), I need a signature > constraint: NewType must be a Vector!(d2,T2), with d2 == d && isNumeric!T2. > The problem is that I don't know how to split NewType into the basic type > (Vector) and its parameters ({uint d2, T2}) as I didn't find anything related > in the std.traits documentation.
OK. First, to get access to a template parameters, the template must expose them by aliasing: template Vector(uint d, T) if (isNumeric!T) { alias d dim; alias T Type; T[d] values; } Now, d and T are externally accessible : alias Vector!(4, float) Float4; static assert(Float4.dim == 4); static assert(is(Float4.Type == int)); Now, there is no built-in way to see if a type is a template instantiation. A solution is to make a templated function do the work for you. Here we go: void vectorCheck(int d, T)(Vector!(d, T) v) {} vectorCheck can be instantiated only if passed a Vector!(someDim, SomeType). I used 'int d' due to some bugs with 'uint d'. Now, __traits offers the nifty 'compiles' instruction that checks at compile-time if a piece of code is valid D code. So we can create a template that checks if a type is indeed a Vector!(... , ...) template isVector(T) { static if (__traits(compiles, { void checkVector(int d, T)(Vector!(d,T) v) {} // define checkVector here checkVector(T.init); // use it with a value of type T: T.init })) // if the previous code compiles, then T is a Vector!(...,...) enum bool isVector = true; else enum bool isVector = false; } So, given a type T, isVector!T is true iff T is a Vector. Note that this template could easily be generalized: isInstanceOf!(someTemplateName, SomeType) Now, Vector V1 can be cast into a Vector V2 iff * V1.dim == V2.dim * is(V1.Type : V2.Type) I used the U : V syntax here, which means U is a kind of V, or U derive from V or U can be cast into a V. Which gives us the last building block: template canBeCastInto(V1, V2) if (isVector!V1 && isVector!V2) { static if ((V1.dim == V2.dim) && is(V1.Type : V2.Type)) // We know V1 and V2 are Vectors, we can use V1.dim and V1.Type enum bool canBeCastInto = true; else enum bool canBeCastInto = false; } Vector!(4,int) v1; Vector!(4,float) v2; assert( canBeCastInto!( typeof(v1), typeof(v2) )); // from int to float, yes assert( ! canBeCastInto!( typeof(v2), typeof(v1) )); // no cast from float to int Of course, you can relax the type condition at your leisure. I mean, you can cast floats into ints if you want to. Also, you could cast small vectors into longer ones, putting the residual coordinates to Type.init. Philippe