你用过宏##粘贴函数,然后用函数指针查找执行吗?

💡 原文中文,约3200字,阅读约需8分钟。
📝

内容提要

宏的使用不仅限于定义符号常量,还可以使用##运算符进行粘贴。#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注释。

🏷️

标签

➡️

继续阅读