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