Some `enum` tricks

On 2017/10/12 at 00:00

After delving into some C projects, I found the usage of enum is more powerful than I used to think. Here are some interesting tricks I learned.

Get the number of elements in a enum type

The need for getting the number of elements in a enum type is common. For example, enum nf_ct_ext_id is defined in linux kernel to specify the type of conntrack extension being used. We may need to know how many extensions are defined in the kernel. So adding the NF_CT_EXT_NUM at the end of the enum can do the job immediately. And adding or removing extention type in the enum will change the value of NF_CT_EXT_NUM automatically.

enum nf_ct_ext_id {
    NF_CT_EXT_HELPER,
    NF_CT_EXT_NAT,
    NF_CT_EXT_SEQADJ,
    NF_CT_EXT_ACCT,
    NF_CT_EXT_TSTAMP,
    NF_CT_EXT_NUM,
};

Using enum to define constants

The first look at enum ip_conntrack_status is a little overwhelming for me. After digging into the code, I found the use of enum ip_conntrack_status is for defining constant. The kernel does not use enum ip_conntrack_status as a type directly (You can found the references to the type is none.). And you can also find a lot of projects use untagged enum to define constant. The following code snippet is from iptable project.

enum {
    O_SET_MARK = 0,
    O_AND_MARK,
    O_OR_MARK,
    O_XOR_MARK,
    O_SET_XMARK,
    F_SET_MARK  = 1 << O_SET_MARK,
    F_AND_MARK  = 1 << O_AND_MARK,
    F_OR_MARK   = 1 << O_OR_MARK,
    F_XOR_MARK  = 1 << O_XOR_MARK,
    F_SET_XMARK = 1 << O_SET_XMARK,
    F_ANY       = F_SET_MARK | F_AND_MARK | F_OR_MARK |
                  F_XOR_MARK | F_SET_XMARK,
};

The discussion for using enum to define constants can be found in the following links.

To me, the most compelling reasons are

  1. The constants defined in the same enum can be thought as in the same group
  2. Constants defined in a enum have a symbol in the debugger's symbol table

sizeof(enum_type) == sizeof(int) which may not be acceptable for some applications

Although using enum to define a new type is common, but in most compiler the size of a enum type is the same as an integer. In some memory limited application, we may want the limit the size of enum type.

In C++11, we can bind the enum type to a existing type. For example:

enum Color : unsigned char {
    Red,
    Blue,
    Yellow
};

However, in C we don't have this kind of priviledge. I found some code use untagged enum to define constants and assign it to the smaller size variable. For example:

enum {
    XT_CONNMARK_SET = 0,
    XT_CONNMARK_SAVE,
    XT_CONNMARK_RESTORE
};

struct xt_connmark_tginfo1 {
    __u32 ctmark, ctmask, nfmask;
    __u8 mode;
};

The __u8 mode is used the save one of XT_CONNMARK_SET, XT_CONNMARK_SAVE, XT_CONNMARK_RESTORE. Instead of using the enum type directly, this example use only one byte to save the mode.

Comments