user5573432
user5573432

Reputation:

Warning about overriden methods when using a mixin

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 ?

Upvotes: 4

Views: 104

Answers (1)

Abstract type
Abstract type

Reputation: 1921

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.

Upvotes: 1

Related Questions