什么是宏重入?
宏在展开的时候,如果遇到同名的宏会终止展开,把同名的宏当成一个整体。
#define MY_CAT(p1,p2) MY_CAT_D(p1,p2)
#define MY_CAT_D(p1,p2) p1##p2
#define MY_NIL
#define AB(p1,p2) MY_CAT(p1,p2)
1,MY_CAT(A, B(x, y)) =>MY_CAT(x,y)
2,MY_CAT(AB(x,y),MY_NIL) =>xy
第2个不叫宏重入,叫“参数宏”,宏的参数是另外一个宏。这种情况下可以得到正确的展开结果xy。另外,这个地方为什么要定义MY_CAT_D?主要原因是MY_CAT在展开p1和p2的时候如果遇到##符号会停止展开,所以通过MY_CAT_D这个中间层可以使p1和p2先被展开然后再连接。
1在展开最外层MY_CAT之后,继续展开又会遇到MY_CAT,所以导致展开失败。
解决思路:通过定义相同功能不同名称的宏
方式1:定义AB的时候hardcode了一个可用的宏
#define MY_CAT1(p1,p2) MY_CAT1_D(p1,p2)
#define MY_CAT1_D(p1,p2) p1##p2
#define MY_CAT2(p1,p2) MY_CAT2_D(p1,p2)
#define MY_CAT2_D(p1,p2) p1##p2
#define MY_NIL
#define AB(p1,p2) MY_CAT2(p1,p2)
1,MY_CAT1(A, B(x, y)) =>xy
方式2:外层宏告诉内层宏使用相同功能可用的下一个“序号”
#define MY_CAT1(p1,p2) MY_CAT1_D(p1,p2)
#define MY_CAT1_D(p1,p2) p1##p2
#define MY_CAT2(p1,p2) MY_CAT2_D(p1,p2)
#define MY_CAT2_D(p1,p2) p1##p2
#define AB_D(d,p1,p2) MY_CAT##d(p1,p2)
MY_CAT1(A, B_D(2,x, y)) => xy
方式3:外层宏可以自动探测下一个可用宏
step1:定义一系列的MY_CAT_xxx宏
#define MY_CAT_1(p1,p2) MY_CAT_1_D(p1,p2)
#define MY_CAT_1_D(p1,p2) p1##p2
#define MY_CAT_2(p1,p2) MY_CAT_2_D(p1,p2)
#define MY_CAT_2_D(p1,p2) p1##p2
#define MY_CAT_3(p1,p2) MY_CAT_3_D(p1,p2)
#define MY_CAT_3_D(p1,p2) p1##p2
#define MY_CAT_4(p1,p2) MY_CAT_4_D(p1,p2)
#define MY_CAT_4_D(p1,p2) p1##p2
#define MY_CAT_5(p1,p2) MY_CAT_5_D(p1,p2)
#define MY_CAT_5_D(p1,p2) p1##p2
#define MY_CAT_6(p1,p2) MY_CAT_6_D(p1,p2)
#define MY_CAT_6_D(p1,p2) p1##p2
#define MY_CAT_7(p1,p2) MY_CAT_7_D(p1,p2)
#define MY_CAT_7_D(p1,p2) p1##p2
#define MY_CAT_8(p1,p2) MY_CAT_8_D(p1,p2)
#define MY_CAT_8_D(p1,p2) p1##p2
step2:定义谓词宏
#define MY_CAT_P(n) MY_CAT(MY_CAT_CHECK_, MY_CAT_ ## n(MY_NIL, MY_NIL))
step3:根据谓词宏拼装的宏来定义结果宏
#define MY_CAT_CHECK_MY_NILMY_NIL 1
#define MY_CAT_CHECK_MY_CAT_8(p1, p2) 0
#define MY_CAT_CHECK_MY_CAT_7(p1, p2) 0
#define MY_CAT_CHECK_MY_CAT_6(p1, p2) 0
#define MY_CAT_CHECK_MY_CAT_5(p1, p2) 0
#define MY_CAT_CHECK_MY_CAT_4(p1, p2) 0
#define MY_CAT_CHECK_MY_CAT_3(p1, p2) 0
#define MY_CAT_CHECK_MY_CAT_2(p1, p2) 0
#define MY_CAT_CHECK_MY_CAT_1(p1, p2) 0
说明:举例,如果MY_CAT_xxx不可用(未定义或已经被外层使用),MY_CAT_P的返回结果是MY_CAT_CHECK_MY_CAT_xxx(MY_NILL,MY_NIL)。我们可以把这个系列都定义成0。如果MY_CAT_xxx可用,MY_CAT_P返回值是MY_CAT_CHECK_MY_NILMY_NIL,把它定义成1.
step4:定义探测宏step5:测试
//#define AB MY_CAT(MY_CAT_, TL_AUTO_REC(MY_CAT_P, 4))
#define AB TL_AUTO_REC(MY_CAT_P, 8)
MY_CAT_4(A, B(x, y)) =>5(x,y)
MY_CAT_2(A, B(x, y)) =>3(x,y)
MY_CAT_1(A, B(x, y)) =>2(x,y)
说明:这个地方故意把AB定义成TL_AUTO_REC(MY_CAT_P, 8),可以看到自动检测的可用的”序号“