39

I found this code in the linux headers, /usr/include/dirent.h:

enum   
  {    
    DT_UNKNOWN = 0,
# define DT_UNKNOWN DT_UNKNOWN
    DT_FIFO = 1,
# define DT_FIFO    DT_FIFO   
    DT_CHR = 2,
# define DT_CHR     DT_CHR
    DT_DIR = 4,
# define DT_DIR     DT_DIR
    DT_BLK = 6,
# define DT_BLK     DT_BLK
    DT_REG = 8,
# define DT_REG     DT_REG
    DT_LNK = 10,
# define DT_LNK     DT_LNK
    DT_SOCK = 12,
# define DT_SOCK    DT_SOCK   
    DT_WHT = 14
# define DT_WHT     DT_WHT
  };   

What is this construct for? - why define something with an identical string, which will then compile to the int value?

GrahamW
  • 571
  • 5
  • 13
  • Maybe there were defines only, and someone added the enum later. – Chris Dec 21 '11 at 10:58
  • also see comments by leushenko and anol on this answer: https://stackoverflow.com/a/33608836/15161 – slashmais Jan 29 '22 at 11:30
  • LLVM uses this idiom to define enum types that signify logical groups of opcodes: https://llvm.org/doxygen/IR_2Instruction_8h_source.html#l00853, https://github.com/llvm/llvm-project/blob/cbde0d9c7be1991751dc3eb5928294d2e00ef26a/llvm/include/llvm/IR/Instruction.def – Hari Jun 27 '23 at 07:57

5 Answers5

24

Besides the other answers which are very good - I would go with them for the main reason - the compiler could generate warnings or errors if you try to redefine DT_UNKNOWN.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
23

My guess is that some other code can then check if one of (or more of) these enum values have been defined using #ifdef.

Sebastiaan M
  • 5,775
  • 2
  • 27
  • 28
  • 3
    The best of both worlds - check for definition with `#ifdef`, but still shows up as a symbolic name in debugging. – Chris Lutz Dec 21 '11 at 10:58
  • Yes, this is probably the case. And as Chris says in the comment to the question, maybe it is legacy - the enum being added later, but the ability to #ifdef on them being needed still. – GrahamW Dec 21 '11 at 11:02
  • 7
    +1: this trick is [described in `gcc`'s `cpp` documentation](http://gcc.gnu.org/onlinedocs/gcc-4.6.2/cpp/Self_002dReferential-Macros.html#Self_002dReferential-Macros) as a useful use of self-referential macros. – Matthew Slattery Dec 21 '11 at 18:36
12

My (uneducated) guess is that the #define statements allow conditional tests to see if the constant has been defined.

For example:

#ifdef DT_UNKNOWN
    // do something
#endif
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
7

I think that Luchian Grigore's answer was correct.

Code without defines:

#include <stdio.h>

// Defined somewhere in headers
#define DT_UNKNOWN 0

enum
{
    DT_UNKNOWN = 0,
    DT_FIFO = 1,
};

int main(int argc, char **argv)
{
    printf("DT_UNKNOWN is %d\n", DT_UNKNOWN);
    return 0;
}

From the compiler's output it is unclear why some line of code inside enum doesn't want to build:

alexander@ubuntu-10:~/tmp$ gcc -Wall ./main.c 
./main.c:7: error: expected identifier before numeric constant

After we add such defines:

#include <stdio.h>

// Defined somewhere in headers
#define DT_UNKNOWN 0

enum
{
    DT_UNKNOWN = 0,
    # define DT_UNKNOWN DT_UNKNOWN
    DT_FIFO = 1,
    # define DT_FIFO    DT_FIFO
};

int main(int argc, char **argv)
{
    printf("DT_UNKNOWN is %d\n", DT_UNKNOWN);
    return 0;
}

Compiler would tell us that DT_UNKNOWN is redefined and a place where it is redefined:

alexander@ubuntu-10:~/tmp$ gcc -Wall ./main2.c 
./main2.c:7: error: expected identifier before numeric constant
./main2.c:8:1: warning: "DT_UNKNOWN" redefined
./main2.c:3:1: warning: this is the location of the previous definition
alexander
  • 2,703
  • 18
  • 16
0

I used -E and -dD arguments (and also -fdump-tree-all) in gcc to see the preprocessor output and found nothing useful. So I guess this code does not have any functionality other than perhaps displaying the symbolic names when debugging using a debugger like gdb.

Sangeeth Saravanaraj
  • 16,027
  • 21
  • 69
  • 98