青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

關于宏的一些技巧,總結得挺好,寫庫的時候比較有用。
轉自http://m.shnenglu.com/kevinlynx/archive/2008/03/19/44828.html
眾多C++書籍都忠告我們C語言宏是萬惡之首,但事情總不如我們想象的那么壞,就如同goto一樣。宏有
一個很大的作用,就是自動為我們產生代碼。如果說模板可以為我們產生各種型別的代碼(型別替換),
那么宏其實可以為我們在符號上產生新的代碼(即符號替換、增加)。
關于宏的一些語法問題,可以在google上找到。相信我,你對于宏的了解絕對沒你想象的那么多。如果你
還不知道#和##,也不知道prescan,那么你肯定對宏的了解不夠。
我稍微講解下宏的一些語法問題(說語法問題似乎不妥,macro只與preprocessor有關,跟語義分析又無關):
1. 宏可以像函數(shù)一樣被定義,例如:
   #define min(x,y) (x<y?x:y) //事實上這個宏存在BUG
   但是在實際使用時,只有當寫上min(),必須加括號,min才會被作為宏展開,否則不做任何處理。
   
2. 如果宏需要參數(shù),你可以不傳,編譯器會給你警告(宏參數(shù)不夠),但是這會導致錯誤。如C++書籍中所描
   述的,編譯器(預處理器)對宏的語法檢查不夠,所以更多的檢查性工作得你自己來做。
3. 很多程序員不知道的#和##
   #符號把一個符號直接轉換為字符串,例如:
   #define STRING(x) #x
   const char *str = STRING( test_string ); str的內容就是"test_string",也就是說#會把其后的符號
   直接加上雙引號。
   ##符號會連接兩個符號,從而產生新的符號(詞法層次),例如:
   #define SIGN( x ) INT_##x
   int SIGN( 1 ); 宏被展開后將成為:int INT_1;

   但有小問題要注意,宏中遇到#或##時就不會再展開宏中嵌套的宏了。什么意思了?比如使用char *pChar = STRING(__FILE__);雖然__FILE__本身也是一個宏,但編譯器不會展開它,所以pChar將指向"__FILE__"而不是你要想的形如"D:\XXX.cpp"的源文件名稱。因此要加一個中間轉換宏,先將__FILE__解析成"D:\XXX.cpp"字符串。
定義如下所示二個宏:
#define _STRING(x) #x
#define STRING(x) _STRING(x)
再調用下面語句將輸出帶""的源文件路徑
       char* pChar = STRING(__FILE__);
       printf("%s %s\n", pChar, __FILE__);
可以比較下STRING(__FILE__)與__FILE__的不同,前將帶雙引號,后一個沒有雙引號。

4. 變參宏,這個比較酷,它使得你可以定義類似的宏:
   #define LOG( format, ... ) printf( format, __VA_ARGS__ )
   LOG( "%s %d", str, count );
   __VA_ARGS__是系統(tǒng)預定義宏,被自動替換為參數(shù)列表。
5. 當一個宏自己調用自己時,會發(fā)生什么?例如:
   #define TEST( x ) ( x + TEST( x ) )
   TEST( 1 ); 會發(fā)生什么?為了防止無限制遞歸展開,語法規(guī)定,當一個宏遇到自己時,就停止展開,也就是
   說,當對TEST( 1 )進行展開時,展開過程中又發(fā)現(xiàn)了一個TEST,那么就將這個TEST當作一般的符號。TEST(1)
   最終被展開為:1 + TEST( 1) 。
6. 宏參數(shù)的prescan,
   當一個宏參數(shù)被放進宏體時,這個宏參數(shù)會首先被全部展開(有例外,見下文)。當展開后的宏參數(shù)被放進宏體時,
   預處理器對新展開的宏體進行第二次掃描,并繼續(xù)展開。例如:
   #define PARAM( x ) x
   #define ADDPARAM( x ) INT_##x
   PARAM( ADDPARAM( 1 ) ); 
   因為ADDPARAM( 1 ) 是作為PARAM的宏參數(shù),所以先將ADDPARAM( 1 )展開為INT_1,然后再將INT_1放進PARAM。
   
   例外情況是,如果PARAM宏里對宏參數(shù)使用了#或##,那么宏參數(shù)不會被展開:
   #define PARAM( x ) #x
   #define ADDPARAM( x ) INT_##x
   PARAM( ADDPARAM( 1 ) ); 將被展開為"ADDPARAM( 1 )"。
   使用這么一個規(guī)則,可以創(chuàng)建一個很有趣的技術:打印出一個宏被展開后的樣子,這樣可以方便你分析代碼:
   #define TO_STRING( x ) TO_STRING1( x )
   #define TO_STRING1( x ) #x
   TO_STRING首先會將x全部展開(如果x也是一個宏的話),然后再傳給TO_STRING1轉換為字符串,現(xiàn)在你可以這樣:
   const char *str = TO_STRING( PARAM( ADDPARAM( 1 ) ) );去一探PARAM展開后的樣子。
