I've fixed several limitations in std.traits.fullyQualifiedName / packageName / moduleName but still have issues with templated types / functions:

currently:
struct A{}
std.traits.fullyQualifiedName!(A!(int)) => CT error.

attempt to fix it:

----
template Stringof(alias T){
static if (!isCallable!T) enum Stringof = T.stringof;
else enum Stringof = __traits(identifier, T);
}
template isTemplateInstantiation(alias T){
import std.algorithm;
enum isTemplateInstantiation=Stringof!T.canFind(`!`);
}
template fullyQualifiedName(alias T)
{
static if (isTemplateInstantiation!T){
enum s=Stringof!T;
import std.algorithm;
enum s2=s.findSplit("!");
mixin(`alias temp=`~s2[0]~`;`);
enum fullyQualifiedName =fullyQualifiedName!temp~s2[1]~s2[2];
}
        else{...}
}

version(unittest){
  struct A(T1,T2){}
}
unittest{
  static assert(fullyQualifiedName!(A!(int,double)) == "util.traits.A!(int, double)");
}
----

however, it works only when "A" is visible in the scope of fullyQualifiedName, so it's pretty useless as it is. A potential fix would be to require the user to use a mixin (mixin(fullyQualifiedNameMixin!(A!double)) ) but that's ugly.

What we need:
__traits(getTemplate, A!T) => A
__traits(getTemplateArguments, A!(T,"foo")) => (T,"foo") (ie returns a tuple as in parameterTypeTuple or similar)

any thoughts?