posts - 16,  comments - 34,  trackbacks - 0

题目有点绕口……

问题见: http://bbs2.chinaunix.net/viewthread.php?tid=1373280

 

问题描述

 

问题解答

 


 

问题分析

需要注意:
I. 宏是作文本替换
II. 替换的终止条件是:文件中不再含有宏

对第9行的:SET_NAME(1212)
1. 首先根据I和第5行,SET_NAME(1212) 会被替换成:CONNECTION(test,1212)
2. CONNECTION依然是一个宏,根据II,继续替换
3. 根据I和第4行,CONNECTION(test,1212),被替换为 test1212
4. 所以第10行最终会被CPP替换成 "int test1212 = 1212;"

对第12行的:SET_NAME(VAR)
1. 首先根据I和第5行,SET_NAME(VAR)会被替换成:CONNECTION(test,VAR)
2. CONNECTIONVAR依然是一个宏,根据II,继续替换
3. 根据I和第11行,CONNECTION(test,VAR)被替换为CONNECTION(test,326)
4. 再根据I和第4行,CONNECTION(test,326)被替换为test326
5. 所以第12行最终会被CPP替换成 "int test326 = 326;"

对第16行的:SET_NAME(VAR),同第12行,最终会被替换成 test86



为什么setname不行?
setname(var) 会被替换成 testvar,而后者不再含有宏,替换终止


 

常见应用

根据行号命名——为了取一些相互不冲突的名字,使用行号作为后缀
因为__LINE__也是一个,所以需要这种方法。

例1,Loki::ScopeGuard

Loki::ScopeGuard

 

Loki::ScopeGuard MACRO 示例


 1// Loki::ScopeGuard macro sample
 2// Loki::ScopeGuard : 范型、轻量的RAII技术 ,对资源管理与异常安全提供非常强大的支持
 3// 该处仅演示使用__LINE__作变量后缀名的方法, 暂不讨论Loki::ScopeGuard
 4
 5#include <cassert>
 6#include <cstdio>
 7#include <stdexcept>
 8#include <string>
 9#include <loki::ScopeGuard>
