你用过宏##粘贴函数,然后用函数指针查找执行吗?
内容提要
宏的使用不仅限于定义符号常量,还可以使用##运算符进行粘贴。#define指令用于定义明示常量,宏的名称不能有空格,可以使用C注释。宏参数列表中的省略号可以使用预定义宏__VA_ARGS__表示。##运算符可以将两个记号组合成一个记号。在Linux内核中,可以使用##粘贴选择要使用的函数。在C++中,可以将宏定义为指针列表,实现指针调用。
延伸解读
##运算符:从常量定义到代码生成
文章展示了##运算符如何将两个记号组合成一个新记号,从而在预处理阶段生成变量名或函数名。例如,def_u32_array(sample_buffer, 64)展开为uint32_t array_sample_buffer[64]。这种技巧在Linux内核中用于根据参数选择不同的函数,如__pcpu_size_call_return通过##粘贴调用raw_cpu_read_1、raw_cpu_read_2等。读者可借此理解宏在代码生成方面的潜力,但需注意宏展开的调试难度。
变参宏与__VA_ARGS__的实用场景
文章提到宏参数列表中的省略号可用__VA_ARGS__表示,实现可变参数宏。例如PR宏可接受不同数量的参数,并展开为printf调用。这种机制适合封装日志或调试输出,减少重复代码。但需注意,变参宏在参数为空或类型不匹配时可能引发编译错误,且过度使用会降低代码可读性。
Linux内核中的宏粘贴实践
文章以Linux内核的__pcpu_size_call_return和__this_cpu_read为例,说明##粘贴如何根据变量大小选择对应的raw_cpu_read_x函数,并通过switch语句实现分派。这种模式在内核中用于优化性能,但要求开发者熟悉宏展开规则和类型安全。读者可从中学习如何用宏减少重复代码,但应谨慎评估可维护性。
C++中宏定义指针列表的调用方式
文章简要提及在C++中可将宏定义为指针列表,通过名称查找并调用函数。示例中FuncPtrTemplate和AddFunc宏用于注册和调用函数,如IsExit。这种方式可实现简单的反射或回调机制,但依赖宏展开和静态初始化,可能增加编译时间,且调试信息不直观。读者需权衡灵活性与代码清晰度。
Q&A
宏定义除了定义常量还能做什么?
宏还可以使用##运算符进行粘贴,将两个记号组合成一个记号,用于生成函数名、变量名等。
##运算符在宏中具体有什么作用?
##运算符称为预处理的粘合剂,可以把两个记号组合成一个记号,用于类函数宏或对象宏的替换部分。
如何在Linux内核中使用##粘贴来选择函数?
在Linux内核中,通过##粘贴将前缀和函数名组合,例如__pcpu_size_call_return宏使用##选择raw_cpu_read_x函数,根据变量大小调用不同的函数。
C++中如何用宏实现函数指针列表调用?
在C++中,可以将宏定义为指针列表,通过宏参数生成函数指针映射,然后根据名称查找并调用对应的函数。
宏参数列表中的省略号有什么用途?
省略号表示可变参数,在替换部分中使用__VA_ARGS__来代表省略号所代表的参数,实现可变参数宏。
定义宏时有哪些命名规则和注意事项?
宏的名称不能有空格,必须遵循C变量命名规则:只能使用字母、数字和下划线,且首字符不能是数字。可以在#define行使用标准C注释。