By now, I'm suspecting that I'm missing the point entirely, but here it is.

The idea is this:
Given
    interface A {...}
    interface B : A {...}
    struct S {supposed to implement B}
use alias this to enable implicit conversions:
    S to Concept!(B, S)
    Concept!(B, S) to Concept!(A, S)
The number of alias this hops then decides which overload is taken.

No rights reserved.


import std.traits: BaseTypeTuple;

mixin template implements(Iface) {
@property inout(Concept!(Iface, typeof(this))) _concept()() inout {
        return typeof(return)(this);
    }
    alias _concept this;

    // ensure that the interface is implemented
    alias typeof(this) Self;
    private class _Tester : Iface {
        Self s;
        mixin ForwardInterface!(s, Iface);
    }
}

struct Concept(Iface, Impl) {
    Impl impl;
    mixin ForwardInterface!(impl, Iface);

    alias BaseTypeTuple!Iface B;
    static if(B.length == 1) {
@property inout(Concept!(B, typeof(impl))) _concept()() inout {
            return typeof(return)(impl);
        }
        alias _concept this;
    } else {
        // because multiple alias this aren't here yet
        static assert(B.length == 0);
    }
}

import std.traits: ParameterTypeTuple, ReturnType;
import std.string: format;

mixin template ForwardInterface(alias target, Iface) {
mixin ForwardMembers!(target, Iface, __traits(allMembers, Iface));
}
mixin template ForwardMembers(alias target, Iface, memberNames ...) {
    static if(memberNames.length > 0) {
        mixin ForwardOverloads!(target, memberNames[0],
            __traits(getOverloads, Iface, memberNames[0]));
        mixin ForwardMembers!(target, Iface, memberNames[1 .. $]);
    }
}
mixin template ForwardOverloads(
    alias target,
    string memberName,
    overloads ...
) {
    static if(overloads.length > 0) {
        alias overloads[0] o;
        mixin(format(
            q{
ReturnType!o %1$s(ParameterTypeTuple!o args) %2$-(%s %) {
                    target.%1$s(args);
                }
            },
            memberName,
            [
                (is(typeof(o) == immutable) ? "immutable" : ""),
                (is(typeof(o) == const) ? "const" : ""),
                (is(typeof(o) == shared) ? "shared" : "")
            ]
        ));
mixin ForwardOverloads!(target, memberName, overloads[1 .. $]);
    }
}

// example from http://forum.dlang.org/post/qqnwpprluchyaphva...@forum.dlang.org

interface A1 {void foo();}
interface A2 : A1 {void bar();}
interface B1 {void fun();}
interface B2 : B1 {void gun();}

struct S1 {
    mixin implements!A2;
    void foo() {}
    void bar() {}
}

struct S2 {
    mixin implements!B2;
    void fun() {}
    void gun() {}
}

int dostuff(X, Y)(Concept!(A1, X) a, Concept!(B1, Y) b) {return 1;} int dostuff(X, Y)(Concept!(A2, X) a, Concept!(B1, Y) b) {return 2;}

import std.stdio;
void main() {
    S1 s1;
    S2 s2;
writeln(dostuff(s1, s2)); // calls the more specialized Overload-2
}

// But, if we add the following overload, then the above
// function call dostuff(s1, s2) doesn't know whether to
// call Overload-2 or, the equally specialized, Overload-3:

int dostuff(X, Y)(Concept!(A1, X) a, Concept!(B2, Y) b) {return 3;}

// And, if we add yet another, more specialized, overload,
// then the previous ambiguity goes away:

int dostuff(X, Y)(Concept!(A2, X) a, Concept!(B2, Y) b) {return 4;}

Reply via email to