10
11void CopyFile(const char* input_file,const char* output_file) /* throw(std::exception) */ {
12    using namescape std;
13
14    FILE* input = fopen(input_file,"r");
15    if (!input) throw runtime_error( string("can't open input file :"+ input_file);
16    LOKI_ON_BLOCK_EXIT(fclose,input);
17
18    FILE* output = fopen(output_file,"wb");
19    if (!output) throw runtime_error( string("can't open output file :"+ output);
20    LOKI_ON_BLOCK_EXIT(fclose,output);
21
22    enum { buf_size = 1212 };
23    char buf[buf_size];
24    size_t r = buf_size;
25
26    do {
27        r = fread(buf,1,buf_size,input);
28        if ( buf_size != fwrite(buf,1,buf_size,output)
29            throw runtime_error( string("write output file : "+ output + " occurs an error" );
30    }

31    while ( r == buf_size );
32
33    if ( !feof(input) {
34        assert( ferror(input) );
35        throw runtime_error( string("read input file : "+ input + " occurs an error");
36    }

37}

38
39
40int main() {
41    try 
44    catch (std::exception& e) 
47}

48


 代码中16和20行,根据loki/ScopeGuard.h (658)中的定义,将被分别替换成:

::Loki::ScopeGuard scopeGuard16 = ::Loki::MakeGuard(fclose,input);
::Loki::ScopeGuard scopeGuard20 
= ::Loki::MakeGuard(fclose,output);

也就是定义2个名字以scopeGuard为前缀文件行号为后缀的“变量”(名字就不会重复)。
它们在退出作用域的时候会分别调用:fclose(input); fclose(output);
PS:ScopeGuard的强大还不仅仅体现在这里,以后会专门介绍。


例2.1,内嵌汇编或者使用goto时,需要一个不重复的跳转标号。

make label



例2.2,做键盘模拟的时候,按照i8042的规则,每次写入端口时,需要等待输入缓冲为空。
所以需要实现一个 KBC_Wait4IBE (key board controller wait for input buffer empty)



但是又不想有函数调用消耗,所以打算用宏实现。
实验1: 失败的例子


#define KBC_WAIT4IBE()  \
KBC_WAIT4IBE_label:     \
_asm in AL,64h      \
_asm    TEST AL,10B \
_asm    JNZ KBC_WAIT4IBE_label


void KBC_KeyDown(byte scan) {
    KBC_WAIT4IBE(); // 等待输入缓冲为空
    _outp(CMD_PORT,CMD_WRITE_OUTPUT_REG); // 准备写入数据
    KBC_Wait4IBE(); // error C2045: 'KBC_WAIT4IBE_label' : label redefined
    // 标号重复
}


有一个办法就是给标号加上行号作为后缀,那么在一个文件中也不会重复(使用 #line 除外……)。


btw:上面那个函数实现 KBC_Wait4IBE ,在VC8 release编译下,会直接被inline,并且生成的代码和KBC_WAIT4IBE完全相同……
所以,要信任编译器的优化,不要无谓的牺牲可读性~


重要补充! 上述解释并不准确!!!
setname(var) 中的var同样是一个宏,为什么不被替换?
SET_NAME(VAR)的第1次替换时,同样VAR没有被替换,为什么第2次替换就会被替换?

根据《代码自动生成-宏带来的奇技淫巧》:http://www.cppblog.com/kevinlynx/archive/2008/03/19/44828.html
的说法,第2次替换时,涉及一个叫prescan的机制。
我平时对CPP研究不多,所以也没弄明白这个机制。硬盘里专门讲C的书也不多,我翻翻看有没有详细介绍的……

感兴趣的读者还可以参考: http://developer.apple.com/documentation/DeveloperTools/gcc-4.0.1/cpp/Macros.html



 

再补充一点: 关于于宏的调试。
在MSVC下,可以给某个编译单元xxx.c(cpp,cxx)加入"/P"(不含引号,P一定大写)命令。
编译该单元后,会在xxx.c的同目录下生成xxx.i,即预处理的结果。
在GCC下,可以使用 gcc(g++) -E xxx.c(cpp,cxx) (必要时还需要 -i ),查看预处理结果。



再次补充

在《The C Programming Language》 2nd Edition中找到了解释
附录A.12.3 Macro Definition and Expansion p207。
以下只摘录重点部分:
During collection(指第1次), arguments are not macro-expanded.
In both (指带参数或者不带参数)kinds of macro, the replacement token sequence is repeatedly rescanned for more defined identifiers.


没能搜到ANSI C标准的文档……

posted on 2009-02-18 23:59 OwnWaterloo 阅读(3097) 评论(4)  编辑 收藏 引用

FeedBack:
# re: 使用宏作宏参数
2009-02-19 09:25 | 飘雪
好文,我原来对这个问题也想了很久  回复  更多评论
  
# re: 使用宏作宏参数
2009-02-19 09:52 | 路青飞
我可不可理解为,宏中宏!
哈哈!好文!赞一个!  回复  更多评论
  
# re: 使用宏作宏参数
2009-02-19 11:50 |
不错,终于明白这个问题了!当初困扰我很久,还是没有解决的问题!哈哈  回复  更多评论
  
# re: 使用宏作宏参数
2010-05-10 17:05 | PattersonGay
That is good that we are able to receive the <a href="http://lowest-rate-loans.com/topics/home-loans">home loans</a> and that opens new opportunities.   回复  更多评论
  
# re: 使用宏作宏参数
2010-08-08 14:31 | ringtone
Any human in the our world wants to stay original, but does not know the correct way to do it. But thousands of different people look for the ringtones download or just composer ringtones to be unique.   回复  更多评论
  
# re: 使用宏作宏参数
2012-07-02 09:25 | on line essays
To my mind, here only we receive the groundbreakingnewfangled brilliant data just about this good topic and that can be easygoing for men to buy term papers or buy an essay bestwritingservice.com from the professional custom essays writing firm.   回复  更多评论
  

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2020年7月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

常用链接

留言簿(8)

随笔档案(16)

链接

搜索

  •  

积分与排名

  • 积分 - 181795
  • 排名 - 123

最新随笔

最新评论

阅读排行榜

评论排行榜