最近在读 lua 5.1.5 源码 , 看到以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#if defined(__cplusplus)
/* C++ exceptions */
#define LUAI_THROW(L,c) throw(c)
#define LUAI_TRY(L,c,a) try { a } catch(...) \
{ if ((c)->status == 0) (c)->status = -1; }

#define luai_jmpbuf int /* dummy variable */

#elif defined(LUA_USE_ULONGJMP)
/* in Unix, try _longjmp/_setjmp (more efficient) */
#define LUAI_THROW(L,c) _longjmp((c)->b, 1)
#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a }
#define luai_jmpbuf jmp_buf

#else
/* default handling with long jumps */
#define LUAI_THROW(L,c) longjmp((c)->b, 1)
#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
#define luai_jmpbuf jmp_buf

#endif

十分疑惑,所以就在网上找了资料学习了下。

怎么回事儿?

setjmplongjmp 需要通过 jmp_buf 结合起来使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
do 
{
jmp_buf __buf;

/**
* setting exceptions handling
*/

switch(setjmp(__buf))
{
case 0 : /* do nothing */ break;
case EXCEPTION_TYPE_1 : /* exception handle */ break;
case EXCEPTION_TYPE_2 : /* exception handle */ break;
case EXCEPTION_TYPE_3 : /* exception handle */ break;
.
.
.
default : /* final */ break;
}

/**
* there are some try blocks
*/

{
//BLOCK CODES

/**
* throw a exception
*/

longjmp(__buf, EXCEPTION_TYPE_VALUE);
}

} while(0);
  • setjmp 第一次执行的时候返回的是 0 ,表示成功设置 jmp_buf
  • longjmp 每次执行之后,程序便会跳转到 setjmp 处,再去执行 setjmp, 不过此时 setjmp 执行之后的返回值是本次 longjmp 调用时传入的第二个参数 EXCEPTION_TYPE_VALUE
  • setjmplongjmp 需要使用同一个 jmp_buf

这里我就很无赖的直接贴上这篇文章([Exceptions in C with Longjmp and Setjmp])最后的给出的代码([try_throw_catch.h])以供参考

最后说点其他的

看到这对函数,我更加明白为何在异常处理中出现异常会很难处理了。也突然想起之前学C语言的时候,书上不建议使用 goto 来写程序。说不定 setjmplongjmp 的实现和 goto 有点关系呢。

_setjmp 和 setjmp 区别

[_setjmp(3) - Linux man page]

  • _longjmp, _setjmp - non-local goto

goto 和 setjmp, longjmp 区别

[Difference between goto and longjmp() and setjmp()]

  • goto 只能在单一函数中实现跳转
  • setjmp, longjmp 可以实现在不同函数之间进行跳转