Saturday, February 12, 2022

Mapping enums to enums

Mapping enums to enums

 There is sometimes the need to translate one enumerate value to another. For example let's have the case of enum 'A' and 'B':

enum A
{
   eA0 = 0,
   eA1,
   eA2,
};

enum B
{
   eB0 = 0,
   eB1,
   eB2,
};

A simple translation without asserting on non existing values could work as follow:

B Translate(A eA)
{
    B e = eB0;

    switch (eA)
    {
    case eA0: e = eB0;  break;
    case eA1: e = eB1;  break;
    case eA2: e = eB2;  break;
    }

    return e;
}

This is a straightforward one to one mapping. With Compiler explorer one can see that the Visual Studio 2019 compiler translates this in a bunch of compare and jump statements:

B Translate(A) PROC
        test    ecx, ecx
        je      SHORT $LN4@Translate
        sub     ecx, 1
        je      SHORT $LN5@Translate
        cmp     ecx, 1
        jne     SHORT $LN4@Translate
        mov     eax, 2
        ret     0
$LN5@Translate:
        mov     eax, 1
        ret     0
$LN4@Translate:
        xor     eax, eax
        ret     0
B Translate(A) ENDP   

In this case the value are exact the same so a more optimal solution would be that it just uses the value:

B Translate2(A eA)
{
    return static_cast<B>(eA);
}

In Compiler explorer it can be seen now that this is denser and faster:

B Translate2(EA) PROC
        mov     eax, ecx
        ret     0
EB Translate2(EA) ENDP  

It seems that Visual Studio doesn't recognize this optimization by itself.

More robust

Above cast can be dangerous if values do not correspond. One can trap for this and for accidental changes by adding some static_asserts

enum A
{
   eAbegin = 0,
   eA0     = ABegin,
   eA1,
   eA2,
   eAend
};

B Translate2(A eA)
{
    static_assert(eAbegin == eBbegin);
    static_assert(eAend == eBend);

    // or (verbose and requires updating after adding an enum):
    static_assert(eA0 == eB0);
    static_assert(eA1 == eB1);
    static_assert(eA2 == eB2);
    
    return static_cast<B>(eA);
}
 

No comments:

Post a Comment

Careful with std::ranges

<ranges>   C++20 has added the the ranges library. Basically it works on ranges instead of iterators but added some subtle constraint...