On 2013-07-11, 20:22, Namespace wrote:

What should he do?

As far as I can see he has 3 options:
1. An external file with the enum information. Both classes would import it and could use the same enum. But he cannot change the API, so this is no real option.

2. Change test1 into this:
----
void test1() {
        B b = cast(B) this.a;
        MyStaticClass.test2(b);
}
----
This works fine. But is it safe? And is it good style?
And how is this cast converted? Is it cheap?

3. Change test2 so that it accepts (even) (u)int. But then he lose the Type safety.

Does anyone have any advice?

This might be an option:


version (unittest) {
    enum A {
        foo = 1,
        bar = 2,
    }

    enum B {
        foo = 1,
        bar = 2,
    }

    enum C {
        foo = 3,
        bar = 4,
    }

    enum D {
        baz,
        qux,
    }
}

template sameMembers(T, U) {
    template sameValue(string member) {
enum sameValue = __traits(getMember, T, member) == __traits(getMember, U, member);
    }
    import std.typetuple : allSatisfy;
    enum sameMembers =
        sameMemberNames!(T,U) &&
        allSatisfy!(sameValue, __traits(allMembers, T));
} unittest {
    assert(sameMembers!(A,A));
    assert(sameMembers!(A,B));
    assert(sameMembers!(B,A));
    assert(sameMembers!(B,B));

    assert(!sameMembers!(A,C));
    assert(!sameMembers!(B,C));
    assert(!sameMembers!(C,A));
    assert(!sameMembers!(C,B));
}

template sameMemberNames(T, U) {
    template Has(Type) {
        template Has(string member) {
            enum Has = __traits(hasMember, Type, member);
        }
    }
    import std.typetuple : allSatisfy;
    enum sameMemberNames =
        allSatisfy!(Has!T, __traits(allMembers, U)) &&
        allSatisfy!(Has!U, __traits(allMembers, T));
} unittest {
    assert(sameMemberNames!(A,A));
    assert(sameMemberNames!(A,B));
    assert(sameMemberNames!(A,C));
    assert(sameMemberNames!(B,A));
    assert(sameMemberNames!(B,B));
    assert(sameMemberNames!(B,C));
    assert(sameMemberNames!(C,A));
    assert(sameMemberNames!(C,B));
    assert(sameMemberNames!(C,C));
    assert(sameMemberNames!(D,D));

    assert(!sameMemberNames!(A,D));
    assert(!sameMemberNames!(B,D));
    assert(!sameMemberNames!(C,D));
    assert(!sameMemberNames!(D,A));
    assert(!sameMemberNames!(D,B));
    assert(!sameMemberNames!(D,C));
}

T ConvertEnum(T,U)(U value) if (sameMembers!(T,U)) {
    return cast(T)value;
}

T ConvertEnum(T,U)(U value) if (sameMemberNames!(T,U) && !sameMembers!(T,U)) {
    final switch (value) {
        foreach (e; __traits(allMembers, U)) {
            case __traits(getMember, U, e):
                return __traits(getMember, T, e);
        }
    }
    assert(false);
} unittest {
    assert(ConvertEnum!A(B.foo) == A.foo);
    assert(ConvertEnum!A(B.bar) == A.bar);
    assert(ConvertEnum!A(C.foo) == A.foo);
    assert(ConvertEnum!A(C.bar) == A.bar);

    assert(ConvertEnum!B(A.foo) == B.foo);
    assert(ConvertEnum!B(A.bar) == B.bar);
    assert(ConvertEnum!B(C.foo) == B.foo);
    assert(ConvertEnum!B(C.bar) == B.bar);

    assert(ConvertEnum!C(A.foo) == C.foo);
    assert(ConvertEnum!C(A.bar) == C.bar);
    assert(ConvertEnum!C(B.foo) == C.foo);
    assert(ConvertEnum!C(B.bar) == C.bar);

    assert(!__traits(compiles, { auto tmp = ConvertEnum!D(A.foo); }));
    assert(!__traits(compiles, { auto tmp = ConvertEnum!D(B.foo); }));
    assert(!__traits(compiles, { auto tmp = ConvertEnum!D(C.foo); }));
    assert(!__traits(compiles, { auto tmp = ConvertEnum!A(D.foo); }));
    assert(!__traits(compiles, { auto tmp = ConvertEnum!B(D.foo); }));
    assert(!__traits(compiles, { auto tmp = ConvertEnum!C(D.foo); }));
}


The function ConvertEnum safely converts from one enum to another, given
that the same members exist in both. If the enums are equal (same values for
each member), a simple cast is used. If the names are equal, but values are
different, a switch/case is built, and A.Foo is converted to B.Foo.

--
Simen

Reply via email to