OK, let's try again. I used to be confronted to this pb too. Here is what I did:
/** Alias itself to true if T is an instance of templ. To obtain the template parameters, see TemplateParametersTypeTuple. Example: ---- auto cy = cycle([0,1,2,3]); // cy is a Cycle!(int[]) alias typeof(cy) Cy; assert(isInstanceOf!(Cy, Cycle)); ---- */ template isInstanceOf(T, alias templ) { static if (T.stringof.length >= __traits(identifier, templ).length && T.stringof[0..__traits(identifier, templ).length] == __traits(identifier, templ)) enum bool isInstanceOf = true; else enum bool isInstanceOf = false; } /** Alias itself to true iff templ is a template name (standard, function, class or struct template). */ template isTemplate(alias templ) { static if (is(typeof(templ) == void) && is(typeof(templ.stringof))) enum bool isTemplate = true; else enum bool isTemplate = false; } The converse is a bit more complicated: given a type T, of which you know it's a template instantiation (T == U!someTypes), get the (someTypes) as a typetuple. /** CTFE function to search a string s for a begin char b and an end char e. returns [s[0..index of b], s[index of b+1, index of e], s[index of e+1, .. $]] Above all else, it's done for a pair of enclosing chars, like ( and ) As such, it does not stop at the first ')' after a '(' but will count the correct numbers of parenthesis. So, between!('(',')', "Foo!(A, B!(int, double), C)") will return ["Foo!", "A,B!(int,double),C", ""] and _not_ ["Foo!", "A,B!(int,double", ", C)"] This may not work so well with non-paired characters. */ string[3] between(char b, char e, string s)() { int foundb; int ib; string notFound = ""; foreach(i,c; s) { if (c==b) { if (foundb == 0) { foundb = 1; ib = i+1; continue; } else { ++foundb; } } if (c==e) { if (foundb == 1) { return [s[0..ib-1], s[ib..i], s[i+1..$]]; // before b, between b and e, after e. Standard case. } else { --foundb; } } } return [s, notFound,notFound]; // no b found, explored the whole string } /** Takes a type instantiating a template (that is, T == A!(someTypes...) for some A) and becomes the template's parameters typetuple: TypeTuple!(someTypes) in the previous example. It won't work for alias parameters, because they're not imported. Example: ---- assert(is(TemplateParametersTypeTuple!(Cycle!(int[])) == TypeTuple!(int[]))); ---- */ template TemplateParametersTypeTuple(T) { mixin("alias TypeTuple!(" ~ between!('(',')',T.stringof)[1] ~ ") TemplateParametersTypeTuple;"); } As a nice side-effect, you can also extract the template name: /** If T is a template instantiation, becomes the template name. For a non-templated type, it just becomes this type name. ---- struct Foo(T...) {} alias Foo!(int, double) Foo_id; assert(TemplateName!(Foo_id) == "Foo"); assert(TemplateName!(int) == "int"); ---- */ template TemplateName(T) { enum string TemplateName = between!('!','(',T.stringof)[0]; } So, given T, you can - determine if it's a template instantiation or not (TBD) - extract the template name (and so, test for their equality) - extract the template parameters, as a TypeTuple I used this to transfer template parameters from one template to another: ToBar(someFoo) -> test if it's a template -> extract the parameters -> instantiate a Bar!Parameters. So, given a Bar!(int,double), it creates a Foo!(int,double). It's a kind of function, from any templated type to Foo... A sort of projection, if you will. > And what does it happen if two templates coming from two modules share the > same name? Say I have a Foo in module A, another in module B. In my main module, when I create a Foo, I have to indicate if that's a A.Foo or a B.Foo. Similarly, any use of Foo has to be qualified, or else the compiler will complain. Halas, it then cuts all qualifiying information from the names :( So: alias A.Foo!(int, double) S1; isInstanceOf(S1, Foo; // Foo does not exist -> doesn't compile isInstanceOf(S1, A.Foo); // compiles, returns true; isInstanceOf(S1, B.Foo); // compiles also (OK), returns true (NOK !) I tested with a static import and it did not change anything. Philippe