traits: how to split parametrized type into basic type and parameters
Philippe Sigaud
philippe.sigaud at gmail.com
Mon Feb 14 02:09:11 PST 2011
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
More information about the Digitalmars-d-learn
mailing list