do-while-zero construct & MISRA Rule 19.4

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

do-while-zero construct & MISRA Rule 19.4

5,475 Views
Ironhorse
Contributor I

 [WRT: CodeWarrior IDE 5.9.0 / Target S12X]

 

I am trying to comply with MISRA Rule 19.4, specifically the requirement that the do-while-zero construct is “the only permitted mechanism for having complete statements in a macro body.”

 

But, when I compile

do{<stuff>}while (0);

the compiler reasonably gives the message “C4001: Condition always false”.

 

Is there a way to use the do-while-zero construct without this nuisance message but without generally disabling the C4001 message, e.g., #pragma MESSAGE DISABLE C4001?          

 

Mind you, for compliance reasons I must have traceability between object and source code (i.e., no optimizations that mangle or obfuscate the object code).  Also, it would be nice to not have any branch that is always false or always true in the object code.

Labels (1)
0 Kudos
19 Replies

2,917 Views
Lundin
Senior Contributor IV

"do while zero" doesn't make sense to use, it is a mechanism used for "spaghetti coding". The only use for it is code like this:

 

do{  ...  if(something)  {    break;  }  ...}while(0);

 

 

which is really just a poorer form of writing

 

...if(something){  goto end;}...end:

 

Both of the above snippets are examples of very poor programming practice.

 

Programmers with poor understanding of language syntax sometimes use do while(0) to get a brace-enclosed statement. I guss that's why MISRA mentioned it... there's too much sloppy code out there already using this syntax. However, do while(0) is obfuscation. Instead, write two braces without the do while(0) part and it is completely equivalent.

 

For function-like macros you'd write:

 

#define MACRO_NAME() \ { \ \ /* code here */}

 

and that code is compliant with MISRA 19.4, since 19.4 only mentions do-while as the only acceptable statement. The above isn't a statement, it is an empty block. And it will not result in a pointless branch if zero instruction.

 

Though of course, function-like macros are also very poor programming practice, and completely useless for anything but very performance-critical functions that must be inlined. When using Codewarrior, it is much better practice to use the #pragma INLINE instead of function-like macros.

0 Kudos

2,916 Views
CompilerGuru
NXP Employee
NXP Employee

I personally try to use a few macros as possible, but sometimes they are the simplest way to keep the code simple, and then I do use them. I don't think language features, including function style macros are bad on their own, they all have a purpose in some cases.

 

Anyway, when using macros, then the do {X} while(0) in macro is a common pattern to keep the macro behavior identical to a simple statement.

