2013-01-22 58 views
2

檢查下面的宏:升壓預處理 - 奇怪的結果

#define INPUT (char, "microsecond", "us")(int, "millisecond", "ms")(int, "second", "s")(int, "minute", "min")(float, "hour", "h") 

目標是增加圍繞每個元組雙括號導致:

((char, "microsecond", "us"))((int, "millisecond", "ms"))((int, "second", "s"))((int, "minute", "min"))((float, "hour", "h")) 

現在我用下面的宏來做這項工作:

#define ADD_PAREN_1(A, B, C) ((A, B, C)) ADD_PAREN_2 
#define ADD_PAREN_2(A, B, D) ((A, B, C)) ADD_PAREN_1 
#define ADD_PAREN_1_END 
#define ADD_PAREN_2_END 
#define OUTPUT0 ADD_PAREN_1 INPUT 
#define OUTPUT1 BOOST_PP_CAT(OUTPUT0, _END) 

結果如下:

OUTPUT0是好的:

((char, "microsecond", "us")) ((int, "millisecond", C)) ((int, "second", "s")) ((int, "minute", C)) ((float, "hour", "h")) ADD_PAREN_2 

但當BOOST_PP_CAT被稱爲OUTPUT1的結果是:

float 

我不理解這種行爲。任何提示?

注意我使用Visual Studio 2010

+4

暗示當日:不要使用宏。 –

+2

@Alex:非常有幫助。謝謝 ! – Mark

+0

爲什麼不只是在地區使用搜索和替換?是否有一些原因需要預處理器而不是編輯器加倍? – AShelly

回答

2

預處理器通過掃描和展開工作。因此,當它擴展了您OUTPUT0宏,它提供了:

ADD_PAREN_1 INPUT 
^ 

然後它會掃描下一個標記,看它是否是一個括號,如果是它會調用ADD_PAREN_1作爲函數宏。但是,它只會看到INPUT,所以它不會調用ADD_PAREN_1。接下來,它會掃描,並擴大了下一個標記:

ADD_PAREN_1 INPUT 
      ^

這將導致在此:

ADD_PAREN_1 (char, "microsecond", "us")(int, "millisecond", "ms")(int, "second", "s")(int, "minute", "min")(float, "hour", "h") 
      ^

接下來,當您嘗試使用OUTPUT1,它便會擴展成:

BOOST_PP_CAT(OUTPUT0, _END) 

其中BOOST_PP_CAT將擴大OUTPUT0然後連接令牌,所以您最終會得到這個:

ADD_PAREN_1 (char, "microsecond", "us")(int, "millisecond", "ms")(int, "second", "s")(int, "minute", "min")(float, "hour", "h") ## _END 

正如您所看到的,您正在使用_END(不允許)與括號相連,並導致編譯器錯誤。在Visual Studio中,您可能會看到不同的結果,因爲它們的預處理器以神祕的方式工作。

最終,使它工作,你只需要在OUTPUT0宏,像這樣以應用額外的掃描:

#define X(x) x 
#define OUTPUT0 X(ADD_PAREN_1 INPUT) 

這將使用C預處理器的工作,我不知道這是否準確在Visual Studio中的工作(我沒有獲得它現在檢查),但我知道這個工程:

#define ADD_PAREN(x) BOOST_PP_CAT(ADD_PAREN_1 x, _END) 
#define ADD_PAREN_1(A, B, C) ((A, B, C)) ADD_PAREN_2 
#define ADD_PAREN_2(A, B, D) ((A, B, C)) ADD_PAREN_1 
#define ADD_PAREN_1_END 
#define ADD_PAREN_2_END 
#define OUTPUT1 ADD_PAREN(INPUT) 

這是類似的,他們如何做到這一點的提升。請參閱BOOST_FUSION_ADAPT_ASSOC_STRUCT_FILLER宏如何使用here

+1

「在Visual Studio中,您可能會看到不同的結果,因爲它們的預處理器以神祕的方式工作。」對!剛剛做了一些繁重的預處理器工作,包括許多連接和重新擴展新形成的宏調用,並且發現了許多關於其預處理器的有趣事實... :)(例如,它將'__VA_ARGS__'視爲單個標記,除非您展開它。)我通常調用你的'X'宏'EXPAND_ARG'。 +1 – GManNickG

+0

@Paul:非常感謝你的回答。額外掃描的竅門確實解決了這個問題。 – Mark