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!();
}

Reply via email to