重载宏函数

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的参数signumcode,而BYTESIG_TRY并没有,即BYTESIG_CATCH是指定了BYTESIG_CATCH块内局部变量的名称。如果宏块内不需要指定的参数即块内局部变量,可直接使用BYTESIG_TRY的实现方式#define BYTESIG_TRY(...)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容