bhook里有一段重载宏函数的实现:
#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推导过程
BYTESIG_TRY宏和BYTESIG_EXIT宏都很容易理解,但是BYTESIG_CATCH宏却存在三种用法BYTESIG_CATCH()\BYTESIG_CATCH(signum)\BYTESIG_CATCH(signum, code)。那我们一起看下BYTESIG_CATCH的实现。
BYTESIG_CATCH_2宏是BYTESIG_CATCH的最终实现,为两个参数版本,BYTESIG_CATCH_1为单个参数版本,BYTESIG_CATCH_0为无参数版本。我们大胆的猜测,
BYTESIG_CATCH(signum, code)
BYTESIG_CATCH(signum)
BYTESIG_CATCH()
最终会被推导成
BYTESIG_CATCH_2(signum, code)
BYTESIG_CATCH_1(signum)
BYTESIG_CATCH_0()
根据上述猜想,我们推断MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)会被拆解两部分,第一部分MACRO_CHOOSER(__VA_ARGS__)被推导成宏定义的名称,第二部分(__VA_ARGS__)会被推导成参数列表,拿BYTESIG_CATCH(signum, code)举例,MACRO_CHOOSER(__VA_ARGS__)被解释成BYTESIG_CATCH_2,(signum, code)被解释成(signum, code)。
那开始我们的推导步骤:
BYTESIG_CATCH(signum, code)
BYTESIG_CATCH(signum)
BYTESIG_CATCH()
先被推导成
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 ())()
下一步被推导成
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, ))()
下一步被推导成
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, )()
这里需要特别注意,预处理器仅执行简单的文本替换。它仅从在括号内看到的逗号数推断出宏函数的参数个数,用逗号分隔的“参数”不必具有有效的语法,这些参数可以是任何文本,也就是说在上面的示例中,NO_ARG_EXPANDER signum, code(), BYTESIG_CATCH_2, BYTESIG_CATCH_1,总共四个逗号,被视为四个参数,第一个参数是NO_ARG_EXPANDER signum,第二个参数是code(),第三个参数是BYTESIG_CATCH_2,第四个参数是BYTESIG_CATCH_1。
下一步被推导成
BYTESIG_CATCH_2(signum, code)
BYTESIG_CATCH_1(signum)
FUNC_CHOOSER (, , BYTESIG_CATCH_0, BYTESIG_CATCH_2, BYTESIG_CATCH_1, )()
下一步被推导成
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的实现却比BYTESIG_CATCH简单的多,这两种到底有什么区别呢?
BYTESIG_TRY(SIGSEGV, SIGBUS)
{
*p = 1;
}
BYTESIG_CATCH(signum, code)
{
LOG("signum %d (code %d)", signum, code);
}
BYTESIG_EXIT
仔细观看会发现,BYTESIG_CATCH块内使用了BYTESIG_CATCH的参数signum和code,而BYTESIG_TRY并没有,即BYTESIG_CATCH是指定了BYTESIG_CATCH块内局部变量的名称。如果宏块内不需要指定的参数即块内局部变量,可直接使用BYTESIG_TRY的实现方式#define BYTESIG_TRY(...)。