【问题标题】:Techniques for testing and report multiple function call return errors测试和报告多个函数调用返回错误的技术
【发布时间】:2014-04-29 01:21:24
【问题描述】:

我正在研究函数调用的错误测试和报告技术,尤其是在调用多个函数时。作为我的意思的一个例子,为简单起见,每个函数都返回一个布尔值:

success = false;

if (fnOne ())
{
    if (fnTwo ())
    {
        if (fnThree ( ))
        {
            success = true;
        }
        else
        {
            cout << "fnThree failed" <<endl;
        }
    }
    else
    {
        cout << "fnTwo failed" <<endl;
    }
}
else
{
    cout << "fnOne failed" <<endl;
}

我发现上面的例子(我到处都可以看到)代码很快变得不可读,尤其是当它调用的代码变成多屏高度时。

目前我在 C++ 中处理这个问题的方式(包括“c”标签,以防有人使用流畅的 C 技术)我在我的对象中存储了一个布尔值和一个字符串。 bool 代表成功/失败,字符串代表失败状态的原因。我调用一个函数,如果函数失败,该函数在内部将对象设置为失败状态并提供基于字符串的原因。我仍然对这种方法不是 100% 满意......但它是迄今为止我所拥有的最好的。外观示例:

void myobj::fnOne (void)
{
    if (m_fluxCapacitorProngCount > 3)
    {
        setState (false, "myobj::fnOne - Flux capacitor has been breeding again");
    }
}

void myobj::fnTwo (void)
{
    if (m_answerToLifeUniverseAndEverything != 42)
    {
        setState (false, "myobj::fnTwo - Probability drive enabled?");    
    }
}

void myobj::setup (void)
{
    // Ensure time travel is possible
    if (valid())
    {
        fnOne ();
    }

    // Ensure the universe has not changed
    if (valid())
    {
        fnTwo ();
    }

    // Error? show the reason
    if (valid() == false)
    {
        cout << getStateReason () << end;
    }
}

其中valid()返回true/false,getStateReason()返回错误发生时函数中提供的字符串。

我喜欢它不需要嵌套条件就可以增长,对我来说我觉得这更具可读性,但我确信存在问题......

处理检测和报告多个函数调用返回条件的最佳[最干净]方法是什么?

【问题讨论】:

  • 您忘记了一个变体(第一个变体):bool r1 = fnOne(), r2 = fnTwo(); r3 = fnThree(); if (r1 &amp;&amp; r2 &amp;&amp; r3) { /* do something */ } else { /* do something else */ }
  • Exceptions 是 C++ 的一个选项,我已经看到它被使用过(不幸的是,大部分情况很糟糕,请省去我的恐怖故事),对于 C(简单而干净)来说不是这样:stackoverflow.com/questions/2891766/…跨度>

标签: c++ error-checking


【解决方案1】:

此代码应该比您的第一个变体更清晰:

if (!fnOne ())
{
    cout << "fnOne failed" <<endl;
    return;
}
if (!fnTwo ())
{
    cout << "fnTwo failed" <<endl;
    return;
}
if (!fnThree ())
{
    cout << "fnThree failed" <<endl;
    return;
}
success = true;

一般来说,对于 C++,您可以使用异常来处理错误。

【讨论】:

  • 我实际上喜欢这种方法,并且我意识到这个主题如何与关于是否应该在每个函数的多个位置使用“return”的哲学密切相关。我一般不会,因为我有过很多不好的经历,调试过大的代码,进入多级条件然后突然退出。我认为像这样使用 return flat 是不错的
  • 也许它有助于区分状态机和错误处理。问自己一个问题:如果 fnOne 失败了可以吗?程序能否正常恢复?如果是这样,也许您的问题可以从使用状态机中受益。否则我会抛出异常。但这是基于意见的,所以试验一下,看看在你的场景中什么是有效的。关于缩进:在我看来,任何函数都不要使用超过两个级别。
【解决方案2】:

如果您真的希望一个函数返回一个表示其他几个函数成功/失败的值(仅此而已 - 不是每个函数的通用返回值,这需要某种返回数组/元组/向量的方法值),这是一种方法:

int bigFunction()
{ int return_value = 0;

  if (function1() != 0)
    return_value |= (1 << 0);

  if (function2() != 0)
    return_value |= (1 << 1);

  if (function3() != 0)
    return_value |= (1 << 2);

  // ....

  return return_value;
}

这个想法是在返回值中分配一个位来指示每个子功能的成功/失败。如果您的子函数具有您实际想要捕获的一小组可能的返回值,则每个函数可以使用多个位 - 即两位将允许您为该字段提供四个不同的值。

另一方面,这样的事情意味着您可能要么 a) 编写一些非常低级的代码,如设备驱动程序或内核或其他东西,要么 b) 可能有更好的方法来解决手头的问题。

【讨论】:

    【解决方案3】:

    处理代码中的错误(错误)和用户输入引起的错误本身就是一个巨大的话题。您采用的技术取决于代码的复杂性和代码的预期寿命。您为家庭作业项目采用的错误处理策略不如您为学期项目采用的错误处理策略复杂,后者比您为内部项目采用的错误处理策略简单,后者将是比将广泛分发给客户的项目更简单。

    策略 1:写入错误消息并中止

    你可以在家庭作业项目中使用的最简单的错误处理策略是写一条消息到stdout,然后调用abort()

    void fun1(int in)
    {
      if (in < 0 )
      {
        printf("Can't work with a negative number.\n");
        abort();
      }
    
      // Rest of the function.
    }
    

    策略二:设置全局错误码并返回

    下一级错误处理涉及检测错误输入并在不调用abort() 的情况下对其进行处理。您可以设置一个全局可访问的错误代码来指示错误的类型。我建议将这种方法用于家庭作业项目、学期项目和探索性项目。

    void fun2(int in)
    {
      if (in < 0 )
      {
        // Indicate that "fun2" came accross a NEGATIVE_INTEGER_ERROR.
        setErrorCode(NEGATIVE_INTEGER_ERROR, "fun2");
        return;
      }
    
      // Rest of the function.
    }
    
    void funUser(int in)
    {
      // Call fun2
      fun2(in);
    
      // If fun2 had any errors, deal with it.
      if (checkErrorCode())
      {
         return;
      }
    
      // Rest of the function.
    }
    

    下一级错误处理涉及检测错误输入并使用其他选项进行处理。您可以从函数返回错误代码。如果您使用的是 C++,则可能会引发异常。这两种选择都是处理大型项目的有效方式——无论是在内部还是为了更广泛的使用而分发。它们适用于用户群超出开发团队的任何项目。

    策略3:从函数返回错误码

    int fun3(int in)
    {
      if (in < 0 )
      {
        // Indicate that "fun3" came accross a NEGATIVE_INTEGER_ERROR.
        return NEGATIVE_INTEGER_ERROR;
      }
    
      // Rest of the function.
    }
    
    void funUser(int in)
    {
      // Call fun3
      int ecode = fun3(in);
    
      // If fun3 had any errors, deal with it.
      if (ecode)
      {
         return;
      }
    
      // Rest of the function.
    }
    

    策略4:从函数中抛出错误代码(C++)

    void fun4(int in)
    {
      if (in < 0 )
      {
        // Indicate that "fun4" came accross a NEGATIVE_INTEGER_ERROR.
        throw NEGATIVE_INTEGER_ERROR;
      }
    
      // Rest of the function.
    }
    
    void funUser(int in)
    {
      // Call fun4. Be prepared to deal with the exception or let it be
      // dealt with another function higher up in the call stack.
      // It makes sense to catch the exception only if this function do
      // something useful with it.
      fun4(in);
    
      // Rest of the function.
    }
    

    希望这为您提供了足够的背景知识,以便为您的项目采用适当的错误处理策略。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-06
      • 2012-07-09
      • 2013-06-19
      • 1970-01-01
      • 2023-04-07
      • 1970-01-01
      相关资源
      最近更新 更多