最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

c++ 預(yù)處理的圖靈完備之引言

 更新時(shí)間:2017年07月23日 16:15:06   作者:華夏之火  
這篇文章主要介紹了c++ 預(yù)處理的圖靈完備之引言,需要的朋友可以參考下

我們還是來(lái)討論c++吧,這幾年在c++里面玩代碼自動(dòng)生成技術(shù),而預(yù)處理是不可避免,也是不可或缺的重要工具。雖然boost pp預(yù)處理庫(kù)在宏的運(yùn)用上很是完善,但是代碼也太多了,而且代碼很不好理解,對(duì)此,不免讓人疑惑,有必要搞得那么復(fù)雜,搞那么多代碼嗎?并且,看了boostpp的使用接口后,感覺(jué)寫(xiě)得很不干凈,也不好組合。因此,重新做了一套預(yù)處理的輪子。以下的代碼,假設(shè)在msvc2013以上的版本運(yùn)行,反正很多人用MSVC的,裝逼的自當(dāng)別論,造出來(lái)的輪子,傾向于先支持msvc。

首先,我們定義一個(gè)宏,用來(lái)給把入?yún)⒆兂勺址?,咦,這個(gè)事情也太easy了,但是,在此,感覺(jué),還是有必要廢話(huà)多解釋一下。以下代碼慣例都是,所有可用的宏函數(shù)都是以PP開(kāi)頭全部大寫(xiě),而以_ZPP開(kāi)頭的全部都是內(nèi)部實(shí)現(xiàn),其實(shí)還可以做得更難看一點(diǎn)。因?yàn)楹旰瘮?shù)是全局的,沒(méi)有作用域的概念,并且只是單純的文本替換,死的時(shí)候,還不知道怎么死,所以,必須謹(jǐn)慎對(duì)待。像是windows.h頭文件那樣,直接用min,max作為宏的名字,雖然用起來(lái)很方便,但也不知道制造了多少麻煩,所以,很多時(shí)候,包含windows.h時(shí),第一件事情就是undef min和max。

以下的代碼,可以隨便在某個(gè)工程下,隨便建立一個(gè)cpp后綴名的源文件,然后按CTRL+F7編譯,不需要F5,就可以看到運(yùn)行的效果,如果編譯通過(guò),就說(shuō)明宏基本上正確,測(cè)試代碼越多,準(zhǔn)確性就越高。當(dāng)然,你們也可以通過(guò)設(shè)置源文件的屬性,讓msvc生成預(yù)處理后的文件,然后用記事本打開(kāi)那個(gè)文件觀(guān)看。

#define PP_TEXT(str) _ZPP_TEXT(str)
#define _ZPP_TEXT(str) #str

在c++預(yù)處理宏中,操作符#是將后面跟隨的表達(dá)式加上兩個(gè)雙引號(hào),也就是字符串。PP_TEXT(str)不是直接定義成#str,而是通過(guò)調(diào)用_ZPP_TEXT(str),然后在那里才將入?yún)⒆兂勺址@得有點(diǎn)輾轉(zhuǎn),有點(diǎn)多此一舉,但,其實(shí)是為了支持宏的全方位展開(kāi),也就是入?yún)tr本身也存在宏調(diào)用的時(shí)候,純屬無(wú)奈。比如,如果這樣實(shí)現(xiàn)

#define PP_TEXT(str) #str

那么,對(duì)于下面的情況,

#define AAA aaa

PP_TEXT(AAA),結(jié)果將是"AAA",而不是"aaa"。因?yàn)楹瓴僮鞣苯邮菍⑷雲(yún)⒆兂勺址?,沒(méi)有讓入?yún)⒂幸稽c(diǎn)點(diǎn)回旋的空間,所以只好引入間接層,讓入?yún)⒂袡C(jī)會(huì)宏展開(kāi)。后面,很多宏函數(shù)都是這樣實(shí)現(xiàn),不得不間接調(diào)用,以便讓宏全面展開(kāi)。而msvc的宏展開(kāi)機(jī)制更加奇葩,更加不人性化,其間接調(diào)用的形式也更丑陋。這都是沒(méi)辦法的事情。
然后,為了調(diào)試宏,或者測(cè)試宏,當(dāng)然,很多時(shí)候,調(diào)試宏,還是要打開(kāi)預(yù)處理的文件來(lái)對(duì)比分析。我們對(duì) static_assert作一點(diǎn)點(diǎn)包裝,因?yàn)閟tatic_assert需要兩個(gè)參數(shù),c++11后面的c++版本中,static_assert好像只需要一個(gè)入?yún)?,那時(shí)就不需要這個(gè)包裝了。

