I was browsing some Route netlink source code.
I wanted to figure out what was the value of RTNLGRP_NEIGH
Source: http://lxr.free-electrons.com/source/include/linux/rtnetlink.h?v=2.6.35#L550
541 /* RTnetlink multicast groups */ 542 enum rtnetlink_groups { 543 RTNLGRP_NONE, 544 #define RTNLGRP_NONE RTNLGRP_NONE 545 RTNLGRP_LINK, 546 #define RTNLGRP_LINK RTNLGRP_LINK 547 RTNLGRP_NOTIFY, 548 #define RTNLGRP_NOTIFY RTNLGRP_NOTIFY 549 RTNLGRP_NEIGH, 550 #define RTNLGRP_NEIGH RTNLGRP_NEIGH 551 RTNLGRP_TC, 552 #define RTNLGRP_TC RTNLGRP_TC 553 RTNLGRP_IPV4_IFADDR, 554 #define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR ... ... ... ... #define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR 585 RTNLGRP_PHONET_ROUTE, 586 #define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE 587 __RTNLGRP_MAX 588 }; 589 #define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
What is this enum with #define doing. What will be the value of RTNLGRP_NEIGH? 6 OR 3
Thanks
The value of RTNLGRP_NEIGH will be 3. You can easily test this with the following program.
#include <stdio.h> /* RTnetlink multicast groups */ enum rtnetlink_groups { RTNLGRP_NONE, #define RTNLGRP_NONE RTNLGRP_NONE RTNLGRP_LINK, #define RTNLGRP_LINK RTNLGRP_LINK RTNLGRP_NOTIFY, #define RTNLGRP_NOTIFY RTNLGRP_NOTIFY RTNLGRP_NEIGH, #define RTNLGRP_NEIGH RTNLGRP_NEIGH RTNLGRP_TC, #define RTNLGRP_TC RTNLGRP_TC RTNLGRP_IPV4_IFADDR, #define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR /* ... */ #define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_ROUTE, #define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE __RTNLGRP_MAX }; #define RTNLGRP_MAX (__RTNLGRP_MAX - 1) int main() { printf("RTNLGRP_NEIGH = %d\n", RTNLGRP_NEIGH); }
It outputs this:
RTNLGRP_NEIGH = 3
Since each macro is #defined to its own name, the RTNLGRP_NEIGH in main will be replaced by RTNLGRP_NEIGH. But since the expansion is not recursive, it will stop at this point and the program use the enum constant RTNLGRP_NEIGH which is the fourth and therefore has value 3.
If you are not sure what the preprocessor does, you can always compile with the -E switch and look at the pre-processed output. Compiling the above example with gcc -E gives (not showing 840 lines of the #included standard library headers)
# 4 "main.c" enum rtnetlink_groups { RTNLGRP_NONE, RTNLGRP_LINK, RTNLGRP_NOTIFY, RTNLGRP_NEIGH, RTNLGRP_TC, RTNLGRP_IPV4_IFADDR, RTNLGRP_PHONET_ROUTE, __RTNLGRP_MAX }; int main() { printf("RTNLGRP_NEIGH = %d\n", RTNLGRP_NEIGH); }
which is hopefully much less confusing.
The #defines mixed into the enum definition have no effect to the enum definition. It doesn't matter where the #defines are located. They could (and probably should) have been placed before or after the definition.
/* RTnetlink multicast groups */ enum rtnetlink_groups { RTNLGRP_NONE, RTNLGRP_LINK, RTNLGRP_NOTIFY, RTNLGRP_NEIGH, RTNLGRP_TC, RTNLGRP_IPV4_IFADDR, /* ... */ RTNLGRP_PHONET_ROUTE, __RTNLGRP_MAX }; #define RTNLGRP_NONE RTNLGRP_NONE #define RTNLGRP_LINK RTNLGRP_LINK #define RTNLGRP_NOTIFY RTNLGRP_NOTIFY #define RTNLGRP_NEIGH RTNLGRP_NEIGH #define RTNLGRP_TC RTNLGRP_TC #define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR #define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR /* ... */ #define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE #define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
The reason they wrote this weired code is probably that they wanted to refactor old code using
#define RTNLGRP_NONE 0 #define RTNLGRP_LINK 1 #define RTNLGRP_NOTIFY 2 #define RTNLGRP_NEIGH 3 #define RTNLGRP_TC 4 #define RTNLGRP_IPV4_IFADDR 5 /* ... */
to use an enum instead. But because existing code might rely on the fact that the identifiers are macros (such as testing #ifdef RTNLGRP_NEIGH) they wanted to provide macros with the same value. Note that this approach is flawed, however, because the preprocessor won't know the value of the constant so you cannot do things like #if RTNLGRP_NEIGH >= 3 which you could, had RTNLGRP_NEIGH been #defined to 3 literally. So, in essence, their approach combines the disadvantages of using macros (name-space pollution) with those of using enums (not available at pre-processing time).
A maybe more useful pattern I have seen before is to #define the constants to actual integers.
enum rtnetlink_groups { RTNLGRP_NONE #define RTNLGRP_NONE 0 = RTNLGRP_NONE, RTNLGRP_LINK #define RTNLGRP_LINK 1 = RTNLGRP_LINK, RTNLGRP_NOTIFY #define RTNLGRP_NOTIFY 2 = RTNLGRP_NOTIFY, RTNLGRP_NEIGH #define RTNLGRP_NEIGH 3 = RTNLGRP_NEIGH, RTNLGRP_TC #define RTNLGRP_TC 4 = RTNLGRP_TC, RTNLGRP_IPV4_IFADDR #define RTNLGRP_IPV4_IFADDR 5 = RTNLGRP_IPV4_IFADDR, /* ... */ };
which will be pre-processed to the following.
enum rtnetlink_groups { RTNLGRP_NONE = 0, RTNLGRP_LINK = 1, RTNLGRP_NOTIFY = 2, RTNLGRP_NEIGH = 3, RTNLGRP_TC = 4, RTNLGRP_IPV4_IFADDR = 5, };
Note that here, it is critical that the #defines are mixed into the enum definition, otherwise we'd get invalid code such as 3 = 3, instead of the desired RTNLGRP_NEIGH = 3.
Oh, and please don't use __RTNLGRP_MAX as an identifier. Names containing two adjacent underscores or beginning with an underscore followed by an upper-case letter are reserved by the C standard. Using them in your own code leads to undefined behavior.
The value of RTNLGRP_NEIGH will be 3 (it is the fourth enumeration constant: RTNLGRP_NONE has the value 0, RTNLGRP_LINK has the value 1, and RTNLGRP_NOTIFY has the value 2).
The #define stuff is somewhat weird ― it is the sort of thing that's apt to make people want to stop you using the C pre-processor.
The idea is that it gives you a macro for RTNLGRP_NEIGH that can be tested, but the expansion of the macro is the enumeration constant (spelled the same). There isn't an infinite loop in the expansions because once a macro has been expanded, it is not expanded again while the replacement text is being rescanned.
So, the upshot is that you can write:
#ifdef RTNLGRP_NEIGH …code using RTNLGRP_NEIGH… #endif