bhook里有一段重載宏函數(shù)的實現(xiàn):
#define BYTESIG_TRY(...) \
do { \
pid_t _bytesig_tid_ = gettid(); \
if (0 == _bytesig_tid_) _bytesig_tid_ = (pid_t)syscall(SYS_gettid); \
sigjmp_buf _bytesig_jbuf_; \
int _bytesig_sigs_[] = {__VA_ARGS__}; \
bytesig_protect(_bytesig_tid_, &_bytesig_jbuf_, _bytesig_sigs_, sizeof(_bytesig_sigs_) / sizeof(int)); \
int _bytesig_protected_ = 1; \
int _bytesig_ex_ = sigsetjmp(_bytesig_jbuf_, 1); \
if (0 == _bytesig_ex_) {
#define BYTESIG_CATCH_2(signum_, code_) \
} \
else { \
bytesig_unprotect(_bytesig_tid_, _bytesig_sigs_, sizeof(_bytesig_sigs_) / sizeof(int)); \
_bytesig_protected_ = 0; \
int signum_ = (int)(((unsigned int)_bytesig_ex_ & 0xFF0000U) >> 16U); \
int code_ = 0; \
if (((unsigned int)_bytesig_ex_ & 0xFF00U) > 0) \
code_ = (int)(((unsigned int)_bytesig_ex_ & 0xFF00U) >> 8U); \
else if (((unsigned int)_bytesig_ex_ & 0xFFU) > 0) \
code_ = -((int)((unsigned int)_bytesig_ex_ & 0xFFU)); \
(void)signum_; \
(void)code_;
#define BYTESIG_CATCH_1(signum_) BYTESIG_CATCH_2(signum_, _bytesig_code_)
#define BYTESIG_CATCH_0() BYTESIG_CATCH_1(_bytesig_signum_)
#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, BYTESIG_CATCH_2, BYTESIG_CATCH_1, ))
#define NO_ARG_EXPANDER() , , BYTESIG_CATCH_0
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__())
#define BYTESIG_CATCH(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
#define BYTESIG_EXIT \
} \
if (1 == _bytesig_protected_) \
bytesig_unprotect(_bytesig_tid_, _bytesig_sigs_, sizeof(_bytesig_sigs_) / sizeof(int)); \
} \
while (0);
/*
int *p = NULL;
//
// usage 1
//
BYTESIG_TRY(SIGSEGV, SIGBUS)
{
*p = 1;
}
BYTESIG_CATCH(signum, code)
{
LOG("signum %d (code %d)", signum, code);
}
BYTESIG_EXIT
//
// usage 2
//
BYTESIG_TRY(SIGSEGV, SIGBUS)
{
*p = 2;
}
BYTESIG_CATCH(signum)
{
LOG("signum %d", signum);
}
BYTESIG_EXIT
//
// usage 3
//
BYTESIG_TRY(SIGILL)
{
func_maybe_illed();
}
BYTESIG_CATCH()
{
do_something();
}
BYTESIG_EXIT
//
// usage 4
//
BYTESIG_TRY(SIGABRT)
{
func_maybe_aborted();
}
BYTESIG_EXIT
*/
BYTESIG_CATCH推導(dǎo)過程
BYTESIG_TRY宏和BYTESIG_EXIT宏都很容易理解,但是BYTESIG_CATCH宏卻存在三種用法BYTESIG_CATCH()\BYTESIG_CATCH(signum)\BYTESIG_CATCH(signum, code)。那我們一起看下BYTESIG_CATCH的實現(xiàn)。
BYTESIG_CATCH_2宏是BYTESIG_CATCH的最終實現(xiàn),為兩個參數(shù)版本,BYTESIG_CATCH_1為單個參數(shù)版本,BYTESIG_CATCH_0為無參數(shù)版本。我們大膽的猜測,
BYTESIG_CATCH(signum, code)
BYTESIG_CATCH(signum)
BYTESIG_CATCH()
最終會被推導(dǎo)成
BYTESIG_CATCH_2(signum, code)
BYTESIG_CATCH_1(signum)
BYTESIG_CATCH_0()
根據(jù)上述猜想,我們推斷MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)會被拆解兩部分,第一部分MACRO_CHOOSER(__VA_ARGS__)被推導(dǎo)成宏定義的名稱,第二部分(__VA_ARGS__)會被推導(dǎo)成參數(shù)列表,拿BYTESIG_CATCH(signum, code)舉例,MACRO_CHOOSER(__VA_ARGS__)被解釋成BYTESIG_CATCH_2,(signum, code)被解釋成(signum, code)。
那開始我們的推導(dǎo)步驟:
BYTESIG_CATCH(signum, code)
BYTESIG_CATCH(signum)
BYTESIG_CATCH()
先被推導(dǎo)成
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER signum, code())(signum, code)
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER signum())(signum)
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER ())()
下一步被推導(dǎo)成
FUNC_RECOMPOSER((NO_ARG_EXPANDER signum, code(), BYTESIG_CATCH_2, BYTESIG_CATCH_1, ))(signum, code)
FUNC_RECOMPOSER((NO_ARG_EXPANDER signum(), BYTESIG_CATCH_2, BYTESIG_CATCH_1, ))(signum)
FUNC_RECOMPOSER((NO_ARG_EXPANDER (), BYTESIG_CATCH_2, BYTESIG_CATCH_1, ))()
下一步被推導(dǎo)成
FUNC_CHOOSER (NO_ARG_EXPANDER signum, code(), BYTESIG_CATCH_2, BYTESIG_CATCH_1, )(signum, code)
FUNC_CHOOSER (NO_ARG_EXPANDER signum(), BYTESIG_CATCH_2, BYTESIG_CATCH_1, )(signum)
FUNC_CHOOSER (NO_ARG_EXPANDER (), BYTESIG_CATCH_2, BYTESIG_CATCH_1, )()
這里需要特別注意,預(yù)處理器僅執(zhí)行簡單的文本替換。它僅從在括號內(nèi)看到的逗號數(shù)推斷出宏函數(shù)的參數(shù)個數(shù),用逗號分隔的“參數(shù)”不必具有有效的語法,這些參數(shù)可以是任何文本,也就是說在上面的示例中,NO_ARG_EXPANDER signum, code(), BYTESIG_CATCH_2, BYTESIG_CATCH_1,總共四個逗號,被視為四個參數(shù),第一個參數(shù)是NO_ARG_EXPANDER signum,第二個參數(shù)是code(),第三個參數(shù)是BYTESIG_CATCH_2,第四個參數(shù)是BYTESIG_CATCH_1。
下一步被推導(dǎo)成
BYTESIG_CATCH_2(signum, code)
BYTESIG_CATCH_1(signum)
FUNC_CHOOSER (, , BYTESIG_CATCH_0, BYTESIG_CATCH_2, BYTESIG_CATCH_1, )()
下一步被推導(dǎo)成
BYTESIG_CATCH_2(signum, code)
BYTESIG_CATCH_1(signum)
BYTESIG_CATCH_0()
BYTESIG_TRY Vs. BYTESIG_CATCH
從示例中我們可以看出BYTESIG_TRY也存在多種用法BYTESIG_TRY(SIGSEGV, SIGBUS)/BYTESIG_TRY(SIGILL)/BYTESIG_TRY(SIGABRT),而BYTESIG_TRY的實現(xiàn)卻比BYTESIG_CATCH簡單的多,這兩種到底有什么區(qū)別呢?
BYTESIG_TRY(SIGSEGV, SIGBUS)
{
*p = 1;
}
BYTESIG_CATCH(signum, code)
{
LOG("signum %d (code %d)", signum, code);
}
BYTESIG_EXIT
仔細(xì)觀看會發(fā)現(xiàn),BYTESIG_CATCH塊內(nèi)使用了BYTESIG_CATCH的參數(shù)signum和code,而BYTESIG_TRY并沒有,即BYTESIG_CATCH是指定了BYTESIG_CATCH塊內(nèi)局部變量的名稱。如果宏塊內(nèi)不需要指定的參數(shù)即塊內(nèi)局部變量,可直接使用BYTESIG_TRY的實現(xiàn)方式#define BYTESIG_TRY(...)。