4
votes

I'd like to use a mixin to implement an interface. This works fine until I subclass. The problem is that the mixin template also implement an template function.

Something like:

interface Features {
    void feature1();
    void feature2();
}

mixin template FeaturesImplementer() {
    int bar;
    final void feature1(){}
    void feature2(){}
    void typeStuff(T)(){}
}

class WithFeature: Features {
    mixin FeaturesImplementer;
    this(){typeStuff!(typeof(this));}
}

class AlsoWithFeature: WithFeature {
    mixin FeaturesImplementer;
    this(){typeStuff!(typeof(this));}
}

void main() {
    new WithFeature;
    new AlsoWithFeature;
}

outputs:

Error: function AlsoWithFeature.FeaturesImplementer!().feature1 cannot override final function WithFeature.FeaturesImplementer!().feature1

Deprecation: implicitly overriding base class method WithFeature.FeaturesImplementer!().feature2 with AlsoWithFeature.FeaturesImplementer!().feature2 deprecated; add 'override' attribute

Error: mixin AlsoWithFeature.FeaturesImplementer!() error instantiating

I could put typeStuff in another template but the problem is that in FeaturesImplementer everything goes together. Even the integer is used by the features. Is there a way to always use the same mixin with everuthing inside ?

1
Why are you mixing it into the child class too? It will be inherited from the base class without that... - Adam D. Ruppe
because otherwise typestuff() dont work. It contains code that work on this, if not remixed, this is always of the same type where the template has been mixed. - user5573432
Ah, the language has an answer for that though: dlang.org/template#TemplateThisParameter - Adam D. Ruppe
Well, your problem is the final method. It just does not make sense to put a final method in a mixing that will be used like you do in your example. - DejanLekic

1 Answers

1
votes

You can make this work with a bit of static checking, using the static if expression, which evaluates at compile time. The idea is to verify if the implementation is already here, using the traits, particularly hasMember.

You'll also have to check using std.traits if it's the first time that the template is mixed, in your example it's equivalent to check if the ancestor (Object) is already a Feature.

For example:

interface Features {
    void feature1();
    void feature2();
}

mixin template FeaturesImplementer() {

    import std.traits: BaseClassesTuple;
    alias C = typeof(this);
    enum OlderHave = is(BaseClassesTuple!C[0] : Features);
    enum Have = is(C : Features);
    enum Base = Have & (!OlderHave);

    static if (Base || !__traits(hasMember, C, "bar"))
    int bar;

    static if (Base || !__traits(hasMember, C, "feature1"))
    final void feature1(){}

    static if (Base || !__traits(hasMember, C, "feature2"))
    void feature2(){}

    void typeStuff(T)(){}
}

class WithFeature: Features {
    mixin FeaturesImplementer;
    this(){typeStuff!(typeof(this));}
}

class AlsoWithFeature: WithFeature {
    mixin FeaturesImplementer;
    this(){typeStuff!(typeof(this));}
}

void main() {
    new WithFeature;
    new AlsoWithFeature;
}

The different function implementations are then only mixed if not already there.

Note that there was also a hidden problem in your original code due to the int field being redeclared, depending on the way you casted an instance, the field might be the one of WithFeature or AlsoWithFeature, but I don't know why the compiler did not complain about that.