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

Reply via email to