【问题标题】:How to forcefully break the execution of a method如何强制中断方法的执行
【发布时间】:2013-11-26 09:25:49
【问题描述】:

我有一个方法:

void doSomethingMain(char* s)
{
   doSomethingElse(s);
   doMoreStuff(s);
}

doSomethingElse() 的方法是这样的:

void doSomethingElse(char* s)
{
   anotherMethod(s);
   moreStuff(s);
}

其中anotherMethod()moreStuff() 调用其他方法,而这些方法又可能返回doSomethingElse()moreStuff() 或其他方法......以非常递归的方式,但它们都没有调用main doSomethingMain() 方法。这是一个有限递归,它将在s 被成功使用时结束。这些方法是无效的,如果不付出巨大的努力,我们就无法改变它。

所有这些方法都在使用参数s,它们正在检查s 中的错误。我想要发生的情况如下:当调用堆栈中的某个方法在其输入参数(s)中遇到错误时,我想立即中止执行发现错误的函数,然后跳回doSomethingMain() 中的 doMoreStuff()

显然,C++ 方法的直接解决方案之一是throwcatch() 对,而C 方法是setjmplongjmp 对...但是我们的单元测试框架不喜欢 throw ... catch 对(它将它们解释为错误),我想避免 longjmp 方法,所以这里是问题:

为了在方法中中断应用程序的执行流程并在特定位置(方法之后的下一条语句)继续,我们还有哪些其他选择?

【问题讨论】:

  • 这正是longjmp() 的用途,抱歉。然而,理论上你可以让你的所有函数都返回一个错误代码,然后所有对这些函数的调用都应该检查返回代码,当遇到错误时,任何函数都应该立即返回(使用相同的错误代码)。因此,一个错误将导致立即返回到第一个调用者(通过调用堆栈上的所有其他帧)。
  • 真正的答案取决于您要使用的语言。现在是 C 还是 C++?
  • @H2CO3:很好。敢于回答! ;-)
  • @alk 谢谢,完成了。
  • 我敢说投掷是完全你想要的正确解决方案(虽然我认为你想要的可能是错误的,assert 可能更适用于深度嵌套的递归函数发现它的输入参数是错误的——但这值得商榷)。无论如何,这是一种“错误,很少发生”的例外情况。正确调用析构函数,清除代码,工作不崩溃。为什么测试框架不喜欢异常?如果您确实捕获并且不重新抛出,则测试框架将永远无法看到它们。

标签: c++ c exception exit break


【解决方案1】:

您可以更改方法以返回 bool。如果解析成功,则在递归结束时返回true,将传递给调用者函数。如果任何一点解析不成功,返回false,并在调用函数中测试整个递归的返回值。

但是我们的单元测试框架不喜欢 throw ... catch 对(它将它们解释为错误,并且在它们之后不再执行测试)

更改您的测试单元框架。

【讨论】:

  • 那你就没有问题了:)
  • 是的,我在考虑 throwing 时出现错误。
  • 是的,我也是,但是 google 测试框架吞下了这个问题 :( 打印一条消息,如:unknown file: Failure Unknown C++ exception thrown in the test body.
【解决方案2】:

返回一个错误代码,该代码已被检查。例如,您可以在错误时返回 false(参见 C 中的 <stdbool.h>),在成功时返回 true。如果一个函数也返回false, then return (possible withfalse`)。

我不推荐使用setjmp/longjmp,即使是这样的情况。

【讨论】:

  • 这种方法的问题是已经有一个 API(基于这些愚蠢的 void 方法)被公司的客户使用了很长时间,如果 API会改变... :(
【解决方案3】:

正是longjmp() 的用途,抱歉。但是,理论上您可以让所有函数都返回错误代码,然后应检查对这些函数的所有调用是否有返回代码。遇到错误时,任何调用函数都应立即返回(使用相同的错误代码)。因此,一个错误将导致立即返回到第一个调用者(通过调用堆栈上的所有其他帧)。

【讨论】:

  • 当我尝试longjmp 时,应用程序得到了一个 SIGSEGV ...可能是因为它(或多或少)是 C++?比如:C++ 方法调用 C 方法(带有一个指向 C++ 对象的指针……它又调用回另一个对象)。
  • @fritzone 我认为这不是问题。我怀疑你用错了longjmp()(很容易用错...)
  • longjmp 不能很好地与 C++ 配合使用。调用时不会调用析构函数。
  • @Sean 确实如此,但为什么不称为 segfault 的析构函数呢?充其量它会泄漏内存,不是吗?
  • @fritzone 如果你想完全避免longjmp(),那么我相信除了“长而正常的返回链”方法之外别无选择。
【解决方案4】:

但是我们的单元测试框架不喜欢 throw ... catch 对(它将它们解释为错误,并且不再执行测试 在他们之后)

这个呢:

try
{
  doMoreStuff(s);
}
catch (...)
{
  // hmmm I won't rethrow because my unit testing framework doesn't like it
}

不使用异常的正当理由是,如果由于某种原因,它们被禁用 (-fno-exceptions)。

【讨论】:

    【解决方案5】:

    没有跳转或 goto 的最佳方法是检查返回并执行一个 do while 循环:

        void myFunc(char *s) {
    
          bool doneAll = false;
          int res = 0;
    
          do {
            res = doSomething(s);
            if(!res) break;
            res = doSomethingElse(s);
            if(!res) {
              break;
            } else { 
              doneAll = true;
            }
          while (false); // no really a loop, a smart way of using breaks
          // you know can access doneAll to check if everything is done
    
          // Any cleanup here as well
        }
    

    【讨论】:

      【解决方案6】:

      另一种方法 - 使用全局错误标志。

      static int found_error = 0;
      
      void anyFunction(char *s) {
          if (found_error) return;
      
          if (something_failed) {
              found_error = 1;
              return;
          }
      }
      

      现在,检测到错误后,仍然会调用一些函数,但不会执行任何操作。

      【讨论】:

        最近更新 更多