Linux内核的__is_constexpr宏
Linux Kernel的__is_constexpr(x)宏如何工作?它的目的是什么?什么时候介绍?为什么要介绍?
/* * This returns a constant expression while determining if an argument is * a constant expression,most importantly without evaluating the argument. * Glory to Martin Uecker <Martin.Uecker@med.uni-goettingen.de> */ #define __is_constexpr(x) (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8))) 有关解决相同问题的不同方法的讨论,请参阅:Detecting Integer Constant Expressions in Macros 解决方法
Linux内核的__is_constexpr宏
介绍 可以在Linux Kernel的include/kernel/kernel.h中找到__is_constexpr(x)宏: /* * This returns a constant expression while determining if an argument is * a constant expression,most importantly without evaluating the argument. * Glory to Martin Uecker <Martin.Uecker@med.uni-goettingen.de> */ #define __is_constexpr(x) (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8))) 它是在2018-04-05的Linux Kernel v4.17,commit 3c8ba0d61d04的合并窗口中引入的;虽然围绕它的讨论开始于一个月前. 该宏值得注意的是利用C标准的细微细节:条件运算符确定其返回类型的规则(6.5.15.6)和空指针常量的定义(6.3.2.3.3). 此外,它依赖于允许的sizeof(void)(并且与sizeof(int)不同),这是GNU C extension. 它是如何工作的? 宏的身体是: (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8))) 让我们关注这一部分: ((void *)((long)(x) * 0l)) 注意:(long)(x)强制转换为intended,以允许x具有指针类型并避免在32位平台上对u64类型发出警告.但是,这个细节对于理解宏的关键点并不重要. 如果x是整数常量表达式(6.6.6),那么((long)(x)* 0l)是值为0的整数常量表达式.因此,(void *)((long)(x)* 0l)是空指针常量(6.3.2.3.3):
如果x不是整数常量表达式,则(void *)((long)(x)* 0l)不是空指针常量,无论其值如何. 知道了,我们可以看到之后会发生什么: 8 ? ((void *)((long)(x) * 0l)) : (int *)8 注意:第二个8字面是intended,以避免编译器警告有关创建指向未对齐地址的指针.前8个文字可能只是1.但是,这些细节对于理解宏的关键点并不重要. 这里的关键是条件运算符返回一个不同的类型,具体取决于其中一个操作数是否为空指针常量(6.5.15.6):
因此,如果x是一个整型常量表达式,那么第二个操作数是空指针常量,因此表达式的类型是第三个操作数的类型,它是指向int的指针. 否则,第二个操作数是指向void的指针,因此表达式的类型是指向void的指针. 因此,我们最终有两种可能性: sizeof(int) == sizeof(*((int *) (NULL))) // if `x` was an integer constant expression sizeof(int) == sizeof(*((void *)(....))) // otherwise 根据GNU C extension,sizeof(void)== 1.因此,如果x是整数常量表达式,则宏的结果为1;否则,0. 此外,由于我们只是比较两个sizeof表达式的相等性,结果本身是另一个整数常量表达式(6.6.3,6.6.6):
因此,总之,如果参数是整数常量表达式,__ is_constexpr(x)宏将返回值为1的整数常量表达式.否则,它返回值为0的整数常量表达式. 为什么要介绍? 该宏来自during the effort以从Linux内核中删除所有Variable Length Arrays (VLAs). 为了方便这一点,需要在内核范围内启用GCC’s 当启用警告时,结果发现GCC报告了许多阵列为VLA的情况,而这些情况并非如此.例如在fs/btrfs/tree-checker.c: #define BTRFS_NAME_LEN 255 #define XATTR_NAME_MAX 255 char namebuf[max(BTRFS_NAME_LEN,XATTR_NAME_MAX)]; 开发人员可能希望max(BTRFS_NAME_LEN,XATTR_NAME_MAX)已解析为255,因此应将其视为标准数组(即非VLA).但是,这取决于max(x,y)宏扩展到什么. 关键问题是如果数组的大小不是C标准定义的(整数)常量表达式,GCC会生成VLA代码.例如: #define not_really_constexpr ((void)0,100) int a[not_really_constexpr]; 根据C90标准,((void)0,100)不是常数表达式(6.6),因为使用了逗号运算符(6.6.3).在这种情况下,GCC选择发布VLA代码,even when it knows the size is a compile-time constant.相比之下,Clang没有. 由于内核中的max(x,y)宏不是常量表达式,因此GCC触发了警告并生成了内核开发人员不想要的VLA代码. 因此,一些内核开发人员试图开发max和其他宏的替代版本以避免警告和VLA代码.一些尝试试图利用GCC’s 在某些时候,Martin Uecker proposed一个特别聪明的方法,没有使用builtins(taking inspiration从glibc’s tgmath.h): #define ICE_P(x) (sizeof(int) == sizeof(*(1 ? ((void*)((x) * 0l)) : (int*)1))) 虽然该方法使用GCC扩展,it was nevertheless well-received并且被用作__is_constexpr(x)宏背后的关键思想,它在与其他开发人员进行几次迭代后出现在内核中.然后使用该宏来实现max宏和其他需要为常量表达式的宏,以避免GCC生成VLA代码. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- linux – rmdir因“设备或资源忙”而失败
- Linux文件系统缓存:将数据从Dirty移动到Writeback
- linux – 有没有办法在两个服务器的目录之间传播chown / ch
- character-encoding – 在awk匹配函数的字符串参数中使用特
- linux – 如何从其他tty上运行的shell进程中获取bash历史记
- linux – 让新文件继承其文件夹的扩展ACL
- GlusterFS快照备份解决方案
- linux – 在Spark sbin /文件夹中的stop-all.sh没有停止所有
- Linux 搭建私有git服务器
- pthread条件变量vs win32事件(linux vs windows-ce)