【问题标题】:Hiding longjmps in C++ interface to C code在 C ++ 接口中隐藏 longjmps 到 C 代码
【发布时间】:2013-12-19 16:00:55
【问题描述】:

为广泛使用 longjmp 和多个跳转目标进行错误管理的旧 C 代码生成 C++ API 的正确方法是什么?

我的想法是编写一个函数,为每个使用的目标设置跳转目标,例如:

void catchJumps() {
    if (setjmp(target1)) throw Error1(); //Error1 and Error2 are some exception classes
    if (setjmp(target2)) throw Error2();
    //...
}

然后我会在每个使用 C 代码的 C++ 函数(更具体地,在每个范围内)中调用 catchJumps

int some_wrapper() {
    catchJumps();
    callCFunction()

    for (int i = 0; i < 1000; i++) {
        catchJumps();
        callOtherCFunction();
    }

    catchJumps();
    callOneMoreCFunction();
    callEvenOneMoreCFunction();
}

这是在不破坏堆栈的情况下捕获所有跳远的安全方法吗?我知道,将 longjmp 放入不同的堆栈帧是很危险的。现在我的函数catchJumps 位于另一个堆栈帧中,而不是调用some_wrapper。我希望(或者我什至可以做到)catchJumps 可以被内联,这样框架是一样的,但我不知道。

每个作用域中的调用(以及上面的循环之后)应该是所有作用域对象的析构函数都被调用的必要条件,对吧?

如果这不是将 longjmps“转换”为调用应用程序的断言的有效方法,我们还能做什么?

【问题讨论】:

  • “我知道,将 longjmp 放入不同的堆栈帧很危险。”这实际上是 longjmp 的 全部要点,只要您 展开 到前一帧,这并不是特别危险,换句话说,您不会 longjmp 到一个函数已经回来了。 C++ 运行时还提供了longjmp 的实现,可以正确展开堆栈帧,因此使用起来很安全(即使不鼓励使用)。
  • 那可能行不通。跳转目标位于catchJumps 中,这是一个在发生长跳转时不再位于堆栈上的例程。宏是“隐藏”跳远的最佳选择。这是否是个好主意是另一个问题。
  • 另外,你能访问图书馆的源代码吗?
  • @zneak:是的,但不能将库中的 longjmps 更改为其他内容,因为它用于不同的项目。
  • 如果重新编译库是一种选择,我建议您将setjmp 定义为空,将longjmp(x) 定义为throw &amp;x。这样,您将能够捕获jmpbuf_t*,并且您将能够通过简单的相等性检查来检查它是哪一个,而对源代码的干扰最小。

标签: c++ c api setjmp


【解决方案1】:

catchJumps 与具有https://stackoverflow.com/a/1376099/471164 中所述的析构函数的自动对象一起使用并引用18.7/4“其他运行时支持”时,您可能会遇到问题:

如果任何自动对象会被抛出的异常破坏 将控制权转移到程序中的另一个(目标)点, 然后在传输点调用 longjmp(jbuf, val) 对同一(目标)点的控制具有未定义的行为。

我认为更好的方法是为您使用的每个 C 函数创建一个包装器,并且可以执行 longjmp 将所有这些非本地 goto 转换为异常。这也将使您的代码更简洁,因为您不会到处都有catchJumps(),而只会在这些包装函数中。

【讨论】:

  • 感谢您的回答,这可能是最干净的解决方案。我只是不遗余力地创建了数千个包装器:-/
  • 是的,我也喜欢这个解决方案。 @urzeit:你肯定不会调用旧 API 的所有函数吗?您只需根据需要创建包装器。
【解决方案2】:

既然您在库中遇到这样的 API,那么让 catchJumps 通过要求传入零参数可调用并使用函数指针或 boost/std::function 来执行实际调用怎么样?

template <typename CallMe>
void catchJumps(CallMe wrappee)
{
    if (setjmp(target1)) throw Error1(); //Error1 and Error2 are some exception classes
    if (setjmp(target2)) throw Error2();
    //...

    wrappee();
}

int some_wrapper()
{
    catchJumps(&callCFunction);

    for (int i = 0; i < 1000; i++)
    {
        catchJumps(&callOtherCFunction);
    }

    catchJumps(&callOneMoreCFunction);
    catchJumps(&callEvenOneMoreCFunction);
}

【讨论】:

  • 或 lambda... -- 不需要类型擦除。
  • 听起来不错;但是有没有一种通用的方法来以这种调用方法的通用形式传递参数(python 家伙会将其称为装饰器?)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-03
  • 2019-01-30
  • 2015-08-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多