#define PP_ASSERT() static_assert((__VA_ARGS__), PP_TEXT(__VA_ARGS__));

PP_ASSERT(...)里面的三個(gè)點(diǎn),是不定參數(shù)的宏,而__VA_ARGS__就代表了...所匹配的所有參數(shù),這條語(yǔ)法很重要,要熟練。這里,就不詳細(xì)解釋其用法了,后面會(huì)有大把大把的宏函數(shù)用到__VA_ARGS__。
好了,我們可以開(kāi)始用PP_ASSERT(...)做測(cè)試了。
PP_ASSERT(2+3==5)
如果,然后編譯這個(gè)文件,發(fā)現(xiàn)編譯通過(guò)了,比如
PP_ASSERT(2+3==4)
編譯的時(shí)候,就會(huì)報(bào)錯(cuò)誤信息,error C2338: 2+3==4
好了,測(cè)試準(zhǔn)備建立起來(lái),就可以開(kāi)始肆無(wú)忌憚的寫(xiě)代碼了。一步一步地構(gòu)建c預(yù)處理宏的圖靈完備。
顯然,當(dāng)務(wù)之急,最根本的宏就是將兩個(gè)宏參數(shù)的并接,也即是##運(yùn)算符,顯然好比#運(yùn)算那樣子,必須給里面參數(shù)有宏展開(kāi)的機(jī)會(huì),因此要間接調(diào)用,下面是其實(shí)現(xiàn)

#define PP_JOIN(_A, _B) _ZPP_JOIN_I(_A, _B)
#define _ZPP_JOIN_I(_A, _B) _ZPP_JOIN_II(~, _A##_B)
#define _ZPP_JOIN_II(p, res) res

竟然不止一層間接,而是兩層,又多此一舉,是因?yàn)榘l(fā)現(xiàn)在做宏遞歸的時(shí)候,一層間接調(diào)用還不能讓宏充分地展開(kāi),所以只好又加間接層,也不明白是何原因,也懶得追究了?,F(xiàn)在,接下來(lái),當(dāng)然是測(cè)試PP_JOIN了。各位同學(xué),可以新建立一個(gè)測(cè)試文件,那個(gè)文件include我們的這個(gè)宏函數(shù)。當(dāng)然,也可以在同一個(gè)文件里面寫(xiě)測(cè)試代碼,注意分成兩段代碼,上一段寫(xiě)宏函數(shù),下一段寫(xiě)測(cè)試代碼,目前來(lái)看,都可以的,后面再整理。

PP_ASSERT(PP_JOIN(1+2, == 3))
#define A 20
#define B 10
PP_ASSERT(PP_JOIN(A + B, == 30))

有了PP_JOIN,就可以開(kāi)始做點(diǎn)其他事情了。比如,計(jì)數(shù)器,

#define _ZPP_INC_JOIN(_A, _B) _ZPP_INC_JOIN_IMP1(_A, _B)
#define _ZPP_INC_JOIN_IMP1(_A, _B) _ZPP_INC_JOIN_IMP2(~, _A##_B)
#define _ZPP_INC_JOIN_IMP2(p, res) res

#define PP_INC(x, ) _ZPP_INC_JOIN(_ZPP_INC_, x)
#define _ZPP_INC_0 1
#define _ZPP_INC_1 2
#define _ZPP_INC_2 3
#define _ZPP_INC_3 4
#define _ZPP_INC_4 5
#define _ZPP_INC_5 6
#define _ZPP_INC_6 7
#define _ZPP_INC_7 8
#define _ZPP_INC_8 9
#define _ZPP_INC_9 10

