有哪些“好”的东西? 如何使用 longjmp/setjmp 进行 C 错误处理?
我必须在一个项目中使用 C,并且我正在考虑使用 longjmp/setjmp 进行错误处理,因为我认为在一个中心位置处理错误比返回代码要容易得多。 如果有一些关于如何做到这一点的线索,我将不胜感激。
我特别关心如果发生任何此类错误,资源清理是否正确完成。
另外,如何处理导致多线程程序使用它们的错误?
更好的是,是否已经存在一些用于错误/异常处理的 C 库?
I have to use C for one project and I am thinking of using longjmp/setjmp
for error handling as I think it will be much easier to handle error in one central place than return codes. I would appreciate if there are some leads on how to do this.
I am particularly concerned with resource cleanup being correctly done if any such error occurs.
Also how do I handle errors that result in multi-threaded programs using them?
Even better, is there some C library that already exists for error/exception handling?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
Symbian 根据
longjmp()
实现了它的Leave
机制,这可以很好地介绍您需要做的所有事情。Symbian 有一个全局“清理堆栈”,您可以在发生跳转时推送和弹出要清理的内容。 这是 C++ 编译器在引发 C++ 异常时执行的自动堆栈展开的手动替代方案。
Symbian 有“陷阱线束”,它会跳出来; 这些可以嵌套。
(Symbian 最近根据 C++ 异常重新实现了它,但接口保持不变)。
总而言之,我认为正确的 C++ 异常不太容易出现编码错误,并且比滚动您自己的 C 等效项要快得多。
(现代 C++ 编译器非常擅长处理“零开销”异常(例如,当不抛出异常时;
longjmp()
必须存储所有寄存器的状态,即使跳转不晚) 诱人使用 C++ 作为更好的 C(仅采用异常和 RAII)将是一个很好的途径,使用
longjmp()
进行异常模拟应该很 给你。Symbian implemented it's
Leave
mechanism in terms oflongjmp()
and this serves as a good walk through of all the things you need to do.Symbian has a global 'cleanup stack' that you push and pop things you want cleaned up should an jump happened. This is the manual alternative to the automatic stack unwinding that a C++ compiler does when a C++ exception is thrown.
Symbian had 'trap harnesses' that it would jump out to; these could be nested.
(Symbian more recently reimplemented it in terms of C++ exceptions, but the interface remains unchanged).
All together, I think that proper C++ exceptions are less prone to coding errors and much faster than rolling your own C equivalent.
(Modern C++ compilers are very good at 'zero overhead' exceptions when they are not thrown, for example;
longjmp()
has to store the state of all the registers and such even when the jump is not later taken, so can fundamentally never be as fast as exceptions.)Using C++ as a better C, where you only adopt exceptions and RAII, would be a good route should using
longjmp()
for exception emulation be tempting to you.到目前为止,异常是一种更好的通用机制,但在过去 C 语言最黑暗的日子里,我编写了一个包含命令 shell 的处理器模拟器。 shell 用于设置jmp/longjmp 进行中断处理(即,处理器正在运行并且用户按下break/ctrl-c,代码将SIGINT 和longjmps 捕获回shell)。
Exceptions are by far a better general mechanism, but in the deep dark days of C past, I wrote a processor emulator that included a command shell. The shell used to setjmp/longjmp for interrupt handling (ie, the processor is running and the user hits break/ctrl-c, the code traps SIGINT and longjmps back to the shell).
我相当整洁地使用了
setjmp
/longjmp
来从回调中逃脱,而无需通过其他各种库级别来协商。这种情况(如果我没记错的话)是 yacc 生成的解析器中的代码可以检测到(非语法)问题,并且想要放弃解析,但向另一端的调用者提供相当有用的错误报告。所有 yacc 生成的代码。 另一个例子是在 Expat 解析器调用的回调中。 在每种情况下,都有其他方法可以做到这一点,但它们似乎比简单地以这种方式摆脱困境更加麻烦和晦涩。
不过,正如其他答案所指出的那样,有必要小心清理,并非常仔细地确保
longjmp
代码只能在longjmp
动态保护的区域范围内调用。代码>setjmp。在多线程编程的上下文中执行它? 我相信这并非不可能,但是哦:现在就拿出你家庭装的阿司匹林吧。 让
setjmp
/longjmp
对尽可能靠近可能是明智的做法。 只要匹配的setjmp
/longjmp
对位于同一线程内,我希望你会没事,但是......要小心。I've used
setjmp
/longjmp
reasonably tidily, to escape from within a callback, without having to negotiate my way up through various other library levels.That case (if I recall correctly) was where a code within a yacc-generated parser could detect a (non-syntactical) problem, and wanted to abandon the parse but give a reasonably useful error report back to the caller on the other side of all the yacc-generated code. Another example was within a callback called from an Expat parser. In each case, there were other ways of doing this, but they seemed more cumbersome and obscure than simply bailing out in this way.
As other answers have pointed out, though, it's necessary to be careful about clean-up, and very thoughtful about making sure that the
longjmp
code is callable only within the scope of the region dynamically protected by thesetjmp
.Doing it in the context of multi-threaded programming? I'm sure that's not impossible, but Oooh: get out your family-pack of aspirin now. It's probably wise to keep the
setjmp
/longjmp
pairs as close together as possible. As long as a matchingsetjmp
/longjmp
pair are within the same thread, I expect you'll be OK, but ... be careful out there.看看这个例子/教程:
http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch .html
Have a look at this example/tutorial:
http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html
如果您担心资源清理,您必须认真考虑
longjmp()
和setjmp()
是否是一个好主意。如果您设计的资源分配系统实际上可以准确地进行清理,那么这是可以的 - 但这种设计往往很棘手,并且通常是不完整的,如果事实上您的代码使用的标准库本身分配的资源必须被发布。 它需要特别小心,并且由于它并不完全可靠,因此它不适合长时间运行的系统,这些系统可能需要多次使用
setjmp()
/longjmp() 调用(它们会泄漏、扩展并最终导致问题)。
If you are worried about resource cleanup, you have to seriously wonder whether
longjmp()
andsetjmp()
are a good idea.If you design your resource allocation system so that you can in fact clean up accurately, then it is OK - but that design tends to be tricky, and typically incomplete if, in fact, the standard libraries that your code uses themselves allocate resources that must be released. It requires extraordinary care, and because it is not wholly reliable, it is not suitable for long-running systems that might need to survive multiple uses of the
setjmp()
/longjmp()
calls (they'll leak, expand, and eventually cause problems).我只发现一个用于
setjmp()/longjmp()
,并且与错误处理无关。实际上没有必要为此使用它,因为它总是可以被重构为更容易遵循的东西。
setjmp()/longjmp()
的使用与goto
非常相似,因为它很容易被滥用。 一般来说,任何降低代码可读性的事情都是一个坏主意。 请注意,我并不是说它们本质上是不好的,只是它们比其他替代方案更容易导致糟糕的代码。FWIW,它们最有价值的地方是我在行业早期所做的一个项目(MS-DOS 6 时间框架)。 我设法使用 Turbo C 组合了一个协作多线程库,该库在
yield()
函数中使用这些函数来切换任务。我很确定从那时起我就没有碰过它们(或者没有必要碰过它们)。
I have only ever found one use for
setjmp()/longjmp()
and it wasn't to do with error handling.There really is no need to use it for that since it can always be refactored into something easier to follow. The use of
setjmp()/longjmp()
is very similar togoto
in that it can be easily abused. Anything that makes your code less readable is a bad idea in general. Note that I'm not saying they're inherently bad, just that they can lead to bad code easier than the alternatives.FWIW, the one place they were invaluable was a project I did in the early days of the industry (MS-DOS 6 time frame). I managed to put together a co-operative multi-threading library using Turbo C which used those functions in a
yield()
function to switch tasks.I'm pretty certain I haven't touched them (or had the need to) since those days.