7. 一個很重要的補充:就像我在第一點說的那樣,如果一個像函數(shù)的宏在使用時沒有出現(xiàn)括號,那么預處理器只是
   將這個宏作為一般的符號處理(那就是不處理)。
我們來見識一下宏是如何幫助我們自動產生代碼的。如我所說,宏是在符號層次產生代碼。我在分析Boost.Function
模塊時,因為它使用了大量的宏(宏嵌套,再嵌套),導致我壓根沒看明白代碼。后來發(fā)現(xiàn)了一個小型的模板庫ttl,說的
是開發(fā)一些小型組件去取代部分Boost(這是一個好理由,因為Boost確實太大)。同樣,這個庫也包含了一個function庫。
這里的function也就是我之前提到的functor。ttl.function庫里為了自動產生很多類似的代碼,使用了一個宏:
#define TTL_FUNC_BUILD_FUNCTOR_CALLER(n)  \
 template< typename R, TTL_TPARAMS(n) > \
 struct functor_caller_base##n \
        ///...
該宏的最終目的是:通過類似于TTL_FUNC_BUILD_FUNCTOR_CALLER(1)的調用方式,自動產生很多functor_caller_base模板:
template <typename R, typename T1> struct functor_caller_base1
template <typename R, typename T1, typename T2> struct functor_caller_base2
template <typename R, typename T1, typename T2, typename T3> struct functor_caller_base3
///... 
那么,核心部分在于TTL_TPARAMS(n)這個宏,可以看出這個宏最終產生的是:
typename T1
typename T1, typename T2
typename T1, typename T2, typename T3
///...
我們不妨分析TTL_TPARAMS(n)的整個過程。分析宏主要把握我以上提到的一些要點即可。以下過程我建議你翻著ttl的代碼,
相關代碼文件:function.hpp, macro_params.hpp, macro_repeat.hpp, macro_misc.hpp, macro_counter.hpp。
so, here we go
分析過程,逐層分析,逐層展開,例如TTL_TPARAMS(1):
#define TTL_TPARAMS(n) TTL_TPARAMSX(n,T)  
=> TTL_TPARAMSX( 1, T )
#define TTL_TPARAMSX(n,t) TTL_REPEAT(n, TTL_TPARAM, TTL_TPARAM_END, t)
=> TTL_REPEAT( 1, TTL_TPARAM, TTL_TPARAM_END, T )
#define TTL_TPARAM(n,t) typename t##n, 
#define TTL_TPARAM_END(n,t) typename t##n
#define TTL_REPEAT(n, m, l, p) TTL_APPEND(TTL_REPEAT_, TTL_DEC(n))(m,l,p) TTL_APPEND(TTL_LAST_REPEAT_,n)(l,p)
注意,TTL_TPARAM, TTL_TPARAM_END雖然也是兩個宏,他們被作為TTL_REPEAT宏的參數(shù),按照prescan規(guī)則,似乎應該先將
這兩個宏展開再傳給TTL_REPEAT。但是,如同我在前面重點提到的,這兩個宏是function-like macro,使用時需要加括號,
如果沒加括號,則不當作宏處理。因此,展開TTL_REPEAT時,應該為:
=> TTL_APPEND( TTL_REPEAT_, TTL_DEC(1))(TTL_TPARAM,TTL_TPARAM_END,T) TTL_APPEND( TTL_LAST_REPEAT_,1)(
TTL_TPARAM_END,T)
這個宏體看起來很復雜,仔細分析下,可以分為兩部分:
TTL_APPEND( TTL_REPEAT_, TTL_DEC(1))(TTL_TPARAM,TTL_TPARAM_END,T)以及
TTL_APPEND( TTL_LAST_REPEAT_,1)(TTL_TPARAM_END,T)
先分析第一部分:
#define TTL_APPEND( x, y ) TTL_APPEND1(x,y) //先展開x,y再將x,y連接起來
#define TTL_APPEND1( x, y ) x ## y
#define TTL_DEC(n) TTL_APPEND(TTL_CNTDEC_, n)
根據(jù)先展開參數(shù)的原則,會先展開TTL_DEC(1)
=> TTL_APPEND(TTL_CNTDEC_,1) => TTL_CNTDEC_1 
#define TTL_CNTDEC_1 0  注意,TTL_CNTDEC_不是宏,TTL_CNTDEC_1是一個宏。
=> 0 , 也就是說,TTL_DEC(1)最終被展開為0。回到TTL_APPEND部分:
=> TTL_REPEAT_0 (TTL_TPARAM,TTL_TPARAM_END,T) 
#define TTL_REPEAT_0(m,l,p)
TTL_REPEAT_0這個宏為空,那么,上面說的第一部分被忽略,現(xiàn)在只剩下第二部分:
TTL_APPEND( TTL_LAST_REPEAT_,1)(TTL_TPARAM_END,T)
=> TTL_LAST_REPEAT_1 (TTL_TPARAM_END,T) // TTL_APPEND將TTL_LAST_REPEAT_和1合并起來
#define TTL_LAST_REPEAT_1(m,p) m(1,p)
=> TTL_TPARAM_END( 1, T )
#define TTL_TPARAM_END(n,t) typename t##n
=> typename T1  展開完畢。
雖然我們分析出來了,但是這其實并不是我們想要的。我們應該從那些宏里去獲取作者關于宏的編程思想。很好地使用宏
看上去似乎是一些偏門的奇技淫巧,但是他確實可以讓我們編碼更自動化。
參考資料:
macro語法: http://developer.apple.com/documentation/DeveloperTools/gcc-4.0.1/cpp/Macros.html
ttl(tiny template library) : http://tinytl.sourceforge.net/
posted on 2013-03-25 17:31 Richard Wei 閱讀(1867) 評論(0)  編輯 收藏 引用 所屬分類: C++
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            性刺激综合网| 国产精品一区2区| 亚洲精品免费网站| 欧美一区国产二区| 香蕉av777xxx色综合一区| 99精品国产在热久久| 日韩视频免费观看| 亚洲先锋成人| 亚洲综合99| 久久国产精彩视频| 久久人体大胆视频| 欧美a级在线| 最新日韩中文字幕| 一本一本a久久| 午夜国产精品视频| 欧美一区国产一区| 久久久青草青青国产亚洲免观| 久久青草福利网站| 欧美日韩成人激情| 国产精品一区二区你懂的| 国内久久婷婷综合| 亚洲国产综合在线看不卡| 亚洲天堂av电影| 久久免费99精品久久久久久| 欧美韩国一区| 亚洲午夜免费视频| 老鸭窝毛片一区二区三区| 欧美国产91| 国产日本欧洲亚洲| 亚洲免费观看高清在线观看| 欧美诱惑福利视频| 亚洲第一区在线观看| 99国产精品久久久| 久久xxxx精品视频| 欧美区国产区| 狠狠色综合播放一区二区| 亚洲视频播放| 欧美激情在线有限公司| 中文一区字幕| 欧美成人久久| 狠狠色噜噜狠狠色综合久| 亚洲欧美日韩国产一区二区| 欧美电影免费观看高清| 亚洲在线视频观看| 欧美精品日韩一区| 在线成人黄色| 久久精品99国产精品日本| 99精品欧美一区二区三区| 美玉足脚交一区二区三区图片| 国产精品一区二区欧美| 亚洲一区二区三区高清| 亚洲人成亚洲人成在线观看图片 | 欧美韩日亚洲| 亚洲国产精品久久久久秋霞不卡| 亚洲综合第一| 欧美视频福利| 一本色道久久综合亚洲精品小说 | 亚洲精品免费一区二区三区| 午夜在线视频观看日韩17c| 欧美日韩免费一区二区三区| 亚洲精品欧美一区二区三区| 麻豆91精品| 久久精品欧洲| 合欧美一区二区三区| 久久久久久免费| 翔田千里一区二区| 国产亚洲va综合人人澡精品| 性色av一区二区三区在线观看| 一区二区三区四区五区精品视频 | 好吊色欧美一区二区三区视频| 久久精品盗摄| 亚洲伊人网站| 国产亚洲一级| 猛男gaygay欧美视频| 黄色影院成人| 欧美专区第一页| 久久大香伊蕉在人线观看热2| 国产九九精品视频| 欧美一区国产在线| 久久精品夜色噜噜亚洲a∨| 激情婷婷亚洲| 亚洲高清在线| 欧美性理论片在线观看片免费| 亚洲欧美日韩另类精品一区二区三区| 99在线精品视频在线观看| 国产精品呻吟| 榴莲视频成人在线观看| 欧美成人自拍视频| 亚洲一区二区三区中文字幕| 亚洲主播在线观看| 国内一区二区三区| 亚洲黄一区二区三区| 欧美午夜片在线观看| 欧美在线观看视频在线| 久久天天综合| 亚洲一区二区毛片| 久久久国产精品一区| 99精品久久久| 欧美一二区视频| 日韩一级精品视频在线观看| 亚洲视频第一页| 亚洲大片在线观看| av不卡在线观看| 伊人一区二区三区久久精品| 亚洲精品系列| 永久555www成人免费| 一本一本久久a久久精品牛牛影视| 亚洲久久在线| 亚洲综合国产激情另类一区| 国产精品成人免费视频| 久久久91精品国产一区二区精品| 久久久噜噜噜久噜久久| 亚洲欧美日韩中文播放| 蜜桃视频一区| 久久精精品视频| 欧美激情国产日韩| 久久亚洲影院| 国产精品一级久久久| 日韩天堂av| 亚洲福利视频一区二区| 亚洲在线播放| 中文国产成人精品久久一| 久久综合色88| 久久高清一区| 国产精品亚洲综合一区在线观看| 亚洲三级网站| 亚洲激情电影中文字幕| 欧美伊人影院| 欧美一区二区三区四区高清| 欧美日韩国产成人| 亚洲黄色大片| 亚洲欧洲在线一区| 久久综合999| 噜噜噜久久亚洲精品国产品小说| 国产精品资源| 亚洲欧美综合v| 久久国产主播精品| 国产视频不卡| 亚洲欧美日韩系列| 欧美亚洲网站| 国产女主播在线一区二区| 亚洲视频在线免费观看| 亚洲中无吗在线| 国产精品久久婷婷六月丁香| 亚洲精品国产欧美| 99re6热只有精品免费观看| 欧美高清在线| 亚洲美女av网站| 亚洲视频一区二区在线观看 | 欧美激情亚洲综合一区| 一区免费观看视频| 久久久免费精品视频| 欧美国产日本高清在线| 亚洲精品一级| 欧美日韩视频专区在线播放| 一区二区三区久久久| 欧美在线观看一区| 国产一区二区成人| 久久视频在线看| 亚洲黄色av一区| 亚洲天堂网在线观看| 国产精品国产三级国产aⅴ浪潮 | 免费一级欧美片在线播放| 亚洲国产精品久久久久婷婷884| 亚洲精选在线| 国产精品乱子乱xxxx| 欧美一区二区视频97| 欧美成在线观看| 亚洲视频在线观看免费| 国产免费成人av| 裸体一区二区| 一个人看的www久久| 亚洲一级黄色| 亚洲欧洲日本mm| 欧美日韩免费一区二区三区| 亚洲嫩草精品久久| 久热精品视频在线观看一区| 亚洲精品四区| 国产欧美 在线欧美| 欧美成黄导航| 性亚洲最疯狂xxxx高清| 亚洲国产天堂久久综合网| 香蕉成人伊视频在线观看 | 欧美va天堂va视频va在线| 亚洲美女在线视频| 久久夜色精品国产亚洲aⅴ| 一本一本大道香蕉久在线精品| 国产精品免费一区二区三区在线观看 | 欧美国产日韩一区| 亚洲欧美色婷婷| 亚洲国产毛片完整版| 欧美中在线观看| 一区二区三区国产在线| 亚洲观看高清完整版在线观看| 国产精品毛片a∨一区二区三区| 蜜桃av久久久亚洲精品| 欧美一区二区三区久久精品| 9久re热视频在线精品| 欧美激情精品久久久久久大尺度 | 国产精品一区二区久久久久|