为什么在枚举中#define 相同的项目?
Why #define same items in an enum?
在很多系统头文件中,我们可以看到
/* Standard well-defined IP protocols. */
enum
{
IPPROTO_IP = 0, /* Dummy protocol for TCP. */
#define IPPROTO_IP IPPROTO_IP
IPPROTO_ICMP = 1, /* Internet Control Message Protocol. */
#define IPPROTO_ICMP IPPROTO_ICMP
...
}
enum EPOLL_EVENTS
{
EPOLLIN = 0x001,
#define EPOLLIN EPOLLIN
EPOLLPRI = 0x002,
#define EPOLLPRI EPOLLPRI
...
}
#define
的目的是什么?
通过简单地包含这样一个 header,将 对正在生成的常量 没有影响。这就是为什么对于大多数熟悉 header 定义的程序员来说,这看起来很奇怪。
系统headers
#define
和 enum
都导致相同类型的 compile-time 常量。我认为开发人员希望确保任何 重新定义 可能散落在(可能更旧的)代码库中都会 弹出 作为编译器警告。一种可能的情况是系统 header 在升级后支持更多同类定义,其中依赖代码已经有不同的定义。
另一点是通过 #if
或 #ifdef
使 pre-processor 可用的常量的存在使 compile-time 分支成为可能。
Linux内核
一些系统 headers 也必须能够在复杂的条件下工作而不破坏代码,例如,如果 wide-spread 存在干扰核心系统的库,这就是 Marco Bonelli 已显示。
正常headers
在“正常”headers(仅限于一个项目并受其控制)中做这样的事情只会导致混乱。
您通常决定对 compile-time 常量使用 #define
或 enum
样式。如果您更改一次决策,请同时更改给定域的所有常量。
一种奇怪的方式可以从这里同时获得好处,让 enum
语法自动增加一个计数器。这是通过省略枚举数的显式值来完成的:
enum {
IPPROTO_IP,
#define IPPROTO_IP IPPROTO_IP
IPPROTO_ICMP,
#define IPPROTO_ICMP IPPROTO_ICMP
// ...
};
这将导致 IPPROTO_IP
成为 0
并且 IPPROTO_ICMP
成为 1
。
您的代码似乎取自 Linux 的 uapi/linux/in.h
header 文件。这些定义是在 Linux v3.12-rc1 中的 this commit 中引入的。正如提交消息所解释的那样:
The kernel promises not to break the UAPI ABI so I don't
see why we can't just have the two userspace headers
coordinate?
If you include the kernel headers first you get those,
and if you include the glibc headers first you get those,
and the following patch arranges a coordination and
synchronization between the two.
这里还有glibc中的sibling commit供参考
因此添加了这些以与用户空间 C 库 (glibc)“同步”并避免冲突。然而,这现在提出了一个问题:为什么 glibc 有这些定义开始?好吧,只是为了在库的几个部分使用 #ifdef
指令。比如glibc中可以看到sysdeps/posix/getaddrinfo.c
:
static const struct gaih_typeproto gaih_inet_typeproto[] =
{
{ 0, 0, 0, false, "" },
{ SOCK_STREAM, IPPROTO_TCP, 0, true, "tcp" },
{ SOCK_DGRAM, IPPROTO_UDP, 0, true, "udp" },
#if defined SOCK_DCCP && defined IPPROTO_DCCP
{ SOCK_DCCP, IPPROTO_DCCP, 0, false, "dccp" },
#endif
#ifdef IPPROTO_UDPLITE
{ SOCK_DGRAM, IPPROTO_UDPLITE, 0, false, "udplite" },
#endif
#ifdef IPPROTO_SCTP
{ SOCK_STREAM, IPPROTO_SCTP, 0, false, "sctp" },
{ SOCK_SEQPACKET, IPPROTO_SCTP, 0, false, "sctp" },
#endif
{ SOCK_RAW, 0, GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE, true, "raw" },
{ 0, 0, 0, false, "" }
};
由于这些定义也被导出,当包含 netinet/in.h
时,用户也可以在他们的用户空间程序中以类似的方式依赖这些定义。
因此,总结一下:由于用户可以包含 glibc 的 netinet/in.h
或 Linux 的 linux/in.h
,因此这些文件已同步,Linux 适应 glibc,并添加 glibc header.
提供的相同定义
在很多系统头文件中,我们可以看到
/* Standard well-defined IP protocols. */
enum
{
IPPROTO_IP = 0, /* Dummy protocol for TCP. */
#define IPPROTO_IP IPPROTO_IP
IPPROTO_ICMP = 1, /* Internet Control Message Protocol. */
#define IPPROTO_ICMP IPPROTO_ICMP
...
}
enum EPOLL_EVENTS
{
EPOLLIN = 0x001,
#define EPOLLIN EPOLLIN
EPOLLPRI = 0x002,
#define EPOLLPRI EPOLLPRI
...
}
#define
的目的是什么?
通过简单地包含这样一个 header,将 对正在生成的常量 没有影响。这就是为什么对于大多数熟悉 header 定义的程序员来说,这看起来很奇怪。
系统headers
#define
和 enum
都导致相同类型的 compile-time 常量。我认为开发人员希望确保任何 重新定义 可能散落在(可能更旧的)代码库中都会 弹出 作为编译器警告。一种可能的情况是系统 header 在升级后支持更多同类定义,其中依赖代码已经有不同的定义。
另一点是通过 #if
或 #ifdef
使 pre-processor 可用的常量的存在使 compile-time 分支成为可能。
Linux内核
一些系统 headers 也必须能够在复杂的条件下工作而不破坏代码,例如,如果 wide-spread 存在干扰核心系统的库,这就是 Marco Bonelli 已显示。
正常headers
在“正常”headers(仅限于一个项目并受其控制)中做这样的事情只会导致混乱。
您通常决定对 compile-time 常量使用 #define
或 enum
样式。如果您更改一次决策,请同时更改给定域的所有常量。
一种奇怪的方式可以从这里同时获得好处,让 enum
语法自动增加一个计数器。这是通过省略枚举数的显式值来完成的:
enum {
IPPROTO_IP,
#define IPPROTO_IP IPPROTO_IP
IPPROTO_ICMP,
#define IPPROTO_ICMP IPPROTO_ICMP
// ...
};
这将导致 IPPROTO_IP
成为 0
并且 IPPROTO_ICMP
成为 1
。
您的代码似乎取自 Linux 的 uapi/linux/in.h
header 文件。这些定义是在 Linux v3.12-rc1 中的 this commit 中引入的。正如提交消息所解释的那样:
The kernel promises not to break the UAPI ABI so I don't
see why we can't just have the two userspace headers
coordinate?
If you include the kernel headers first you get those,
and if you include the glibc headers first you get those,
and the following patch arranges a coordination and
synchronization between the two.
这里还有glibc中的sibling commit供参考
因此添加了这些以与用户空间 C 库 (glibc)“同步”并避免冲突。然而,这现在提出了一个问题:为什么 glibc 有这些定义开始?好吧,只是为了在库的几个部分使用 #ifdef
指令。比如glibc中可以看到sysdeps/posix/getaddrinfo.c
:
static const struct gaih_typeproto gaih_inet_typeproto[] =
{
{ 0, 0, 0, false, "" },
{ SOCK_STREAM, IPPROTO_TCP, 0, true, "tcp" },
{ SOCK_DGRAM, IPPROTO_UDP, 0, true, "udp" },
#if defined SOCK_DCCP && defined IPPROTO_DCCP
{ SOCK_DCCP, IPPROTO_DCCP, 0, false, "dccp" },
#endif
#ifdef IPPROTO_UDPLITE
{ SOCK_DGRAM, IPPROTO_UDPLITE, 0, false, "udplite" },
#endif
#ifdef IPPROTO_SCTP
{ SOCK_STREAM, IPPROTO_SCTP, 0, false, "sctp" },
{ SOCK_SEQPACKET, IPPROTO_SCTP, 0, false, "sctp" },
#endif
{ SOCK_RAW, 0, GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE, true, "raw" },
{ 0, 0, 0, false, "" }
};
由于这些定义也被导出,当包含 netinet/in.h
时,用户也可以在他们的用户空间程序中以类似的方式依赖这些定义。
因此,总结一下:由于用户可以包含 glibc 的 netinet/in.h
或 Linux 的 linux/in.h
,因此这些文件已同步,Linux 适应 glibc,并添加 glibc header.