這里,我們重新又實(shí)現(xiàn)了一遍PP_JOIN,這也是沒(méi)辦法的事情,后面在重重嵌套的時(shí)候,會(huì)出現(xiàn)PP_JOIN里面又包含PP_JOIN的情況,這樣會(huì)導(dǎo)致宏停止展開(kāi)了,所以,只好對(duì)于每一個(gè)要用到JOIN之處,都用自己版本的JOIN。
這是宏函數(shù)的實(shí)現(xiàn)方式,通過(guò)并接,文本替換,一一枚舉,才達(dá)到這樣的效果,也就是說(shuō),我們通過(guò)JOIN函數(shù),在宏里面構(gòu)造了一個(gè)計(jì)數(shù)器的數(shù)據(jù)類(lèi)型。如果每個(gè)宏函數(shù)都這樣寫(xiě),豈不是很累。好消息是,只需用這種苦逼方式實(shí)現(xiàn)幾個(gè)最基本的函數(shù),然后通過(guò)宏的遞歸引擎,其他的宏函數(shù)就不需這樣子一個(gè)一個(gè)苦逼的并接替換了。
PP_ASSERT(PP_INC(9)==10)
PP_ASSERT(PP_INC(PP_INC(9)) == 11)
寫(xiě)測(cè)試代碼習(xí)慣了,寫(xiě)起來(lái)就很有意思了,測(cè)試通過(guò),也是最激動(dòng)人心的時(shí)刻。
接下來(lái),要處理msvc里面宏的惡心行為,然后就結(jié)束本引言。

#define PAIR_SECOND(x, y) y
PP_ASSERT(PAIR_SECOND(10, 20) == 20)

這樣子,還不錯(cuò),下面,再define一個(gè)宏函數(shù),讓其返回一個(gè)pair,也就是兩個(gè)值

#define MAKE_PAIR(x, y) x, y

然后,這樣調(diào)用,
PAIR_SECOND(MAKE_PAIR(10, 20))
編譯器馬上就不高興了,warning C4003: “PAIR_SECOND”宏的實(shí)參不足
好像是編譯器沒(méi)有先展開(kāi)MAKE_PAIR(10, 20),然后再調(diào)用PAIR_SECOND,而是直接把MAKE_PAIR(10, 20)整個(gè)當(dāng)成一個(gè)函數(shù)傳給PAIR_SECOND,然后,PAIR_SECOND就提示實(shí)參不足,然后,硬要測(cè)試,
PP_ASSERT(PAIR_SECOND(MAKE_PAIR(10, 20)) == 20)
顯然,無(wú)論如何,編譯器勢(shì)必就龍顏大怒了。對(duì)此,我們只好再引入間接層,想辦法讓MAKE_PAIR(10, 20)先展開(kāi),然后再傳給PAIR_SECOND。這樣,就不能直接用這樣的形式了,PAIR_SECOND(MAKE_PAIR(10, 20)) 。只好改成這樣,下面的幾行代碼,很有點(diǎn)驚天地泣鬼神的味道。

#define _ZPP_INVOKE_JOIN(_A, _B) _ZPP_IMP_INVOKE_JOIN_I(_A, _B)
#define _ZPP_IMP_INVOKE_JOIN_I(_A, _B) _ZPP_IMP_INVOKE_JOIN_II(~, _A##_B)
#define _ZPP_IMP_INVOKE_JOIN_II(p, res) res

#define PP_INVOKE(m, args, ) _ZPP_INVOKE_JOIN(m, args) 

前面幾行代碼都是PP_INVOKE的JOIN函數(shù)實(shí)現(xiàn),可以直接當(dāng)它們是JOIN函數(shù),關(guān)鍵是PP_INVOKE(m, args, ...)這里,第一個(gè)參數(shù)m是宏函數(shù),第二個(gè)是args,是要傳給第一個(gè)參數(shù)m的參數(shù)列表,用括號(hào)括起來(lái),至于后面的省略號(hào),是有些時(shí)候?yàn)榱巳偩幾g器而添加的,也不知道是什么原因,反正這樣子就可以了,懶得追究。垃圾宏,垃圾預(yù)處理,只要能完成功能就行了,c++中,代碼生成代碼,重頭戲在tmp那里,宏只是小小必要的輔助工具而已。然后,這樣調(diào)用,
PP_ASSERT(PP_INVOKE(PAIR_SECOND, (MAKE_PAIR(10, 20))) == 20)
編譯通過(guò)了,好不容易?。?/p>

