Detect when multiple enum items map to same value

后端 未结 8 2116
猫巷女王i
猫巷女王i 2020-11-28 13:44

Is there a compile-time way to detect / prevent duplicate values within a C/C++ enumeration?

The catch is that there are multiple items which are initialize

相关标签:
8条回答
  • 2020-11-28 14:08

    While we do not have full on reflection, you can solve this problem if you can relist the enumeration values.

    Somewhere this is declared:

    enum E { A = 0, B = 0 };
    

    elsewhere, we build this machinery:

    template<typename S, S s0, S... s>
    struct first_not_same_as_rest : std::true_type {};
    template<typename S, S s0, S s1, S... s>
    struct first_not_same_as_rest : std::integral_constant< bool,
      (s0 != s1) && first_not_same_as_rest< S, s0, s... >::value
    > {};
    
    
    template<typename S, S... s>
    struct is_distinct : std::true_type {};
    
    template<typename S, S s0, S... s>
    struct is_distinct : std::integral_constant< bool,
      std::is_distinct<S, s...>::value &&
      first_not_same_as_rest< S, s0, s... >::value
    > {};
    

    Once you have that machinery (which requires C++11), we can do the following:

    static_assert( is_distinct< E, A, B >::value, "duplicate values in E detected" );
    

    and at compile time we will ensure that no two elements are equal.

    This requires O(n) recursion depth and O(n^2) work by the compiler at compile time, so for extremely large enums this could cause problems. A O(lg(n)) depth and O(n lg(n)) work with a much larger constant factor can be done by sorting the list of elements first, but that is much, much more work.

    With the enum reflection code proposed for C++1y-C++17, this will be doable without relisting the elements.

    0 讨论(0)
  • 2020-11-28 14:14

    I don't believe there's a way to detect this with the language itself, considering there are conceivable cases where you'd want two enumeration values to be the same. You can, however, always ensure all explicitly set items are at the top of the list:

    typedef enum
    {
      MsgFoo1A = BASE1_VAL,       // 5
      MsgFoo2A = BASE2_VAL,       // 7
      MsgFoo1B,                   // 8
      MsgFoo1C,                   // 9
      MsgFoo1D,                   // 10
      MsgFoo1E,                   // 11
      MsgFoo2B                    // 12
    } FOO;
    

    So long as assigned values are at the top, no collision is possible, unless for some reason the macros expand to values which are the same.

    Usually this problem is overcome by giving a fixed number of bits for each MsgFooX group, and ensuring each group does not overflow it's allotted number of bits. The "Number of bits" solution is nice because it allows a bitwise test to determine to which message group something belongs. But there's no built-in language feature to do this because there are legitimate cases for an enum having two of the same value:

    typedef enum
    {
        gray = 4, //Gr[ae]y should be the same
        grey = 4,
        color = 5, //Also makes sense in some cases
        couleur = 5
    } FOO;
    
    0 讨论(0)
提交回复
热议问题