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 (it is the fourth enumeration constant:RTNLGRP_NONE
has the value 0,RTNLGRP_LINK
has the value 1, andRTNLGRP_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:
The value of
RTNLGRP_NEIGH
will be 3. You can easily test this with the following program.It outputs this:
Since each macro is
#define
d to its own name, theRTNLGRP_NEIGH
inmain
will be replaced byRTNLGRP_NEIGH
. But since the expansion is not recursive, it will stop at this point and the program use theenum
constantRTNLGRP_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 withgcc -E
gives (not showing 840 lines of the#include
d standard library headers)which is hopefully much less confusing.
The
#define
s mixed into theenum
definition have no effect to theenum
definition. It doesn't matter where the#define
s are located. They could (and probably should) have been placed before or after the definition.The reason they wrote this weired code is probably that they wanted to refactor old code using
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, hadRTNLGRP_NEIGH
been#define
d to3
literally. So, in essence, their approach combines the disadvantages of using macros (name-space pollution) with those of usingenum
s (not available at pre-processing time).A maybe more useful pattern I have seen before is to
#define
the constants to actual integers.which will be pre-processed to the following.
Note that here, it is critical that the
#define
s are mixed into theenum
definition, otherwise we'd get invalid code such as3 = 3,
instead of the desiredRTNLGRP_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.