1、libcpp中和宏相关的基础知识
libcpp是gcc的C/C++语言预处理器,gcc将C/C++语言预处理器cpp以库的形式独立出来,故取名曰libcpp。libcpp的输出为预处理标记cpp_token序列,为了实现回退机制、预处理指令处理和宏扩展,libcpp分三层输出预处理标记cpp_token序列:
a)_cpp_lex_direct:这是预处理标记序列输出的第一层,它直接对程序文件进行词法分析,不处理任何预处理指令,也不提供回退功能,它反映的是真实的程序文本;
b)cpp_lex_token:第二层在第一层的基础上实现了回退和预处理指令处理功能,调用这一层的用户看不到符合C/C++标准的预处理指令行;
c)cpp_get_token:这是最外一层,调用这个函数看到的将是C/C++标准中的翻译单元。这一层相对上一层主要是实现了宏扩展和##操作符处理功能。
2、预处理指令的处理
预处理指令的处理流程起始于cpp_lex_token函数,不考虑回退机制(回退机制将在后续文章中单独讨论),cpp_lex_token调用第一层分析函数_cpp_lex_direct,如果得到标记#,且其为当前行的第一个标记,转入_cpp_handle_directive。_cpp_handle_directive继续调用_cpp_lex_direct读取下一个标记,如果其为标识符(dname->type == CPP_NAME),并且为有效的预处理指令名字,转入相应的预处理指令处理函数。
为了提高效率,libcpp使用了两个小技巧。首先,为了快速判别出一个标识符是否为预处理指令名字,libcpp在标识符的存储结构cpp_hashnode中提供了一个is_directive标记,用于标识该标识符是否为预处理指令名字。另外,为了快速定位相应预处理指令的处理函数,libcpp将所有的预处理指令处理函数按顺序保存在一个数组当中,并且在cpp_hashnode中额外提供了一个directive_index成员,用于对处理函数数组索引。
处理函数数组的定义代码如下:
#define DIRECTIVE_TABLE /
D(define, T_DEFINE = 0, KANDR, IN_I) /* 270554 */ /
D(include, T_INCLUDE, KANDR, INCL | EXPAND) /* 52262 */ /
D(endif, T_ENDIF, KANDR, COND) /* 45855 */ /
D(ifdef, T_IFDEF, KANDR, COND | IF_COND) /* 22000 */ /
D(if, T_IF, KANDR, COND | IF_COND | EXPAND) /* 18162 */ /
D(else, T_ELSE, KANDR, COND) /* 9863 */ /
D(ifndef, T_IFNDEF, KANDR, COND | IF_COND) /* 9675 */ /
D(undef, T_UNDEF, KANDR, IN_I) /* 4837 */ /
D(line, T_LINE, KANDR, EXPAND) /* 2465 */ /