C\C++語言中的宏多重展開和遞歸展開


宏定義中的#,##
1. 宏中的參數前面使用一個#,預處理器會把這個參數轉換為一個字符數組    
2.記號粘貼操作符(token paste operator): ##       
    “##”是一種分隔連接方式,它的作用是先分隔,然后進行強制連接       
實參代入宏文本后,實參之前或之后遇到###,實參不再展開 
                                     

一個較長的宏定義可以分成若干行,這需要在待續的行末尾加上一個反斜杠符”\”

#define TUP_ASSERT(__expression) do {\

        if (!( __expression )) \

        { \

            SYSLOG_LEGACY(EAaSysLogSeverityLevel_Error,"<TUP ASSERTION FAILED> (%s) file: %s line:%u", #__expression,__FILE__, __LINE__); \

            AaErrorAssertion(#__expression,__FILE__, __LINE__); \

        } \

} while(0)

在替換文本中,參數名以#作為前綴則結果將被擴展為由實際參數替換該參數的帶引號的字符串

#define dprint(expr) printf(#expr “ =%g\n”, expr)

使用語句dprint(x/y),該宏將被擴展為

printf(“x/y” “= %g\n”, x/y);

預處理運算符##為宏展開提供參數連接的作用

#define paste(front, back) front ##back

宏調用paste(name,1)的結果將建立記號name1

宏展開順序大致可以歸結為:

第一步:首先用實參代替形參,將實參代入宏文本中

第二步:如果實參也是宏,則展開實參

第三步:最后繼續處理宏替換后的宏文本,如果仍包含宏,則繼續展開

注意:如果在第二步,實參代入宏文本后,實參之前或之后遇到###,實參不再展開

1

#define cat(a,b) a ## b

宏調用:cat(cat(1, 2), 3) 的展開順序為:

cat(cat(1, 2), 3) -->cat(1, 2) ## 3  -->cat(1, 2)3

cat(1,2)仍是宏,但后面是##,不再展開,結果為:cat(1, 2)3

2

#define  cat(a,b)   a ## b

#define  xcat(x, y)   cat(x, y)

宏調用 xcat(xcat(1, 2), 3) 的展開順序為:

xcat(xcat(1,2), 3) -->cat(xcat(1, 2), 3) -->cat(cat(1, 2), 3) -->cat(1 ## 2, 3) --> 1 ##2 ## 3 -->123

務必注意參數的處理順序,第二步在第三步前面執行,第一層宏展開后,實參是宏,則首先處理實參的宏展開,即使宏替換后本身也是宏。在例2中,雖然也生成的cat(cat(1, 2), 3),但是是首先執行里面的cat(1, 2), 所以結果不一樣。例2中以下順序是錯的:

xcat(xcat(1,2), 3) --> cat(xcat(1, 2), 3) --> xcat(1, 2) ## 3 -->xcat(1, 2)3

 

Linux測試結果:

使用gcc –E 編譯可以只做預處理:源文件hepeng.c:

*****************************************************************************

#define cat(a,b) a ## b

#define xcat(x,y) cat(x,y)

int  main()

{

   cat(cat(1,2),3);

   xcat(xcat(1,2),3);

   xcat(cat(1,2),3);

    return 0;

}

*****************************************************************************

[penhe@hzling23 program]$gcc -E hepeng.c |more

*****************************************************************************

# 1 "hepeng1.c"

# 1 "<built-in>"

# 1 "<command line>"

# 1 "hepeng1.c"

int main()

{

    cat(1,2)3;

    123;

    123;

    return 0;

}

*****************************************************************************

 

boost 中包含了許多奇技淫巧的代碼,這里分析宏的自身迭代

以這樣的宏代碼調用

1
BOOST_PP_ENUM_PARAMS(4, typename T)

它的宏展開為

1
typename T0 , typename T1 , typename T2 , typename T3

這在boost中被多用於簡化代碼量 比如 boos::function 中

下面來分析這類宏的具體實現

宏1:

1
#define BOOST_PP_ENUM_PARAMS(count, param) BOOST_PP_REPEAT(count, BOOST_PP_ENUM_PARAMS_M, param)

首先看看 BOOST_PP_ENUM_PARAMS_M 的作用

宏2:

1
#define BOOST_PP_REPEAT BOOST_PP_CAT(BOOST_PP_REPEAT_, BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4))

由宏2,可以看出,宏1展開為

1
BOOST_PP_CAT(BOOST_PP_REPEAT_, BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4))(count, BOOST_PP_ENUM_PARAMS_M, param)

BOOST_PP_AUTO_REC(BOOST_PP_REPEAT_P, 4) 其實就是 1,這個是一個定值,在目前,我們不必去深究 那么, BOOST_PP_CAT 就可以展開為

1
BOOST_PP_REPEAT_1(count, BOOST_PP_ENUM_PARAMS_M, param)

BOOST_PP_REPEAT_1 是一個非常簡單的迭代宏

1
2
3
4
5
6
7
# define BOOST_PP_REPEAT_1(c, m, d) BOOST_PP_REPEAT_1_I(c, m, d)
# define BOOST_PP_REPEAT_1_I(c, m, d) BOOST_PP_REPEAT_1_ ## c(m, d)
# define BOOST_PP_REPEAT_1_0(m, d)
# define BOOST_PP_REPEAT_1_1(m, d) m(2, 0, d)
# define BOOST_PP_REPEAT_1_2(m, d) BOOST_PP_REPEAT_1_1(m, d) m(2, 1, d)
# define BOOST_PP_REPEAT_1_3(m, d) BOOST_PP_REPEAT_1_2(m, d) m(2, 2, d)
... ...

所以

1
BOOST_PP_REPEAT(count, BOOST_PP_ENUM_PARAMS_M, param)

展開就等於

1
2
3
4
BOOST_PP_ENUM_PARAMS_M(2, 0, param)
BOOST_PP_ENUM_PARAMS_M(2, 1, param)
... ...
BOOST_PP_ENUM_PARAMS_M(2, count - 1, param)

BOOST_PP_ENUM_PARAMS_M 自然也是一個宏

1
# define BOOST_PP_ENUM_PARAMS_M(z, n, param) BOOST_PP_COMMA_IF(n) param ## n

所以就有

1
2
BOOST_PP_ENUM_PARAMS_M(2, 0, param) // 展開 第一個參數不要, 其實這個參數被用於優化的目的
BOOST_PP_COMMA_IF(0) param ## 0

BOOST_PP_COMMA_IF 是一個這樣的宏,如果參數非0,那么打印出逗號,否則就不打印逗號

1
2
3
4
5
6
7
8
9
10
11
12
13
#define BOOST_PP_COMMA_IF(cond) BOOST_PP_IF(cond, BOOST_PP_COMMA, BOOST_PP_EMPTY)()
#define BOOST_PP_IF(cond, t, f) BOOST_PP_IIF(BOOST_PP_BOOL(cond), t, f)
#define BOOST_PP_IIF(bit, t, f) BOOST_PP_IIF_I(bit, t, f)
#define BOOST_PP_IIF_I(bit, t, f) BOOST_PP_IIF_ ## bit(t, f)
# define BOOST_PP_IIF_0(t, f) f
# define BOOST_PP_IIF_1(t, f) t
#define BOOST_PP_BOOL(x) BOOST_PP_BOOL_I(x)
# define BOOST_PP_BOOL_I(x) BOOST_PP_BOOL_ ## x
# define BOOST_PP_BOOL_0 0
# define BOOST_PP_BOOL_1 1
# define BOOST_PP_BOOL_2 1
... ...
# define BOOST_PP_BOOL_256 1

可以看出,宏多用窮舉

 
參考:

 https://www.xuebuyuan.com/2198272.html

 https://blog.csdn.net/buye1986/article/details/45100339

可以看出,宏多用窮舉


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM