Best practice for compute the function return value

前端 未结 5 2063
你的背包
你的背包 2020-11-29 09:57

Often I built functions, in C, that checks some parameters and return an error code.

Which is the best approach to stop the values checking when I found an error?

相关标签:
5条回答
  • 2020-11-29 10:10

    The second is best because it is so much easier to read, scales well with increased complexity and immediately stops executing the function upon errors. This is the only sensible way to write such functions when you have extensive error handling inside a function, for example if the function is a parser or protocol decoder.

    That MISRA-C disallows multiple return statements in a function is a defect of MISRA-C. The intention is supposedly to disallow spaghetti code that returns from all over the place, but dogmatically banning multiple return statements can actually turn code far less readable, as we can see from your example. Imagine if you needed to check 10 different errors. You'd then have 10 compound if statements, which would be an unreadable mess.

    I have reported this defect several times to the MISRA committee but they have not listened. Instead, MISRA-C just blindly cites IEC 61508 as source for the rule. Which in turn only lists one questionable source for this rule (IEC 61508:7 C.2.9) and it's some a dinosaur programming book from 1979.

    This is not professional nor scientific - both MISRA-C and IEC 61508 (and ISO 26262) should feel ashamed over (directly or indirectly) listing subjective nonsense from 1979 as their only source and rationale.

    Simply use the second form and raise a permanent deviation against this defect MISRA rule.

    0 讨论(0)
  • 2020-11-29 10:14

    I tend to use a mix of the two styles, with 2nd style (multiple returns) before, and (perhaps) the first style (local variable to be returned later) afterwards.

    The rationale is: "multiple returns" is definitive. It can/should be used when there is something absolutely wrong about the parameters passed, or some other unrecoverable condition.
    The "local variable" style, instead, allows to write code that can modify the return value even more than once. It tends to produce code that means "let's start by supposing failure; but if everything is ok, then I will rewrite the result as OK". Or the contrary: "assume OK; if anything goes wrong set the result as failure". And in between of these steps, there still can be other returns!

    As last thought... I would say that the right style depends on the situation, never assume one is always right and the other is always wrong.

    0 讨论(0)
  • 2020-11-29 10:19

    The method I use is goto error_exit.

    You have to consider why a function might fail.

    Reason 1 is illegal arguments, like passing a negative to a square root. So assert fail, the error is caller's.

    Reason 2 is out of memory - that's an inherent problem with functions that scale. You need to shunt the failure up, though normally if a program won't give you a small amount of memory to hold, say, a file path, then it's dead.

    Reason 3 is bad grammar. That's a special case of illegal arguments. If the argument is a double for a square root, caller can reasonably be expected to check for negatives. If the argument is a basic program, caller cannot check for correctness except by effectively writing his own parser. So bad grammar needs to be handled as normal flow control.

    Reason 4 is malfunctioning hardware. Nothing much you can do except shunt the error up, unless you are familiar with the specific device.

    Reason 5 is an internal programming error. By definition there is no correct behaviour because your own code is not correct. But you often need to fudge or throw out degenerate cases in geometry, for example.

    The goto error_exit method is the one I favour, however. It keeps the one point of entry . and of exit principle essentially intact, without introducing artificial nesting for memory allocation errors that are less likely to happen than the computer breaking.

    0 讨论(0)
  • 2020-11-29 10:22

    Funny nobody noticed, that the above 2nd example demonstrates, why the MISRA rule exists in the first place: it leaves out a default return value for all cases where the if clauses do not match.

    So what happens, if (foo == bar) && (foo1 == bar1) && (foo2 == bar2) ?

    Moreover, in the 1st example for me it's easier to grasp, in which special case there is a non-default return value.

    0 讨论(0)
  • 2020-11-29 10:33

    I agree with the Lundin’s answer but I would like to provide another solution that complies with the single exit rule and still is similarly readable to the second example:

    ErrorCode_e myCheckFunction( some params )
    {
      ErrorCode_e error = CHECK_FAILED;
    
      if( foo != bar )
      {
         error = CHECK_FAILED;
      }
      else if( foo_1 != bar_1 )
      {
         error = CHECK_FAILED;
      }
      else if( foo_2 != bar_2 )
      {
         error = CHECK_SUCCESS;
      }
      else
      {
         // else (even empty) is required by MISRA after else-if
      }
      return error;
    }
    

    Since there are only two options in the example, we could use just one condition:

    ErrorCode_e myCheckFunction( some params )
    {
      ErrorCode_e error = CHECK_FAILED;
    
      if( (foo == bar) && (foo_1 == bar_1) && (foo_2 != bar_2) )
      {
         error = CHECK_SUCCESS;
      }
    
      return error;
    }
    

    This case can be even more simplified, we don’t need any local variables:

    ErrorCode_e myCheckFunction( some params )
    {
      return ( (foo == bar) && (foo_1 == bar_1) && (foo_2 != bar_2) )
          ? CHECK_SUCCESS : CHECK_FAILED;
    }
    
    0 讨论(0)
提交回复
热议问题