How to protect enum assignment

社会主义新天地 提交于 2019-12-10 19:20:59

问题


I want to prevent invalid value enum assignment. I know if i even assign value that is not in enum it will work. Example:

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 = 43; // also will work
  bar(the_enum); // this function assumes that input parameter is correct
}

Is there easy, efficient way to check if assignment to enum is correct? I could test value by function

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

  // do something with valid enum
}

I could resolve this in following way:

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;
}

For me, my solution is inefficient, how can i write this if i have more enums and more values in enums?


回答1:


As long as the enum is continuous one can do something like this:

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; }

Another alternative is to not validate the enum value beforehand, but error out once the code detects an invalid value:

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

But there is no way to enforce that the enum value doesn't go out of the range at all in C. The best you can do if you want to secure the enum against fiddling is to hide the value away in a struct and then have functions to manipulate the struct. The function and struct implementation can be hidden away from the user via a forward declaration in the .h file and the implementation in the .c file:

struct example_t {
     enum example_enum value;
}

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



回答2:


There is no way of warning about assigning integers that fit into the enum.

Enumerators in C are synonyms for integer types. Assuming the type chosen for enum example_enum is int, then your code is identical to:

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
}

You could use structures, but even that can be circumvented:

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

Basically if you want to restrict a type to specific values, you will need to perform checks.




回答3:


If anyone is interested in this topic, here I have some solution which works.

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;
}

Pros:

  • enum value assignment works like struct assignment
  • enum for pointers also works
  • enum value can't be changed

Cons:

  • can't be used in switch
  • can't be directly compared
  • can't directly return enum number value

Note: sorry for abusing preprocessor here



来源:https://stackoverflow.com/questions/39725331/how-to-protect-enum-assignment

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!