• <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>

            C/C++圖形圖像的世界

            圖形與游戲編程

            常用鏈接

            統(tǒng)計

            積分與排名

            Blog

            最新評論

            C/C++宏的奇技淫巧

            來源:http://blog.misakamm.org/p/209
            宏的主要作用就是簡化代碼編寫,簡化一些需要重復編碼的地方,以得到看起來更優(yōu)雅的代碼。但宏要用得好并不容易,用的不好很容易引發(fā)災難性的后果。本文會介紹宏比較偏門但又很實用的技巧。
            首先就是最常用的技巧(http://blog.misakamm.org/p/209):
            #define MACROCAT( x, y ) MACROCAT1 ( x, y )
            #define MACROCAT1( x, y ) x##y
            #define TOSTRING( s ) #s
            MACROCAT把x和y展開后連結(jié),而TOSTRING把s轉(zhuǎn)化為字符串,比如可以printf(TOSTRING(%s), TOSTRING(abcdefg));
            然后,因為宏不能遞歸,但可以做遞歸模擬,我們可以這樣玩。比如要生成n位的二進制數(shù)并且從小到大構(gòu)成的字符串(用到前面的宏):
            #define BIN_0(arg) TOSTRING ( arg )
            #define BIN_1(arg) BIN_0(MACROCAT(arg, 0)) "," BIN_0(MACROCAT(arg, 1))
            #define BIN_2(arg) BIN_1(MACROCAT(arg, 0)) "," BIN_1(MACROCAT(arg, 1))
            #define BIN_3(arg) BIN_2(MACROCAT(arg, 0)) "," BIN_2(MACROCAT(arg, 1))
            #define BIN_4(arg) BIN_3(MACROCAT(arg, 0)) "," BIN_3(MACROCAT(arg, 1))
            int main()
            {
            puts(BIN_4());
            return 0;
            }

            這里要注意的是,比如BIN_2(),實際上展開的結(jié)果是
            "0" "0" "," "0" "1" "," "1" "0" "," "1" "1"
            不過c/c++規(guī)定這樣連寫的字符串,編譯時就會合并成一個,于是就能用puts直接完整輸出結(jié)果了
            如果你想得到更多的位,很簡單,只要你不介意,上面的宏復制并改改數(shù)字就可以了
            不過,這樣一改要改若干個數(shù)字,比較麻煩,能不能讓它工作得更好?比如只要改宏名?
            這個時候,就要用更富有技巧性的一招了:讓每個宏多一個參數(shù)n,然后前面的BIN_x使用MACROCAT把它與數(shù)字連結(jié)起來,不就可以了么?
            想法不錯,不過問題是宏本身沒有做減法的能力,能做的僅僅是替換。減1應該怎么實現(xiàn)呢?
            其實不難,見以下定義:
            #define DECVAL_1 0
            #define DECVAL_2 1
            #define DECVAL_3 2
            #define DECVAL_4 3
            #define DECVAL_5 4
            #define DECVAL_6 5
            #define DECVAL_7 6
            #define DECVAL_8 7
            #define DECVAL_9 8
            #define DECVAL( n ) DECVAL_##n
            好了,有了這個利器,我們就可以對原宏改造了,先拿0號和1號宏開刀:
            #define BIN_0(n, arg) TOSTRING ( arg )
            #define BIN_1(n, arg) MACROCAT(BIN_, DECVAL(n)) (DECVAL(n), MACROCAT(arg, 0)) \
            "," MACROCAT(BIN_, DECVAL(n)) (DECVAL(n), MACROCAT(arg, 1))
            看得懂替換了一些什么嗎?這樣,后面的2,3,4,5號,只要復制一下1號的定義,改一改宏名就解決問題了
            思考題:
            這里生成的二進制結(jié)果是帶前導0的,如何改寫能使生成的結(jié)果不帶前導0?
            source: http://blog.misakamm.org/p/209

            使用此法可以“遞歸”式生成很多類似代碼,同時這個技巧也非常的實用,但遞歸構(gòu)造并不容易,需要編寫的人仔細想清楚,否則很容易出錯,特別要注意宏展開的時機,一般不直接使用MACROCAT1宏,因為那個很可能不是你想要的結(jié)果
            之后,到C99標準出臺后(也就是說,下文內(nèi)容與bc3/tc/vc6不兼容),宏里面多了一個狠角色:可變參數(shù)個數(shù)宏
            比如可以 #define PRINTF(...) fprintf(stdout, __VA_ARGS__)
            其中__VA_ARGS__代表了‘...’部分的全部參數(shù),這樣可以輕松的重定義庫函數(shù)里不定參數(shù)的函數(shù)的輸出行為,比如printf重定向到文件(雖然也可以用freopen實現(xiàn),但只想說明宏也可以這樣搞)
            好了,下文將區(qū)分編譯器來介紹,一共分為兩派,vc派和gcc派(包括clang/objc),因為兩者對以下代碼的處理并不一致,需要使用略為不同的宏來實現(xiàn),目前我也只遇到這兩派。
            現(xiàn)在的目的是這樣,因為__VA_ARGS__包含了若干參數(shù),我怎么才能知道里面參數(shù)有多少個呢?
            比如寫一個宏NUM_PARAMS(),里面寫NUM_PARAMS(abc,a,d,e)的話,替換后得到的結(jié)果要是4,能辦到嗎?
             
             
             
             
             
             
             
             
             
             
            廣告時間:
            http://blog.misakamm.org/p/209
            廣告過后,回來精彩的節(jié)目
             
             
             
             
             
             
             
             
             
             
            首先先介紹gcc派的解決方案:
            #define PP_NARG(...) PP_NARG_(__VA_ARGS__, PP_RSEQ_N())
            #define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
            #define PP_ARG_N( \
            _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
            _11,_12,_13,_14,_15,_16, N, ...) N
            #define PP_RSEQ_N() \
            16,15,14,13,12,11,10, \
            9,8,7,6,5,4,3,2,1,0
            非常漂亮巧妙又簡潔的方案,我想不用我多解釋了吧?
            不過,請注意,這是gcc的方案,以上代碼放在vc8/vc9/vc2010等都會得不到正確的結(jié)果的,這個和vc的宏處理方式有關
            接下來就是給出vc的解決方案(以下均以vc2008和vc2010為準)
            #define BRACKET_L() (
            #define BRACKET_R() )
            #define PP_NARG(...) \
            PP_NARG_ ( __VA_ARGS__, PP_RSEQ_N() )
            #define PP_NARG_(...) \
            PP_ARG_N BRACKET_L() __VA_ARGS__ BRACKET_R()
            #define PP_ARG_N( \
            _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
            _11,_12,_13,_14,_15,_16, N,...) N
            #define PP_RSEQ_N() \
            16,15,14,13,12,11,10, \
            9,8,7,6,5,4,3,2,1,0
            
            這里很特別的一點是對部分小括號做了替換。
            問題在于PP_NARG_到PP_ARG_N做參數(shù)傳遞的時候,如果已有顯式的括號,那么不對里面的宏做展開計算參數(shù)個數(shù),僅直接按顯式的逗號個數(shù)判斷出參數(shù)個數(shù),從而導致__VA_ARGS__被當成一個參數(shù)傳入。而把括號用宏替換掉后,則不出現(xiàn)直接的括號,就先對宏做展開,而展開后,再展開新構(gòu)造出來的宏,這樣才能讓參數(shù)匹配上。
            不過gcc里面不能這么干,gcc會把宏名展開出來后,如果發(fā)現(xiàn)后面的符號并不是顯式的括號,則把前面的宏符號化,不再展開。這兩種不同的特性讓我現(xiàn)在還不知道怎么編寫宏能讓兩派都能兼容,正確展開出我想要的東西。
            解釋了兩個編譯器的不同點以后,后面不再解釋相同的問題,而會同時給出兩份代碼。
            另一個類似的問題,就是既然有不定個數(shù)的參數(shù),如果我希望對每個參數(shù)都做一些處理,那如何做呢?
            舉例,實現(xiàn)一個宏#define SPREAD(...),要把參數(shù)里的東西連結(jié)成一個字符串
            之前的例子里,已經(jīng)實現(xiàn)了把不定參數(shù)展開的手段,現(xiàn)在我們來嘗試遞歸下降式展開(gcc版本):
            #define SPREAD0( arg ) #arg
            #define SPREAD1(arg, ...) SPREAD0(arg)
            #define SPREAD2(arg, ...) SPREAD0(arg) SPREAD1(__VA_ARGS__,)
            #define SPREAD3(arg, ...) SPREAD0(arg) SPREAD2(__VA_ARGS__,)
            #define SPREAD4(arg, ...) SPREAD0(arg) SPREAD3(__VA_ARGS__,)
            #define SPREAD5(arg, ...) SPREAD0(arg) SPREAD4(__VA_ARGS__,)
            #define SPREAD6(arg, ...) SPREAD0(arg) SPREAD5(__VA_ARGS__,)
            #define SPREAD7(arg, ...) SPREAD0(arg) SPREAD6(__VA_ARGS__,)
            #define SPREAD8(arg, ...) SPREAD0(arg) SPREAD7(__VA_ARGS__,)
            #define SPREAD9(arg, ...) SPREAD0(arg) SPREAD8(__VA_ARGS__,)
            #define SPREAD(...) SPREAD9(__VA_ARGS__)
            在這里,每進入一層,就從__VA_ARGS__拆解一個最前面的參數(shù)出來,把剩下的參數(shù)給下一層
            這里有一個細節(jié)是__VA_ARGS__后面有一個逗號,意思就是補一個空參數(shù),避免后面參數(shù)不足
            然后就可以用puts(SPREAD(1, 2, 3, 4));來測試了
            當然,還要使用前文的方式處理一下(gcc版):
            #define SPREAD0( arg ) #arg
            #define SPREAD1(n, arg, ...) SPREAD0(arg)
            #define SPREAD2(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )
            #define SPREAD3(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )
            #define SPREAD4(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )
            #define SPREAD5(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )
            #define SPREAD6(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )
            #define SPREAD7(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )
            #define SPREAD8(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )
            #define SPREAD9(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) ( DECVAL(n), __VA_ARGS__, )
            #define SPREAD(...) SPREAD9 ( 9, __VA_ARGS__ )
            vc版:
            #pragma warning(disable:4003) // 去除警告
            #define SPREAD0( arg ) #arg
            #define SPREAD1(n, arg, ...) SPREAD0(arg)
            #define SPREAD2(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()
            #define SPREAD3(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()
            #define SPREAD4(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()
            #define SPREAD5(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()
            #define SPREAD6(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()
            #define SPREAD7(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()
            #define SPREAD8(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()
            #define SPREAD9(n, arg, ...) SPREAD0(arg) MACROCAT(SPREAD, DECVAL(n)) BRACKET_L() DECVAL(n), __VA_ARGS__, BRACKET_R()
            #define SPREAD(...) SPREAD9 BRACKET_L() 9, __VA_ARGS__, BRACKET_R()
            以上只是模糊方式展開,因為參數(shù)個數(shù)不知道,后面會遇到宏參數(shù)為空的情況,于是vc編譯器給出了警告
            如果把之前說的過技巧,就是分析出不定參數(shù)個數(shù)的宏,與這個結(jié)合,將產(chǎn)生更大的威力,我們可以實現(xiàn)精確展開,就是在SPREAD宏的定義里,有9的地方使用宏PP_NARG(__VA_ARGS__)替換一下,于是__VA_ARGS__后面的逗號可以去掉,也可以簡化一些代碼了,也能避免展開后有你所不希望的多余字符出現(xiàn)。
            測試考題1:
            定義一宏#define printf,讓它能把printf(str, a, b, c);替換成std::cout<<a<<b<<c<<std::endl;
            參數(shù)個數(shù)不確定,不用考慮str的內(nèi)容,但假設不多于10個參數(shù)

            http://blog.misakamm.org/p/209
            宏的威力還不止至此,當宏與C++模板編程結(jié)合的時候,真正的可怕就來臨了。。。
            測試考題2:
            在C++0x之前,模板還沒有不定參數(shù),于是需要多個參數(shù)的時候,不得不手工解決,或者聰明的人,使用模板來生成多參模板代碼。嘗試一下這么做,看看和之前的問題難度加大在哪里。比如生成一個名為sum的模板函數(shù),能接受1 - 10個參數(shù),返回這些參數(shù)的相加的結(jié)果

             
             
             
             
             
             
             
             
             
             
             
            文章附帶:
            第一考題參考答案:
            #define BINARY_E0(n, arg) TOSTRING ( arg )
            #define BINARY_E1(n, arg) MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 0) )\
            "," MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 1) )
            #define BINARY_E2(n, arg) MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 0) )\
            "," MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 1) )
            #define BINARY_E3(n, arg) MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 0) )\
            "," MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 1) )
            #define BINARY_E4(n, arg) MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 0) )\
            "," MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 1) )
            #define BINARY_E5(n, arg) MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 0) )\
            "," MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 1) )
            #define BINARY_E6(n, arg) MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 0) )\
            "," MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 1) )
            #define BINARY_E7(n, arg) MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 0) )\
            "," MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 1) )
            #define BINARY_E8(n, arg) MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 0) )\
            "," MACROCAT(BINARY_E, DECVAL(n)) ( DECVAL(n), MACROCAT(arg, 1) )
            #define BINARY_ENUM(n) MACROCAT(BINARY_E, n) ( n, )
            #define BIN_0(n, arg) TOSTRING ( arg )
            #define BIN_1(n, arg) MACROCAT(BIN_, DECVAL(n)) (DECVAL(n), arg) \
            "," MACROCAT(BINARY_E, DECVAL(n)) (DECVAL(n), MACROCAT(arg, 1))
            #define BIN_2(n, arg) MACROCAT(BIN_, DECVAL(n)) (DECVAL(n), arg) \
            "," MACROCAT(BINARY_E, DECVAL(n)) (DECVAL(n), MACROCAT(arg, 1))
            #define BIN_3(n, arg) MACROCAT(BIN_, DECVAL(n)) (DECVAL(n), arg) \
            "," MACROCAT(BINARY_E, DECVAL(n)) (DECVAL(n), MACROCAT(arg, 1))
            #define BIN_4(n, arg) MACROCAT(BIN_, DECVAL(n)) (DECVAL(n), arg) \
            "," MACROCAT(BINARY_E, DECVAL(n)) (DECVAL(n), MACROCAT(arg, 1))
            #define BIN_5(n, arg) MACROCAT(BIN_, DECVAL(n)) (DECVAL(n), arg) \
            "," MACROCAT(BINARY_E, DECVAL(n)) (DECVAL(n), MACROCAT(arg, 1))
            #define BIN_6(n, arg) MACROCAT(BIN_, DECVAL(n)) (DECVAL(n), arg) \
            "," MACROCAT(BINARY_E, DECVAL(n)) (DECVAL(n), MACROCAT(arg, 1))
            #define BIN_7(n, arg) MACROCAT(BIN_, DECVAL(n)) (DECVAL(n), arg) \
            "," MACROCAT(BINARY_E, DECVAL(n)) (DECVAL(n), MACROCAT(arg, 1))
            #define BIN_8(n, arg) MACROCAT(BIN_, DECVAL(n)) (DECVAL(n), arg) \
            "," MACROCAT(BINARY_E, DECVAL(n)) (DECVAL(n), MACROCAT(arg, 1))
            #define BIN_ENUM(n) "0" MACROCAT(BIN_, n) ( n, )
            測試代碼:puts(BIN_ENUM(8));
            測試考題不提供答案。
            

            posted on 2012-01-16 16:22 御坂美琴 閱讀(7207) 評論(0)  編輯 收藏 引用

            国产99久久久久久免费看| 少妇久久久久久被弄到高潮| 色婷婷综合久久久中文字幕| 精品久久久久中文字幕日本| 精品国产乱码久久久久久1区2区| 品成人欧美大片久久国产欧美| 波多野结衣久久| 久久亚洲欧美日本精品| 一级a性色生活片久久无少妇一级婬片免费放 | 久久人人爽人人爽人人av东京热| 亚洲伊人久久大香线蕉综合图片| 久久免费国产精品一区二区| 久久久久久午夜精品| 久久九九全国免费| 亚洲AV无码久久精品成人| 久久综合伊人77777麻豆| 国产精品一久久香蕉国产线看观看| 久久久久无码精品国产app| 久久久久无码精品国产| 精品久久久一二三区| 久久本道综合久久伊人| 久久国产亚洲精品无码| 亚洲av伊人久久综合密臀性色| 久久精品国产亚洲Aⅴ香蕉 | 伊人久久大香线蕉无码麻豆| 中文字幕久久欲求不满| 波多野结衣中文字幕久久 | 久久久噜噜噜久久| 女人香蕉久久**毛片精品| 国产精品久久久亚洲| 久久久久亚洲AV无码永不| 久久无码国产专区精品| 亚洲欧美成人久久综合中文网 | 亚洲AV无码久久精品成人| 2021国产精品久久精品| 久久久这里只有精品加勒比| 理论片午午伦夜理片久久| 久久se精品一区二区影院| 国产精品无码久久久久| 久久亚洲国产成人影院网站| 久久久久综合国产欧美一区二区|