GCC-3.4.6源代码学习笔记(35)

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_alloccpp_readera_buff 中分配长度为len的非临时的,对齐的存储空间。这个缓存用于保存#define中的宏替换列表,及解析#assert#unassert#if中的答案(连带可能的宏展开)。

cpp_reader中,有base_run——这是用来保存一般的符号(包括宏的名字),a_buff用于保存宏的展开体等,不可等同视之的符号,另u_buff则是保存预处理中遇到的字符串。因此,下面_cpp_create_definition1486行,调用_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,如果我们预读了符号;否则我们需要从pfilebuffer中通过_cpp_lex_direct获取符号(现在PCH文件中宏的定义已经在pfilebuffer中)。

 

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行,stateparsing_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 00d4,这是definition_length,跟着的08 008,它是name_length,而00 000,为 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 *) &macro->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_token1278行,在parse_params中,识别符对应的cpp_hashnode已被创建(如果已存在,则返回这个已存在的节点,注意它可能代表源代码中别的同名变量),并被存入cpp_tokennode域中。

 

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_parameter1255行,设置了标识符NODE_MACRO_ARG。因此对于那些在展开体内找到的同参数名符号,需要标记它为CPP_MACRO_ARG。并看到对于这种符号,它的值在1374行被设置为对应参数的序列号。因而在后面的处理中,能快速找到对应的实体。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值