July 12, 2013
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
1 2
Next ›   Last »