www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Calling all D template gurus: exposing the overload/template matching

In keeping with "design by introspection" I've been wanting to 
write library code that statically inspects the abilities of the 
entities it's passed and reacts accordingly. Unfortunately, this 
approach tends to hide implementation mistakes: there's no 
general way to distinguish between "this function doesn't support 
calling with an array" v.s. "this function has a typo in it when 
called with an array". The result is that the code generated in 
the library might be either completely wrong or at least 
unexpected. The error messages often appear in totally unrelated 
places.

I've been working on getting a way to distinguish at the 
call/instantiation site between two cases:
1) a call / instantiation couldn't find a match (e.g. failed 
template constraint)
2) a call / instantiation found a match, but that match failed to 
compile (e.g. implementation bug)

The code below is as far as I've got, as a potential candidate 
for inclusion in std.traits if there's a way of making it work. 
Two problems I'm having:
a) relies on the match being more specialised than a single 
variadic template arg
b) I can't get the last unittest assert to pass and it's totally 
confusing me.

Any help would be much appreciated :)
Also, if a compiler dev felt like just implementing a builtin 
trait for it instead, that would be great and almost certainly 
way faster to compile...

template isMatch(alias foo, TemplArgs...)
{
     struct DummyT { }
     template TestMatch(TestTemplArgs...)
     {
         DummyT TestMatch(RTArgs...)(auto ref RTArgs args)
         {
             return DummyT.init;
         }
     }
     alias olSet = foo;
     alias olSet = TestMatch;
     bool isMatch(RTArgs...)(auto ref RTArgs args)
     {
         static if (__traits(isTemplate, foo))
             return  !is(typeof(olSet!TemplArgs(args)) == DummyT);
         else
             return  !is(typeof(olSet(args)) == DummyT);
     }
}

 safe unittest
{
     template Tests()
     {
         static void foo0(int a);
         static assert(isMatch!foo0(3));
         static assert(!isMatch!foo0(3.0));

         struct S { }
         static S foo1();
         static assert(isMatch!foo1);
         static assert(!isMatch!foo1(3));

         template T0() if (false) { }
         static assert(!isMatch!T0);

         template T1() if (true) { }
         static assert(isMatch!T1);

         template T2() if (false) { }
         template T2() if (true) { }
         static assert(isMatch!T2);

         void foo2(Q = float, T = short)(T t) if (!is(Q == real) 
&& !is(T == float))
         {
             static assert(!is(Q == T));
         }

         static assert(isMatch!foo2(3));
         static assert(isMatch!(foo2, int, long)(3));
         static assert(!__traits(compiles, foo2!(int, int)(3))); 
// internal static assertion...
         static assert(isMatch!(foo2, int, int)(3)); // but *is* a 
match :)
         static assert(isMatch!(foo2, double)(3));
         static assert(!isMatch!(foo2, real)(3));
         static assert(!__traits(compiles, foo2(3.0f)));
         static assert(!isMatch!foo2(3.0f)); // FAILS HERE, I'm 
confused
     }

     alias t = Tests!();
}
Feb 24 2017