【发布时间】:2010-10-23 14:55:14
【问题描述】:
我必须在一个项目中使用 C,我正在考虑使用 longjmp/setjmp 进行错误处理,因为我认为在一个中心位置处理错误比返回代码要容易得多。如果有一些关于如何做到这一点的线索,我将不胜感激。
如果发生任何此类错误,我特别关心资源清理是否正确。
另外我该如何处理导致多线程程序使用它们的错误?
更好的是,是否已经存在一些用于错误/异常处理的 C 库?
【问题讨论】:
标签: c error-handling setjmp
我必须在一个项目中使用 C,我正在考虑使用 longjmp/setjmp 进行错误处理,因为我认为在一个中心位置处理错误比返回代码要容易得多。如果有一些关于如何做到这一点的线索,我将不胜感激。
如果发生任何此类错误,我特别关心资源清理是否正确。
另外我该如何处理导致多线程程序使用它们的错误?
更好的是,是否已经存在一些用于错误/异常处理的 C 库?
【问题讨论】:
标签: c error-handling setjmp
如果您担心资源清理,您必须认真考虑 longjmp() 和 setjmp() 是否是个好主意。
如果您设计的资源分配系统实际上可以准确地清理,那么它是可以的 - 但这种设计往往很棘手,而且通常不完整,如果事实上,您的代码使用自己的标准库分配必须释放的资源。它需要特别小心,并且由于它不是完全可靠的,因此它不适合可能需要在多次使用 setjmp()/longjmp() 调用后存活的长时间运行的系统(它们会泄漏、扩展并最终导致问题)。
【讨论】:
【讨论】:
thread_local 变量可能会对您有所帮助。
Symbian 以longjmp() 的形式实现了它的Leave 机制,这可以很好地了解您需要做的所有事情。
Symbian 有一个全局“清理堆栈”,如果发生跳转,您可以推送和弹出要清理的内容。这是 C++ 编译器在抛出 C++ 异常时执行的自动堆栈展开的手动替代方法。
Symbian 有“陷阱线束”,它会跳出来;这些可以嵌套。
(Symbian 最近根据 C++ 异常重新实现了它,但接口保持不变。
总的来说,我认为正确的 C++ 异常更不容易出现编码错误,并且比滚动您自己的 C 等效项要快得多。
(例如,现代 C++ 编译器非常擅长在不抛出“零开销”异常时;longjmp() 必须存储所有寄存器的状态等,即使以后不进行跳转,也可以基本上永远不会像异常一样快。)
使用 C++ 作为更好的 C,您只采用异常和 RAII,这将是一个很好的途径,应该使用 longjmp() 进行异常模拟对您很有吸引力。
【讨论】:
我只发现一个用于setjmp()/longjmp(),它与错误处理无关。
确实没有必要为此使用它,因为它总是可以被重构为更容易理解的东西。 setjmp()/longjmp() 的使用与goto 非常相似,因为它很容易被滥用。通常,任何使您的代码可读性降低的事情都是一个坏主意。请注意,我并不是说它们天生就不好,只是说它们比替代品更容易导致糟糕的代码。
FWIW,他们非常宝贵的一个地方是我在行业早期所做的一个项目(MS-DOS 6 时间框架)。我设法使用 Turbo C 组合了一个协作多线程库,它使用 yield() 函数中的这些函数来切换任务。
我很确定从那时起我就没有碰过它们(或有必要)。
【讨论】:
到目前为止,异常是一种更好的通用机制,但在 C 过去的黑暗时期,我编写了一个包含命令 shell 的处理器模拟器。 shell 用于设置 jmp/longjmp 以进行中断处理(即处理器正在运行并且用户点击 break/ctrl-c,代码将 SIGINT 和 longjmps 捕获回 shell)。
【讨论】:
我已经相当整洁地使用了setjmp/longjmp,以从回调中逃脱,而无需通过其他各种库级别进行协商。
这种情况(如果我没记错的话)是 yacc 生成的解析器中的代码可以检测到(非语法)问题,并希望放弃解析但在所有 yacc 生成的代码的另一面。另一个例子是在从 Expat 解析器调用的回调中。在每种情况下,都有其他方法可以做到这一点,但它们似乎比简单地以这种方式摆脱困境更加麻烦和晦涩。
正如其他答案所指出的那样,有必要小心清理,并且非常周到地确保 longjmp 代码只能在由 setjmp 动态保护的区域范围内调用.
在多线程编程的上下文中执行它?我敢肯定这不是不可能的,但是哦:现在就拿出你家装的阿司匹林吧。将setjmp/longjmp 对尽可能靠近可能是明智之举。只要匹配的setjmp/longjmp 对在同一个线程中,我希望你会没事,但是......要小心。
【讨论】: