注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

如何构造一个有限递归宏  

2017-06-01 21:14:40|  分类: C/C++基础 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、C++对于宏递归展开的规定
16.3.4 Rescanning and further replacement
1 After all parameters in the replacement list have been substituted, the resulting preprocessing token sequence is rescanned with all subsequent preprocessing tokens of the source file for more macro names to replace.
2 If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file’s preprocessing tokens), it is not replaced. Further, if any nested replacements encounter the name of the macro being replaced, it is not replaced. These nonreplaced macro name preprocessing tokens are no longer available for further replacement even if they are later (re)examined in contexts in which that macro name preprocessing token would otherwise have been replaced.
3 The resulting completely macroreplaced preprocessing token sequence is not processed as a preprocessing directive even if it resembles one.
第二点的说明,正在被替换的宏再次被发现不会被展开;并且,任意嵌套替换如果遇到正在被展开的宏都不会被替换。
3、中说明,完整宏替换之后的内容不会再被作为一个预处理指示来处理。
顺便看到关于 pragma的说明也挺有意思,为了兼容,编译器不识别的pragma指示会被忽略而不会报错,这个是为了支持一些编译器定义的扩展并且为了在不同的系统之间兼容。
16.6 Pragma directive
1 A preprocessing directive of the form
# pragma pptokensopt newline
causes the implementation to behave in an implementation defined manner. Any pragma that is not recognized by the implementation is ignored.
二、gcc的实现
其实gcc的实现还是非常直观的,在展开一个宏的时候,在这个宏定义的结构上打上一个标志位,从而下次遇到的时候就不会再展开这个宏了。
gcc-4.1.0\libcpp\macro.c
输入词法流扫描的时候
/* Read each token in, until end of the current file.  Directives are
   transparently processed.  */
void
cpp_scan_nooutput (cpp_reader *pfile)
{
……

  if (CPP_OPTION (pfile, traditional))
    while (_cpp_read_logical_line_trad (pfile))
      ;
  else
    while (cpp_get_token (pfile)->type != CPP_EOF)
      ;
……
}

展开某个宏时设置NODE_DISABLED标志位:
static int
enter_macro_context (cpp_reader *pfile, cpp_hashnode *node)
{
……
      /* Disable the macro within its expansion.  */
      node->flags |= NODE_DISABLED;
……
}      

单个词法分析时,判断如果该宏有这个标志位的时候就不再进行展开
const cpp_token *
cpp_get_token (cpp_reader *pfile)
{
……

      if (!(node->flags & NODE_DISABLED))
{
……
}
      else
{
……
}
……
}
三、是否可以递归展开 
注意下面的注释:"并且,可以轻松的构造出持续递归到任意深度然后终止的例子"。我想了下,构造任意长的递归很容易,但是,如何让这个递归可控可终止呢?就像原子弹的爆炸比起爆炸的可控要简单很多。
gcc-4.1.0\libcpp\traditional.c
/* Returns TRUE if traditional macro recursion is detected.  */
static bool
recursive_macro (cpp_reader *pfile, cpp_hashnode *node)
{
  bool recursing = !!(node->flags & NODE_DISABLED);

  /* Object-like macros that are already expanding are necessarily
     recursive.

     However, it is possible to have traditional function-like macros
     that are not infinitely recursive but recurse to any given depth.
     Further, it is easy to construct examples that get ever longer
     until the point they stop recursing.  So there is no easy way to
     detect true recursion; instead we assume any expansion more than
     20 deep since the first invocation of this macro must be
     recursing.  */
……
}
下面是gcc自带的测试例子gcc-4.1.0\gcc\testsuite\gcc.dg\cpp\trad\recurse-3.c中的内容改造过来的,从这个例子中可以看到,递归的确是可以递归有限次之后结束,虽然暂时看不出这么做有什么意义,至少证明了注释的作者没有乱说(反面的例子就是费马关于费马大定理的注释):

tsecer@harry: cat gcc_trad_recursive_macro.cpp 
#define f(a,b,c,d,e,f,g,h,i) a(b,c,d,e,f,g,h,i,2 3 4 5)
f(f,f,f,f,f,f,f,f,f)

tsecer@harry: /home/tsecer/sda6/Download/gccobj4.1/gcc/cc1 -traditional-cpp gcc_trad_recursive_macro.cpp  -E
# 1 "gcc_trad_recursive_macro.cpp"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "gcc_trad_recursive_macro.cpp"

2 3 4 5(2 3 4 5,2 3 4 5,2 3 4 5,2 3 4 5,2 3 4 5,2 3 4 5,2 3 4 5,2 3 4 5,2 3 4 5)


Execution times (seconds)
 TOTAL                 :   0.00             0.01             0.03                860 kB
tsecer@harry: 
这个功能可能在更高的gcc版本中已经被完全禁用,例如我使用我系统自带的4.8.2版本的gcc已经不能复现这个现象,这个输出是使用我自己编译的4.1版本执行的输出
四、备注
测试的代码的由来
gcc-4.1.0\gcc\testsuite\gcc.dg\cpp\trad\recurse-3.c

/* Tests that macros that look recursive but are not are accepted.  */

/* { dg-do preprocess } */

#define g(x) x
g(g(g(g(g(g(g))))));       /* { dg-bogus "detected recursion" } */

/* This macro gets longer with each loop, to thwart tests for
   recursion based on length.  */
#define f(a,b,c,d,e,f,g,h,i) a(b,c,d,e,f,g,h,i,2 3 4 5)
f(f,f,f,f,f,f,f,f,f)       /* { dg-bogus "detected recursion" } */

/* The above cases should be enough, but this is taken from cccp
   sources so let's try it too.  */
#define foo(x,y) bar (x (y,0), y)
foo (foo, baz);       /* { dg-bogus "detected recursion" } */

#define mac mac/**/ro
mac       /* { dg-bogus "detected recursion" } */

#define mac2 mac2
"mac2"       /* { dg-bogus "detected recursion" } */

#define macro "macro
macro mac2       /* { dg-bogus "detected recursion" } */
  评论这张
 
阅读(53)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017