4.1.3.1.2.1.4.4. 创建宏定义 – ISO模式
我们已经看到,标准模式是基于符号(token)的,因此对于每个看到的符号,都要以下面的形式保存其信息。
175 struct cpp_token in cpplib.h
176 {
177 fileline line; /* Logical line of first char of token. */
178 unsigned short col; /* Column of first char of token. */
179 ENUM_BITFIELD(cpp_ttype) type : CHAR_BIT; /* token type */
180 unsigned char flags; /* flags - see above */
181
182 union
183 {
184 cpp_hashnode *node; /* An identifier. */
185 const cpp_token *source; /* Inherit padding from this token. */
186 struct cpp_string str; /* A string, or number. */
187 unsigned int arg_no; /* Argument no. for a CPP_MACRO_ARG. */
188 } val;
189 };
_cpp_aligned_alloc从cpp_reader的a_buff 中分配长度为len的非临时的,对齐的存储空间。这个缓存用于保存#define中的宏替换列表,及解析#assert,#unassert或#if中的答案(连带可能的宏展开)。
在cpp_reader中,有base_run——这是用来保存一般的符号(包括宏的名字),a_buff用于保存宏的展开体等,不可等同视之的符号,另u_buff则是保存预处理中遇到的字符串。因此,下面_cpp_create_definition的1486行,调用_cpp_aligned_alloc为解析宏展开体准备。
1522 unsigned char *
1523 _cpp_aligned_alloc (cpp_reader *pfile, size_t len) in cpplex.c
1524 {
1525 _cpp_buff *buff = pfile->a_buff;
1526 unsigned char *result = buff->cur;
1527
1528 if (len > (size_t) (buff->limit - result))
1529 {
1530 buff = _cpp_get_buff (pfile, len);
1531 buff->next = pfile->a_buff;
1532 pfile->a_buff = buff;
1533 result = buff->cur;
1534 }
1535
1536 buff->cur = result + len;
1537 return result;
1538 }
下面的_cpp_create_definition在一次调用中,将读入并处理宏定义中的所有符号。这意味着当我们从其返回时,我们可能已经在源文件中前进了一大段。
1479 bool
1480 _cpp_create_definition (cpp_reader *pfile, cpp_hashnode *node) in cppmacro.c
1481 {
1482 cpp_macro *macro;
1483 unsigned int i;
1484 bool ok;
1485
1486 macro = (cpp_macro *) _cpp_aligned_alloc (pfile, sizeof (cpp_macro));
1487 macro->line = pfile->directive_line;
1488 macro->params = 0;
1489 macro->paramc = 0;
1490 macro->variadic = 0;
1491 macro->used = ! CPP_OPTION (pfile, warn_unused_macros);
1492 macro->count = 0;
1493 macro->fun_like = 0;
1494 /* To suppress some diagnostics. */
1495 macro->syshdr = pfile->map->sysp != 0;
1496
1497 if (CPP_OPTION (pfile, traditional))
1498 ok = _cpp_create_trad_definition (pfile, macro);
1499 else
1500 {
1501 cpp_token *saved_cur_token = pfile->cur_token;
1502
1503 ok = create_iso_definition (pfile, macro);
1504
1505 /* Restore lexer position because of games lex_expansion_token()
1506 plays lexing the macro. We set the type for SEEN_EOL() in
1507 cpplib.c.
1508
1509 Longer term we should lex the whole line before coming here,
1510 and just copy the expansion. */
1511 saved_cur_token[-1].type = pfile->cur_token[-1].type;
1512 pfile->cur_token = saved_cur_token;
1513
1514 /* Stop the lexer accepting __VA_ARGS__. */
1515 pfile->state.va_args_ok = 0;
1516 }
4.1.3.1.2.1.4.4.1. 提取符号
GCC实现的是边读入边展开的预处理,所有的符号都将被保存入a_buff所持有的缓存,但宏定义的符号除外。这些符号需要另外保存,这样在遇到需要宏展开的地方,直接将其替换之。为此,上面1501行,首先需要保存当前符号的游标,而后完成宏解析后,在1512行恢复这个游标,为宏解析所保存在a_buff中的符号将被新符号覆盖,以避免宏的递归。
1383 static bool
1384 create_iso_definition (cpp_reader *pfile, cpp_macro *macro) in cppmacro.c
1385 {
1386 cpp_token *token;
1387 const cpp_token *ctoken;
1388
1389 /* Get the first token of the expansion (or the '(' of a
1390 function-like macro). */
1391 ctoken = _cpp_lex_token (pfile);
下面704行的lookaheads为非0,如果我们预读了符号;否则我们需要从pfile的buffer中通过_cpp_lex_direct获取符号(现在PCH文件中宏的定义已经在pfile的buffer中)。
691 const cpp_token *
692 _cpp_lex_token (cpp_reader *pfile) in cpplex.c
693 {
694 cpp_token *result;
695
696 for (;;)
697 {
698 if (pfile->cur_token == pfile->cur_run->limit)
699 {
700 pfile->cur_run = next_tokenrun (pfile->cur_run);
701 pfile->cur_token = pfile->cur_run->base;
702 }
703
704 if (pfile->lookaheads)
705 {
706 pfile->lookaheads--;
707 result = pfile->cur_token++;
708 }
709 else
710 result = _cpp_lex_direct (pfile);
711
712 if (result->flags & BOL)
713 {
714 /* Is this a directive. If _cpp_handle_directive returns
715 false, it is an assembler #. */
716 if (result->type == CPP_HASH
717 /* 6.10.3 p 11: Directives in a list of macro arguments
718 gives undefined behavior. This implementation
719 handles the directive as normal. */
720 && pfile->state.parsing_args != 1
721 && _cpp_handle_directive (pfile, result->flags & PREV_WHITE))
722 continue;
723 if (pfile->cb.line_change && !pfile->state.skipping)
724 pfile->cb.line_change (pfile, result, pfile->state.parsing_args);
725 }
726
727 /* We don't skip tokens in directives. */
728 if (pfile->state.in_directive)
729 break;
730
731 /* Outside a directive, invalidate controlling macros. At file
732 EOF, _cpp_lex_direct takes care of popping the buffer, so we never
733 get here and MI optimization works. */
734 pfile->mi_valid = false;
735
736 if (!pfile->state.skipping || result->type == CPP_EOF)
737 break;
738 }
739
740 return result;
741 }
上面的712行,BOL表示符号在行的开头,而在716行的CPP_HASH表示符号是#(因此,连同后跟的符号,可以找到一个指示)。同时在720行,state的parsing_args如果非0,表示正在解析一个函数型宏的参数,因为指示作为宏参数的行为没有定义,GCC的实现仅将其视为普通的参数。除此之外,则将接下来读入的符号按指示处理。
不过,在PCH文件中,没有#符号出现(在产生PCH文件时,所有指示都被处理了)。PCH文件中的宏可能看起来像如下例子:
D4 00 00 00 08 00 00 00 “strndupa(s,n) (__extension__ ({ __const char *__old = (s); size_t __len = strnlen (__old, (n)); char *__new = (char *) __builtin_alloca (__len + 1); __new[__len] = '/0'; (char *) memcpy (__new, __old, __len); }))” 0C
在上面的字符串中,由“”包含的数据是ASCII的形式,其他的是2进制形式。其中,D4 00 00 00 08 00 00 00构成macrodef_struct,因为数据在Linux平台上获取,D4 00 00 00是d4,这是definition_length,跟着的08 00是8,它是name_length,而00 00是0,为 flags。
对于行变化(获取了行的第一个符号),也需要更新行号。但对于PCH文件,这没有什么意义。
4.1.3.1.2.1.4.4.2. 解析参数
对于非函数型宏,没有参数可解析,由1404行分支处理。
create_iso_definition (continue)
1393 if (ctoken->type == CPP_OPEN_PAREN && !(ctoken->flags & PREV_WHITE))
1394 {
1395 bool ok = parse_params (pfile, macro);
1396 macro->params = (cpp_hashnode **) BUFF_FRONT (pfile->a_buff);
1397 if (!ok)
1398 return false;
1399
1400 /* Success. Commit the parameter array. */
1401 BUFF_FRONT (pfile->a_buff) = (uchar *) ¯o->params[macro->paramc];
1402 macro->fun_like = 1;
1403 }
1404 else if (ctoken->type != CPP_EOF && !(ctoken->flags & PREV_WHITE))
1405 cpp_error (pfile, CPP_DL_PEDWARN,
1406 "ISO C requires whitespace after the macro name");
对于函数型的宏,左括号和宏名字之间不能插入空格(空格,tab,在ISO模式下注释)。对于正确的形式,parse_params将解析参数列表。
1271 static bool
1272 parse_params (cpp_reader *pfile, cpp_macro *macro) in cppmacro.c
1273 {
1274 unsigned int prev_ident = 0;
1275
1276 for (;;)
1277 {
1278 const cpp_token *token = _cpp_lex_token (pfile);
1279
1280 switch (token->type)
1281 {
1282 default:
1283 /* Allow/ignore comments in parameter lists if we are
1284 preserving comments in macro expansions. */
1285 if (token->type == CPP_COMMENT
1286 && ! CPP_OPTION (pfile, discard_comments_in_macro_exp))
1287 continue;
1288
1289 cpp_error (pfile, CPP_DL_ERROR,
1290 "/"%s/" may not appear in macro parameter list",
1291 cpp_token_as_text (pfile, token));
1292 return false;
1293
1294 case CPP_NAME:
1295 if (prev_ident)
1296 {
1297 cpp_error (pfile, CPP_DL_ERROR,
1298 "macro parameters must be comma-separated");
1299 return false;
1300 }
1301 prev_ident = 1;
1302
1303 if (_cpp_save_parameter (pfile, macro, token->val.node))
1304 return false;
1305 continue;
1306
1307 case CPP_CLOSE_PAREN:
1308 if (prev_ident || macro->paramc == 0)
1309 return true;
1310
1311 /* Fall through to pick up the error. */
1312 case CPP_COMMA:
1313 if (!prev_ident)
1314 {
1315 cpp_error (pfile, CPP_DL_ERROR, "parameter name missing");
1316 return false;
1317 }
1318 prev_ident = 0;
1319 continue;
1320
1321 case CPP_ELLIPSIS:
1322 macro->variadic = 1;
1323 if (!prev_ident)
1324 {
1325 _cpp_save_parameter (pfile, macro,
1326 pfile->spec_nodes.n__VA_ARGS__);
1327 pfile->state.va_args_ok = 1;
1328 if (!CPP_OPTION (pfile, c99) && CPP_OPTION (pfile, pedantic))
1329 cpp_error (pfile, CPP_DL_PEDWARN,
1330 "anonymous variadic macros were introduced in C99");
1331 }
1332 else if (CPP_OPTION (pfile, pedantic))
1333 cpp_error (pfile, CPP_DL_PEDWARN,
1334 "ISO C does not permit named variadic macros");
1335
1336 /* We're at the end, and just expect a closing parenthesis. */
1337 token = _cpp_lex_token (pfile);
1338 if (token->type == CPP_CLOSE_PAREN)
1339 return true;
1340 /* Fall through. */
1341
1342 case CPP_EOF:
1343 cpp_error (pfile, CPP_DL_ERROR, "missing ')' in macro parameter list");
1344 return false;
1345 }
1346 }
1347 }
在这个函数中,变量prev_ident表示最后一个看到的符号是识别符(identifer),而在1322行的variadic表示宏类似于可变参数函数——在参数列表中包含了…。可变参数宏的细节由【4】给出如下。
宏可以被声明为接受数目可变的参数,就像函数那样。定义这样宏的语法类似于定义这样的函数。这里有一个例子: #define eprintf(…) fprintf (stderr, __VA_ARGS__) 这种宏叫做可变参数宏。当这个宏被调用时,在最后一个具名实参(这个宏没有)包括逗号后,所有的符号视作可变参数。这一符号序列替代宏定义体内所出现的__VA_ARGS__。因而,我们得到如下的展开: eprintf (“%s:%d: “, input_file, lineno) à fprintf (stderr, “%s:%d: “, input_file, lineno) 在插入宏展开前,可变参数已完全宏展开了(macro-expanded),就像普通参数那样。你可以使用#及##操作符,来字符化这个可变参数,或将其中的开头或结尾的符号与其他符号粘贴起来(但参考下面关于##的一个重要、特殊的案例)。 如果你的宏很复杂,你可能希望给可变参数更可读的名字而不是__VA_ARGS__。GNU CPP,作为扩展,允许这样。你可以直接在…前写出参数名;这个名字就被用作可变参数。上面的eprintf宏,使用这个扩展可被写作 #define eprintf(args…) fprintf (stderr, args) 不能在同一个宏里同时使用__VA_ARGS__和这个扩展。 在可变参数宏里你同样可以使用具名实参。我们可以这样定义eprintf如下: #define eprintf(format, …) fprintf(stderr, format, __VA_ARGS__) 这个形式更具可读性,然而不幸的是它不那么灵活:你必须在格式字串后提供至少一个实参。在标准C中,你不能忽略把可变参数及具名参数隔开的逗号。更甚,如果你将不传实参给可变参数,你会得到一个语法错误,因为在格式字串后多了个逗号。 eprintf(“suecess!/n”, ); à fprintf(stderr, “success!/n”, ); GUN CPP有一组扩展来处理这个问题。首先,允许可变实参为空: eprintf(“sueccess!/n”) à fprintf(stderr, “success!/n”, ); 其次,##符号粘贴操作符,当被放置在可变参数及逗号之间时,有特别含义。如果写作 #define eprintf(format, …) fprintf(stderr, format, ##_VA_ARGS__) 那么如果使用eprintf时,可变实参部分为空,##之前的逗号就会被删掉。如果你传入整个空的参数(即不是##),或##之前是逗号以外的符号,不会发生这个删除。 C99要求识别符__VA_ARGS__唯一可以出现的地方是可变参数宏的替换列表中(replacement list of a variadic macro)。它不能用作宏的名字,宏的参数名,或在别的类型的宏里。它也可能不能用在开放文本中;标准对此模棱两可。 可变参数宏是C99中的新特性。GNU CPP早已支持,但仅对于居名的可变参数(arg…,而不是…及__VA_ARGS__)。如果关注与旧版本GCC的可移植问题,你应该只使用具名可变参数。另一方面,如果关注与其他符合C99的编译器的移植问题,应该只使用__VA_ARGS__。 之前的GNU CPP实现了更为通用的逗号删除扩展。在这个版本里我们限制了这个扩展,以使得和C99的差异最小化。为了在当前版本及之前版本中取得同样的效果,在这个特殊的##之前的符号必须是逗号,而且逗号之前必须是空格: #define eprintf(format, args…) fprintf (stderr, format, ##args) |
对于找到的参数,在上面_cpp_lex_token的1278行,在parse_params中,识别符对应的cpp_hashnode已被创建(如果已存在,则返回这个已存在的节点,注意它可能代表源代码中别的同名变量),并被存入cpp_token的node域中。
1238 bool in cppmacro.c
1239 _cpp_save_parameter (cpp_reader *pfile, cpp_macro *macro, cpp_hashnode *node)
1240 {
1241 unsigned int len;
1242 /* Constraint 6.10.3.6 - duplicate parameter names. */
1243 if (node->flags & NODE_MACRO_ARG)
1244 {
1245 cpp_error (pfile, CPP_DL_ERROR, "duplicate macro parameter /"%s/"",
1246 NODE_NAME (node));
1247 return true;
1248 }
1249
1250 if (BUFF_ROOM (pfile->a_buff)
1251 < (macro->paramc + 1) * sizeof (cpp_hashnode *))
1252 _cpp_extend_buff (pfile, &pfile->a_buff, sizeof (cpp_hashnode *));
1253
1254 ((cpp_hashnode **) BUFF_FRONT (pfile->a_buff))[macro->paramc++] = node;
1255 node->flags |= NODE_MACRO_ARG;
1256 len = macro->paramc * sizeof (union _cpp_hashnode_value);
1257 if (len > pfile->macro_buffer_len)
1258 {
1259 pfile->macro_buffer = xrealloc (pfile->macro_buffer, len);
1260 pfile->macro_buffer_len = len;
1261 }
1262 ((union _cpp_hashnode_value *) pfile->macro_buffer)[macro->paramc - 1]
1263 = node->value;
1264
1265 node->value.arg_index = macro->paramc;
1266 return false;
1267 }
在_cpp_save_parameter中,这个cpp_hashnode节点及其value部分分别被保存入对齐的缓存cpp_reader (a_buff )和cpp_macro中的macro_buffer中(考虑到这个节点可能代表别的语法成分)。而在macro->paramc保存了宏参数的个数,参数对应的cpp_hashnode节点,在1265行被更新为保存参数的序号。
解析了参数列表后,接下来是解析宏展开体。这由lex_expansion_token完成,这个函数首先调用alloc_expansion_token来为进入的符号提供来自cpp_reader的对齐缓存。注意在下面的1366行,pfile->cur_token原来是使用base_run里的缓存,现由a_buff提供。
1350 static cpp_token *
1351 alloc_expansion_token (cpp_reader *pfile, cpp_macro *macro) in cppmacro.c
1352 {
1353 if (BUFF_ROOM (pfile->a_buff) < (macro->count + 1) * sizeof (cpp_token))
1354 _cpp_extend_buff (pfile, &pfile->a_buff, sizeof (cpp_token));
1355
1356 return &((cpp_token *) BUFF_FRONT (pfile->a_buff))[macro->count++];
1357 }
上面的BUFF_FRONT定义为#define BUFF_FRONT(BUFF) ((BUFF)->cur)。而域macro->count记录了宏展开体的符号的个数。
1361 static cpp_token *
1362 lex_expansion_token (cpp_reader *pfile, cpp_macro *macro) in cppmacro.c
1363 {
1364 cpp_token *token;
1365
1366 pfile->cur_token = alloc_expansion_token (pfile, macro);
1367 token = _cpp_lex_direct (pfile);
1368
1369 /* Is this a parameter? */
1370 if (token->type == CPP_NAME
1371 && (token->val.node->flags & NODE_MACRO_ARG) != 0)
1372 {
1373 token->type = CPP_MACRO_ARG;
1374 token->val.arg_no = token->val.node->value.arg_index;
1375 }
1376 else if (CPP_WTRADITIONAL (pfile) && macro->paramc > 0
1377 && (token->type == CPP_STRING || token->type == CPP_CHAR))
1378 check_trad_stringification (pfile, macro, &token->val.str);
1379
1380 return token;
1381 }
ident_hash保证在宏的定义中,每个由cpp_hashnode代表的识别符都是唯一的;而对应参数的识别符,则在_cpp_save_parameter的1255行,设置了标识符NODE_MACRO_ARG。因此对于那些在展开体内找到的同参数名符号,需要标记它为CPP_MACRO_ARG。并看到对于这种符号,它的值在1374行被设置为对应参数的序列号。因而在后面的处理中,能快速找到对应的实体。