So that code (I always write braces with if's...) like

if (cond)

  MACRO;

else

   ....;

 

compiles as expected.

 

For this to work, macros like MACRO are often defined with a do {xxx;} while(0) pattern.

I did never see such a macro to contain a break, the do {} while (0) pattern is only to embedd the actual content of the macro so it works as a whole like a statement without the semicolon, even when used within if, while, else, for,... constructs.

 

For the case of the HC08 compiler, I fear either the warning has to be globally disabled or the do {} while (0) pattern must not be used.

BTW: I doubt that there is any C compiler which does generate code for a do {} while(0).

 

Daniel

0 Kudos

2,916 Views
Lundin
Senior Contributor IV

> I personally try to use a few macros as possible, but sometimes they are the simplest way to keep the code simple, and then I do use them. I don't think language features, including function style macros are bad on their own, they all have a purpose in some cases.

 

Would you care to give an example? I don't know of any such case beyond inlining performance-critical functions.

 

I also strongly disagree with the statement that all language features have a purpose, there are so much redundant functionality in the C language (bit fields, function-like macros, the ?: operator, goto/continue statements, "fall through" in case statements and so on). That's why we need standards like MISRA in the first place.

 

If someone thinks "the language allows this, so it must have a purpose", then they are oblivious to the fact that C was designed in ancient days when dinosaurs roamed the earth, on none had a clue about good and bad programming practice.

 

> For this to work, macros like MACRO are often defined with a do {xxx;} while(0) pattern.

 

But you don't need the "do" and you don't need the "while(0)", that's just obfuscation. Two braces will work perfectly.

 

 

0 Kudos

2,916 Views
kef
Specialist I

Two braces won't work:

 

 if(1)

     MACRO;

else

{}

 

 

  if(1)

     {

     };

  else

  {}

 

 

And do{} while(0) will work perfectly

 

  if(1)

     do{} while(0)

     ;

  else

  {}

 

0 Kudos

2,917 Views
Ironhorse
Contributor I

>>>"There's also zero chance you can achieve MISRA-compliance "

>>>"if you include CW libraries or use PE."

 

That is what I thought, but it is helpful have it in writing from an authoritative source.

 

For now I’m making use of the productivity and education, but will eventually have to wean off the CW libraries and PE.    I’m kicking that down the road, but I'll have to revisit the subject discussed under the thread "Linker Message - missing symbol on S12X port", but at least I have a clue now.

 

Right, I always use the braces in ifs anyway.

 

The overlap of Rule 14.9 and Rule 19.4 may just be a case of belt and {}.*

 

Compliance with Rule 14.9 makes me compliant with the intent of Rule 19.4 even if I omit “do” and “while(0)?  I would rather have Rule 19.4 set up to make the compiler bomb if I ever break Rule 14.9.

 

*“How can you trust a man who [uses] both [do-while-zero] and [braces]? The man can't even trust his own [code].” (Henry Fonda, Once Upon a Time in the West)

0 Kudos

2,917 Views
Lundin
Senior Contributor IV

> Compliance with Rule 14.9 makes me compliant with the intent of Rule 19.4 even if I omit “do” and “while(0)? I would rather have Rule 19.4 set up to make the compiler bomb if I ever break Rule 14.9.

 

Exactly. This appears to be an inconsistent flaw in the MISRA standard. I have notified the committee about this.

 

0 Kudos

2,917 Views
Ironhorse
Contributor I

To further the discussion a little bit, I mentioned interest in placing non-executable statements in macros.

This trivial example (that demonstrates my ignorance) compiles:

 

#define DEF \   u8 a;    \   u8 b;          DEF; 

 

But this does not:

 

#define DEF \   do {     \   u8 a;    \   u8 b;    \   } while (0)   DEF;

 

Leaving aside whether the trivial example has any virtue (or suggestions that I use struct or X-macros), is there included in Rule 18.4 any intent to forbid declaration statements in macros?

Or is that just a known bad practice?

 

 

0 Kudos

2,917 Views
Lundin
Senior Contributor IV

It doesn't really make sense to call macros from a file scope location, just as it doesn't make sense to place any other code there. The compiler error has nothing to do with the declarations inside the macro. What exactly are you trying to achieve with it?

 

Macros in general are known bad practice. In the implementation of MISRA used by my company, we enforce much stricter rules and bancode inside macros entierly, together with most of the pre-processor. The only allowed uses for the pre-processor in our policies are constant declarations, header guards and compiler switches for disabling of debug/test code.

 

You don't need code inside macros, unless you are working with the very rare case where you must be compliant with ISO C90 and at the same time must inline a function for performance purposes. In that very rare case, Codewarrior's #pragma INLINE works well, effectively eliminating the need for macros entirely.

0 Kudos

2,917 Views
Ironhorse
Contributor I

"header guards"

Yeah, I'd just been thinking about having my header guards prompt me wherever I happen to have multiple inclusions of a header. (or just use a good dependency mapping tool).  (no circular dependencies either).

0 Kudos

2,917 Views
Ironhorse
Contributor I

“What exactly are you trying to achieve with it?”

Parametric construction of fixed (constant) modular structures at build time.  Even though the content may be variable, the pointers to them are constant (as CW is kindly reminding me.)

 

I am in the process of refactoring layered C software I did years ago (been assembly programming in the mean time).  I did use parameterized macros for any repeated pattern of code and declarations (maintain >nothing< in more than one place).  I think I got a lot done with little code.  I was surprised how powerful agressive abstraction was for me for code changes and reuse.  I remember approaching big data structure changes thinking this will be a lot of work, but being surprised by “Wow, I’m done already!?” I could completely redo a module and nothing else would break!   I knew then functions would have been better than macros and generally did use functions except for highly called segments and I will make use of the inline pragma. 

 

If I have questions later about macros for parameterizing complex modular declarations, I get back to you after I’ve reconsidered. 

 

Thanks

0 Kudos

2,917 Views
Lundin
Senior Contributor IV

> Parametric construction of fixed (constant) modular structures at build time. Even though the content may be variable, the pointers to them are constant (as CW is kindly reminding me.)

I would use typedef struct instead. Not only will you get rid of the macros, you'll also get a data type that the compiler and static analyzer can check, warning about suspicious casts.

0 Kudos

2,917 Views
Ironhorse
Contributor I

I am using typedefs now for declarations, for most of the later reasons you site, and a typedef would work for a single-instance initialization/definition, but I have been using the macros for build-time definitions of multiple instances of the typedefs.  

 

Suppose I have multiple functions operating on a struct n passed by reference &n.  It is likely that more than one of those functions may have common statements (executable and not (e.g., local definitions)).   Thus, I would like to abstract and maintain those common statements in one location rather than verbosely repeat the code in all locations.   It may seem odd, but I get great reductions in code (for me) and improved maintainability for my applications.

 

I note that just wrapping the declaration in braces presents no problem yet for compiling within function scope (unlike my egregious global scope example of 2010-11-18). (function scope is probably my intent, I was just stupid)

 

Yes, for the executable code I could use inlined functions, but for the common function scope declarations and definitions, I do not at this time see a way other than macros or X-macros.

0 Kudos

2,917 Views
Lundin
Senior Contributor IV

> Suppose I have multiple functions operating on a struct n passed by reference &n. It is likely that more than one of those functions may have common statements (executable and not (e.g., local definitions)). Thus, I would like to abstract and maintain those common statements in one location rather than verbosely repeat the code in all locations. It may seem odd, but I get great reductions in code (for me) and improved maintainability for my applications.

 

I think the compiler should be able to do that optimization for you.If it doesn't, then #pramga inline. But is your program really that time-critical? I work with real-time applications all the time, and even then I encounter such rare real-time demands in perhaps 1 project out of 50.

 

Macros never make the code more maintainable, as they make the code much harder to read, compile and debug. 

 

And seriously... have you actually managed to fill the whole flash the S12? Are program size optimizations really so important that you must sacrifice readability/maintainability? I believe that would be even more rare than the inline scenario.

 

Since you are using MISRA, a safe, bug-free program is likely the top priority. Macros don't fit in with that at all.

Out of curiousity, what's the end application for this code?

 

0 Kudos

2,917 Views
Ironhorse
Contributor I

***“Macros never make the code more maintainable, as they make the code much harder to read, compile and debug.”

 

I’m not wanting to disagree with you because I have really appreciated your advice.  But I also look to advice that suggests that, for maintainability, I use macros to avoid replicating literals.  AUTOSAR seems to suggest that macros be used to separate shear layers for portability.  (A tradeoff between readability and portability?)  Some of those involve complete statements (Specification of Compiler Abstraction, Section 9.2- 9.4).

 

Does information hiding worsen or improve readability or maintainability?  I think it is possible to have too much or too little, depending.  I find that each stakeholder wants ALL information hidden EXCEPT his.

 

So, you would advise that the conventional

#define setReg8Bits(RegName, SetMask)      (RegName |= (byte)(SetMask))

be a function rather than a macro? 

Mind you, doing such operations as functions rather than macros is more in line with my thoughts.  Remember, my interest in macros is not necessarily for executable statements.

 

Most of my information hiding is done in structs; THAT is what has made the code more readable and easier to change for me.  Most of my use of macros has been in definitions of those structs.  But all of those macros are constant definitions, so they may be in line with your policies stated earlier.  But, I have been known to make such definitions in contiguous multi-line statements and prefer macros to cut and paste, thus invoking Rule 19.4.

 

Consider that I may have a constant struct consisting mainly of pointers to struts.  To define the struct I must also define the referenced structs; so, the definition is inherently multi-statement.  I have to decide which is least unreadable: being too verbose (literals) or too terse (macros).  I have a bad head for lots of text but a good head for abstraction, so I may be unduly biased towards the terse.

 

 

***“have you actually managed to fill the whole flash the S12”

 

I am not concerned with filling the S12, in fact I look at the 512k Flash as “too much rope” to hang myself with.  (We only went to the S12 for the >1 CAN).  In general, it is the source LOC that drives my expenses due to code verification costs.   Also, each object code instruction must also be ultimately two-way traceable to specification and to test data.  If I inline a function, I may be called upon to demonstrate structural test coverage of each inline copy (things haven’t gone that far yet, but may); even so, I try to write things to make test coverage as trivial as possible.   [Thinking about it, inlining might lead to more thorough testing if each instance must be exercised by the caller to achieve, say, decision, coverage.]

 

 

***“Out of curiousity, what's the end application for this code?”

Controllers.  In the end, I find that I have executable source code that looks like SCADE output (quite without intention), but in applications way too small to warrant that tool.  (and code generators can’t take engineer phone calls, yet)

0 Kudos

2,917 Views
Lundin
Senior Contributor IV

> I use macros to avoid replicating literals

 

How? Macros will not reduce literals, the compiler will just do a "text replacement" when finding such macros, and the result would be the same as if you typed them out directly in the code. Though of course, some constant must be declared inside #define macros, such as array sizes.

 

The proper solution to reduce literal memory size is otherwise to declare all literals as file scope constants:

 

static const uint8 SOME_VAL = 123;

 

If the above is placed at file scope, it will typically be allocated in dedicated ROM and not in program memory. And with compilers like CW you can #pragma it to a desired memory location. This will not only reduce program size but also ease debugging.

 

> AUTOSAR seems to suggest that macros be used to separate shear layers for portability

 

I don't know AUTOSAR, but there's no coincidence that  chapter 19 is the longest one in the whole MISRA. The whole pre-processor is poorly defined by the C standard.

 

> Does information hiding worsen or improve readability or maintainability? I think it is possible to have too much or too little, depending. I find that each stakeholder wants ALL information hidden EXCEPT his.

 

Information hiding is good, but you don't need macros for it.

 

 

> So, you would advise that the conventional

> #define setReg8Bits(RegName, SetMask) (RegName |= (byte)(SetMask))
> be a function rather than a macro?

 

Yes, because functions have stronger typing. You can pass anything to that macro: large ints, pointers, arrays, floats... And if you get any compiler warnings/error, they will likely be of a very bemusing nature. The typecast inside that macro only makes things worse, as it will hide away most such type casting erros.

 

> Most of my information hiding is done in structs; THAT is what has made the code more readable and easier to change for me. Most of my use of macros has been in definitions of those structs.

 

This is likely acceptable, what you should avoid is executable code inside macros. One example of common practice of such macros is to reduce the amount of typing for nested structs. Example:

 

typedef struct { int val; } X;

typedef struct {  X x; } Y;

typedef struct { Y y; } Z;

 

Z Z;

Z.y.x.val = something;

 

Instead of nested struct notation, the code is often clearer if you write:

 

#define z_val (z.y.x.val)

...

z_val =  something;

 

The above method is common code in register and data protocol declarations, and I would agree that this particular example increases the readability of the code. Just note the need for parenthesis, the "dot" operator has lower presedence than [] and -> operators  (and you need parenthesis for MISRA).

 

 

>  Also, each object code instruction must also be ultimately two-way traceable to specification and to test data.

 

Macros and inline functions aren't any different in this case, they will both end up in program memory. If you need to trace the actual object code, I'd say ordinary functions is a must, since you can #pragma allocate them at a known memory location.

0 Kudos

2,917 Views
Ironhorse
Contributor I

>>>"I note that just wrapping the declaration in braces presents

>>>  no problem yet for compiling within function scope "

 

But then the braces place the declarations in a useless block scope...

0 Kudos

2,917 Views
Lundin
Senior Contributor IV

Yes that's the original purpose, but it is still obfuscation. The main problem in that code isn't the macro, it is the lack of braces in the if-statement. You shouldn't go write obfuscated macros to allow poor-style if-statements.

 

Omitting braces from if-else is banned by MISRA C rule 14.9. They even thought it was so important that they made it a mandatory rule, despite their efforts to otherwise stay out of coding style, i.e. they likely have overwhelming proof that omitting braces leads to severe bugs. And I think any somewhat experienced C programmer will agree with that.

 

0 Kudos

2,917 Views
Ironhorse
Contributor I

Thank you for the advice. Neither the break nor goto examples are code I have ever conceived – don’t know what that says about me. The do-while-zero is new to me and I perhaps naively accepted that MISRA had a reason for it beyond the brace enclosure. Application of do-while-zero Rule 19.4 seems more general than just function-like macros. "Function-like" was not mentioned in Rule 19.4, but maybe it is implied.

 

All I am looking for is text substitution for separation of concerns or shear layers, some times that is for multi-line code and sometimes that is non-executable code (i.e., not function-like macros). (perhaps bad practice?) For the executable code, I’ve realized that I can do that with functions and can make use of inlining.

 

>>>Though of course, function-like macros are

>>>also very poor programming practice,

I noted some function-like macros in the CodeWarrior library and PE Generated code that I will eventually have to deal with. But then, isn't a possible purpose there to make the source code complier agnostic? (or just legacy)  But then, even the inline pragmas can be made compiler agnostic (Ref. AUTOSAR Specification of Compiler Abstraction, item 9.1.4).

0 Kudos

2,916 Views
Lundin
Senior Contributor IV

I've seen some do-while(0) constructs in the past, and the reason was either braces for function-like macros, or "goto wrappers". Either is muddy thinking. The biggest flaw in MISRA, imo, is that it allows a lot of pre-processor madness that simply should have been banned.

 

There's also zero chance you can achieve MISRA-compliance if you include CW libraries or use PE.

 

0 Kudos