【问题标题】:How to protect enum assignment如何保护枚举分配
【发布时间】:2017-02-05 02:59:56
【问题描述】:

我想防止无效值枚举分配。我知道如果我什至分配不在枚举中的值,它就会起作用。示例:

enum example_enum
{
    ENUM_VAL0,
    ENUM_VAL1,
    ENUM_VAL2,
    ENUM_VAL3
};

void example_function(void)
{
  enum example_enum the_enum = ENUM_VAL3; // correct
  the_enum = 41; // will work
  the_enum = 0xBADA55; // also will work
  bar(the_enum); // this function assumes that input parameter is correct
}

是否有简单有效的方法来检查对枚举的分配是否正确?我可以按功能测试值

void foo(enum example_enum the_enum)
{
  if (!is_enum(the_enum))
    return;

  // do something with valid enum
}

我可以通过以下方式解决这个问题:

static int e_values[] = { ENUM_VAL0, ENUM_VAL1, ENUM_VAL2, ENUM_VAL3 };
int is_enum(int input)
{
  for (int i=0;i<4;i++)
    if (e_values[i] == input)
      return 1;
  return 0;
}

对我来说,我的解决方案效率低下,如果我有更多的枚举和枚举中的更多值,我该怎么写?

【问题讨论】:

  • 编译器警告?
  • 使用断言。在 C 中,enums 实际上是 int。 C 不是 C++。

标签: c testing enums compile-time compile-time-constant


【解决方案1】:

没有办法警告分配适合枚举的整数。

C 中的枚举数是整数类型的同义词。假设为 enum example_enum 选择的类型是 int,那么您的代码与以下内容相同:

void example_function(void)
{
  int the_enum = ENUM_VAL3; // correct
  the_enum = 12345; // will work
  bar(the_enum); // this function assumes that input parameter is correct
}

void foo(int the_enum)
{
  if (!is_enum(the_enum))
    return;
  // do something with valid enum
}

你可以使用结构,但即使这样也可以绕过:

struct example_enum_struct e = { 12345 };
e.value = 23456;

基本上,如果您想将类型限制为特定值,则需要执行检查。

【讨论】:

    【解决方案2】:

    只要enum 是连续的,就可以这样做:

    static int e_values[] = { ENUM_VAL0, ENUM_VAL1, ENUM_VAL2, ENUM_VAL3, ENUM_VAL_COUNT };
    
    int is_enum(int input) { return 0 <= input && input < ENUM_VAL_COUNT; }
    

    另一种选择是不预先验证枚举值,但一旦代码检测到无效值就会出错:

    switch(input) {
        case ENUM_VAL0: ... break;
        case ENUM_VAL1: ... break;
        ...
        default:
            assert(0 && "broken enum"); 
            break;
    } 
    

    但是没有办法强制 enum 值在 C 中不会超出范围。如果您想保护 enum 免受摆弄,最好的办法就是隐藏该值远离struct,然后具有操作struct 的功能。函数和struct 实现可以通过.h 文件中的前向声明和.c 文件中的实现对用户隐藏:

    struct example_t {
         enum example_enum value;
    }
    
    void example_set_val0(example_t* v) { v->value = ENUM_VAL0; }
    

    【讨论】:

    • 好吧,我认为使用return 0 &lt;= input &amp;&amp; input &lt; ENUM_VAL_COUNT; 是最好的解决方案,但仅适用于连续枚举。不幸的是,我有 usb_class 枚举,它是锯齿状的。我必须把它放到结构中
    【解决方案3】:

    如果有人对此主题感兴趣,我有一些可行的解决方案。

    typed_enums.h

    #ifndef TYPED_ENUMS_H
    #define TYPED_ENUMS_H
    
    #define TYPED_ENUM(name_) \
        typedef struct { int v; } name_
    
    #define TYPED_ENUM_VALUE(name_, value_)       (name_) { value_ }
    #define GET_TYPED_ENUM_VALUE(en_)             (en_.v)
    #define TYPED_ENUM_EQ(a_, b_)                 (GET_TYPED_ENUM_VALUE(a_) == GET_TYPED_ENUM_VALUE(b_))
    
    #endif
    

    usb_class.h

    #ifndef USB_CLASS_H
    #define USB_CLASS_H
    
    #include "typed_enums.h"
    
    TYPED_ENUM(UsbClass);
    
    #define USB_CLASS_BILLBOARD                     TYPED_ENUM_VALUE(UsbClass, 0x11)
    #define USB_CLASS_TYPE_C_BRIDGE                 TYPED_ENUM_VALUE(UsbClass, 0x12)
    #define USB_CLASS_DIAGNOSTIC_DEVICE             TYPED_ENUM_VALUE(UsbClass, 0xDC)
    #define USB_CLASS_WIRELESS_CONTROLLER           TYPED_ENUM_VALUE(UsbClass, 0xE0)
    
    #endif
    

    usb_class_example.c

    #include "typed_enums.h"
    #include "usb_class.h"
    
    #include <stdio.h>
    
    int main(int argc, char ** argv)
    {
        UsbClass usbClass = USB_CLASS_WIRELESS_CONTROLLER;
        usbClass = 12345; // tadam!!!! throws error
        usbClass = USB_CLASS_VIDEO;
        if (TYPED_ENUM_EQ(usbClass, USB_CLASS_VIDEO)) {
            printf("usbClass = USB_CLASS_VIDEO\n");
        }
    
        printf("usb class value: %02X\n", GET_TYPED_ENUM_VALUE(usbClass));
    
        return 0;
    }
    

    优点:

    • 枚举值赋值类似于结构赋值
    • 指针的枚举也有效
    • 枚举值无法更改

    缺点:

    • 不能在开关中使用
    • 不能直接比较
    • 不能直接返回枚举数值

    注意:很抱歉在这里滥用预处理器

    【讨论】:

      猜你喜欢
      • 2014-09-20
      • 2020-02-05
      • 2020-01-25
      • 2012-04-20
      • 2016-04-13
      • 1970-01-01
      • 1970-01-01
      • 2015-04-06
      • 2020-08-16
      相关资源
      最近更新 更多