3

For example:

#define SUCCESS 1u
status_t status;

/* Initialize a peripheral */
status = start_Timer();

if(status == SUCCESS)
{
    /* Proceed */
    status = another_initialization();

    if(status == SUCCESS)
    {
        /* Proceed further */
    }

}

This gets a lot of indentation in a few consecutive procedures which leaves a really small line-width for the actual algorithms in the program. Is there any alternative to exception handling in C?

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
abraguez
  • 345
  • 3
  • 11

4 Answers4

4

Inspired from CrisBD answer but without return in middle of execution (not recommended by MISRA).

#define SUCCESS 1u
status_t status;

/* Initialize a peripheral */
status = start_Timer();

if(status == SUCCESS)
{
   /* Proceed */
   status = another_initialization();
}

if(status == SUCCESS)
{
    /* Proceed further */
}
Julien
  • 1,810
  • 1
  • 16
  • 34
  • This turns rather messy though, as the number of cases build up. Most importantly, the idea in mission-critical systems is to stop executing when you encounter an error. Less importantly, I think this version might also give worse branch prediction as you get numerous potentially pointless checks. – Lundin Apr 28 '20 at 08:32
  • Personally I find it equally messy as the other solution when scaling up. Branch prediction could be a problem depending of MCU. But eventually the compiler will optimize the code as in the first nested example. This would be interesting to try out. – Julien Apr 28 '20 at 09:46
2

I find it better to inverse the test logic and instead of testing to continue the flow of code, test to stop.

#define SUCCESS 1u
status_t status;

/* Initialize a peripheral */
status = start_Timer();

if(status != SUCCESS)
{
   return ;
}
/* Proceed */
status = another_initialization();

if(status != SUCCESS)
{
    return;
}
/* Proceed further */
ChrisBD
  • 9,104
  • 3
  • 22
  • 35
  • I agree that this can be a suitable way to deal with the typical situation to run a sequence of subfunctions, ready to abort on any (critical) error. However, the pattern leads to multiple `return` statements, which violates a MISRA rule. I have worked in different MISRA-compliant development teams, where I noticed very different opinions for/against relaxing that rule for cases like the present one. – HelpingHand Apr 27 '20 at 18:52
  • True, as always though it's a trade off between sometimes impenetrable code and something that can be followed. Normally though I'd look at something like an initialisation routine to follow some sort of state machine, so that it returns to a known state. – ChrisBD Apr 28 '20 at 06:53
  • 1
    Preferably you should `return status;` if the function where this code resides is properly written. If you do that, then I'd say this is the correct way to write such code. – Lundin Apr 28 '20 at 08:33
  • True. It's what I normally do, especially in embedded systems, just didn't post it up above. – ChrisBD Apr 28 '20 at 08:42
0

For simple code examples like the one provided in the question, I don't see a nesting problem (with or without braces and indentation). But I guess that the code snippet has been chosen as a minimal reproducible example to demonstrate an issue that may become serious with multiple block nestings.

For the more complicated examples, I would like to point to the important recommendation to limit the complexity of functions (usually the cyclomatic complexity). As far as I know, MISRA itself does not dictate a hard limit to any complexity measure. Nevertheless it is good practice to apply a limit at a McCabe cyclomatic complexity around 10-20.

This limits the maximum amount of nestable ifs, fors, switches etc. to a small number that can be handled even with considerable indent width and curly braces everywhere.

HelpingHand
  • 1,294
  • 11
  • 27
  • 1
    Yeah so by following the non-scientific hogwash MISRA/IEC 61508 rule of one single return statement (taken from a dinosaur programming book written in 1979), we make the code _less_ readable and _increase_ cyclomatic complexity radically. In other words, making the code _more dangerous_ in order to conform with MISRA/IEC 61508. There are cases where multiple returns are bad, but not cases where you have extensive error checks such as when writing protocol decoders, parsers etc and need to return an error at multiple places. – Lundin Apr 28 '20 at 08:27
  • @Lundin - This goes a bit too fast. Firstly, there is no equation between MISRA and IEC 61508. It is only that IEC 61508 (and its substandards) demand software development using programming languages like C and C++ to limit themselves to a language subset, which can be MISRA. Now, MISRA holds directives and rules that aren't all *mandatory*, but some are just *required*. In MISRA terminology, this means that organisation units may decide to apply or relax those rules (like the no-multi-return rule). I.e., you can even decide to skip that rule without even breaking MISRA. – HelpingHand Apr 28 '20 at 20:45
  • MISRA-C:2004 rule 14.7 (req) and MISRA-C:2012 rule 15.5 (adv) lists the following sources for the rule: "[IEC 61508-3 Table B.9], [ISO 26262-6 Table 8]". The rationale given for the rule in MISRA 2012 is "A single point of exit is required by IEC 61508 and ISO 26262 as part of the requirements for a modular approach." If you then in turn go to dig inside IEC 61508 for a rationale for the rule, they name the dinosaur book, which if memory serves was Edward Yourdon, _Classics in Software Engineering_ from 1979. No doubt teaching what was deemed classic back in 1979... – Lundin Apr 29 '20 at 06:45
  • And yes the solution is to create a permanent deviation against the rule. But you still have to argue against IEC 61508 "one entry/exit point" which is Highly Recommended from SIL 1 to 4. This is exactly how standards enforce engineers to introduce hazards in safety-critical systems, where no hazards existed before the standard enforced them. – Lundin Apr 29 '20 at 06:48
0

Unless the function allocates any resources which it has to clean up in case of failure, I would just break out of the function on errors. This is a MISRA violation, but you can easily justify it. The motivation behind this is to prevent resource leaks and discourage complex control flows which become unreadable. Breaking out of the function early with a prominent error code is simple and clean and not doing it (on a formal basis) is like hogtying yourself in the middle of a bar fight.