相關(guān)文章

  • C++如何實(shí)現(xiàn)廣義表詳解

    C++如何實(shí)現(xiàn)廣義表詳解

    廣義表是非線(xiàn)性結(jié)構(gòu),其定義是遞歸的。那么下面跟著小編一起看看如何用C++實(shí)現(xiàn)廣義表,有需要的可以參考借鑒。
    2016-08-08
  • C++實(shí)現(xiàn)猜數(shù)字游戲

    C++實(shí)現(xiàn)猜數(shù)字游戲

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)猜數(shù)字游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • 簡(jiǎn)介C++編程中的運(yùn)算符重載

    簡(jiǎn)介C++編程中的運(yùn)算符重載

    這篇文章簡(jiǎn)單介紹了C++編程中的運(yùn)算符重載,是C++入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-09-09
  • C語(yǔ)言深入淺出講解直接插入排序算法的實(shí)現(xiàn)

    C語(yǔ)言深入淺出講解直接插入排序算法的實(shí)現(xiàn)

    插入排序也是最簡(jiǎn)單的一類(lèi)排序方法,我今天介紹的也是插入排序里最直觀(guān)且淺顯易懂的直接插入排序。對(duì)這個(gè)很簡(jiǎn)單的排序,記得當(dāng)時(shí)也是花了近兩個(gè)晚上才搞懂它的原理的,接下來(lái)就來(lái)介紹一下
    2022-05-05
  • C++設(shè)計(jì)模式之建造者模式(Builder)

    C++設(shè)計(jì)模式之建造者模式(Builder)

    這篇文章主要介紹了C++設(shè)計(jì)模式之建造者模式Builder的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • C++讀取帶空格字符串的方法

    C++讀取帶空格字符串的方法

    今天小編就為大家分享一篇C++讀取帶空格字符串的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • C語(yǔ)言驅(qū)動(dòng)開(kāi)發(fā)內(nèi)核枚舉IoTimer定時(shí)器解析

    C語(yǔ)言驅(qū)動(dòng)開(kāi)發(fā)內(nèi)核枚舉IoTimer定時(shí)器解析

    這篇文章主要為大家介紹了C語(yǔ)言驅(qū)動(dòng)開(kāi)發(fā)內(nèi)核枚舉IoTimer定時(shí)器解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • c++ 類(lèi)函數(shù)作為模板參數(shù)實(shí)現(xiàn)方式詳解

    c++ 類(lèi)函數(shù)作為模板參數(shù)實(shí)現(xiàn)方式詳解

    這篇文章主要介紹了c++ 類(lèi)函數(shù)作為模板參數(shù)實(shí)現(xiàn)方式,在實(shí)現(xiàn)中加入增強(qiáng)邏輯,這種方式對(duì)代碼侵入性過(guò)高,而且無(wú)法控制該邏輯是否需要,如果不需要的話(huà)又得重新修改代碼實(shí)現(xiàn),需要的朋友可以參考下
    2023-03-03
  • C++11基于范圍的for循環(huán)代碼示例

    C++11基于范圍的for循環(huán)代碼示例

    這篇文章主要給大家介紹了關(guān)于C++11基于范圍的for循環(huán)的相關(guān)資料,范圍for循環(huán)(也稱(chēng)為C++11的基于范圍的for循環(huán))是一種簡(jiǎn)化迭代容器(如數(shù)組、向量、列表等)元素的方式,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-06-06
  • C++ string格式化輸出方式

    C++ string格式化輸出方式

    今天小編就為大家分享一篇C++ string格式化輸出方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-12-12

最新評(píng)論

东方市| 阳信县| 兴隆县| 东宁县| 昭通市| 玛沁县| 含山县| 康马县| 内江市| 永胜县| 宽甸| 富民县| 罗甸县| 黄梅县| 太谷县| 靖远县| 漳浦县| 南通市| 南郑县| 浦江县| 板桥市| 肥城市| 星子县| 镇宁| 武定县| 延吉市| 崇州市| 威宁| 来凤县| 视频| 泾阳县| 巴南区| 大余县| 淳化县| 靖州| 南昌县| 浦北县| 娄底市| 越西县| 拉孜县| 沙雅县|