2

Can anyone help me to understand the meaning of this line?

I know it's kind of macro structure, but what does , suggest in the code??

#define ReturnErr(fCall) if (iErr = (fCall), (iErr != NO_ERRORS)) {return iErr;}
Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
Karan Shah
  • 1,912
  • 1
  • 29
  • 42
  • 2
    C or C++? Pick one. They are two separate languages. Which one are you actually using? – Lightness Races in Orbit Apr 04 '14 at 11:20
  • 6
    @Lightness Races in Orbit: I believe the macro has a valid syntax in C and C++. Does it have different semantics? If it's both syntactically correct and semantically equivalent in both languages, is it not appropriate to apply both tags? – Peter - Reinstate Monica Apr 04 '14 at 11:23
  • 1
    @PeterSchneider while it's true in this case, as a question asking pattern one should be more specific. Developing that pattern of specificity avoids future problems. – mah Apr 04 '14 at 11:24
  • 1
    @mah I can't follow. If the question is relevant to n languages it should carry all n tags, because it's interesting to all n audiences. – Peter - Reinstate Monica Apr 04 '14 at 11:30
  • 2
    @PeterSchneider: Such a consideration might be part of an answer. It should not be part of the question, because then the OP is asserting something that he doesn't know. If nothing else, a good answer to this question if it's a C++ question is "don't do this", whereas in C there are fewer alternatives. A Stack Overflow question that is tagged with more than one programming language is _too broad_. – Lightness Races in Orbit Apr 04 '14 at 11:31
  • @Lightness Races in Orbit: I can't follow. You keep half the audience from answering to begin with if they don't have the other tag in their tag preferences. They don't even see the question which is of undisputed relevance to them. – Peter - Reinstate Monica Apr 04 '14 at 11:33
  • @LightnessRacesinOrbit On the other hand the OP is not asking if he should or should not do this. He is asking what does it do. Not the same thing imo. – rozina Apr 04 '14 at 11:33
  • @rozina: A full answer explains what it does, then explains why people use it to do that. It ends by presenting generally-accepted opinion on whether the OP should "join the party" and begin doing it also, or whether he should find a different approach. – Lightness Races in Orbit Apr 04 '14 at 11:39
  • @rozina True. But even the answer "don't do that in C++ because C++ offers feature x which makes this typical C macro obsolete" is relevant not only to C++ but to both languages because it compares both languages. A C programmer may learn something ("try C++!"), as well as the C++ programmer, obviously ("use feature x!"). If that macro were valid C# (which it is not), the C# tag should be added as well. – Peter - Reinstate Monica Apr 04 '14 at 11:41
  • @LightnessRacesinOrbit: Sure. And you can do all that. If you are not sure about other languages, you can limit your answer to just the language you are proficient in. The example in question can be used in both C and C++ so both tags make sense. No reason to limit it to 1 language and then even repeat the same question once for C and once for C++. – rozina Apr 04 '14 at 11:41
  • I'll say it again: a question about more than one programming language is _too broad_. – Lightness Races in Orbit Apr 04 '14 at 11:42
  • This macro is really bad design, because it buries an unterminated control structure inside a pseudo function call, and because it depends on a variable. Better would be something like `do { int iErr = (fCall); if (iErr != NO_ERRORS) return iErr; } while (0)` – Jens Gustedt Apr 04 '14 at 11:54
  • @JensGustedt Or instead of using obscure macro tricks, always use {} after each control statement, which you should be doing anyhow. – Lundin Apr 04 '14 at 12:37
  • @Lundin, he does that already, this is not sufficient here. He needs something to prevent the dangling `else` problem. And this is not "an obscure macro trick", this is how such things are done in C macro programming. Here it also helps to resolve the dependency of a variable that has to be declared previously, and avoids the bogus use of the comma operator. – Jens Gustedt Apr 04 '14 at 12:59
  • @Lundin: It's not obscure, it's canonical (i.e. the universally acknowledged one and only way) best practice. Curly braces don't cut the mustard completely because of `if(cond) ReturnErr(fCall); else somethingElse();`. Yes, you can fix that with curly braces too, but _that's_ obscure (camouflaging an extraneous empty statement).-- Btw, in principle I agree with your curly braces rule (cf. Apple's SSL disaster). – Peter - Reinstate Monica Apr 04 '14 at 13:04
  • @JensGustedt do-while(0) is _only_ used to prevent bugs of the kind `if(x) my_macro();` where the proper code would have been `if(x) { my_macro(); }`. I agree that the dependence on a previously allocated variable is really ugly, but as for hiding a local variable inside the macro, a simple {} would suffice, assuming that the caller's code is a proper `if(x) { my_macro(); }`. – Lundin Apr 04 '14 at 13:43
  • @JensGustedt I know about the "trailing else" reasoning but I'm against writing ad hoc fixes instead of solving the core problem, which can be solved by for example setting your static analyser to give an error whenever it encounters a control statement without a {} body. – Lundin Apr 04 '14 at 13:44
  • @JensGustedt For the record, [I made the very same remark](http://www.misra.org.uk/forum/viewtopic.php?f=72&t=1096) about MISRA-C:2004, which shared your view of do-while(0) as canonical. This is one of the few cases where the committee actually listened to me, the recommendation of do-while(0) was removed in MISRA-C:2012, since MISRA-C already enforces the mandatory {} after all control/loop statements. – Lundin Apr 04 '14 at 14:16
  • @Lundin: Interesting MISRA discussion. Respect for involving and making a difference. I see the point for MISRA, but outside its scope I still like the do..while(). Perhaps it's just bad taste ;-). – Peter - Reinstate Monica Apr 04 '14 at 16:17
  • And then: What is the motivation for this macro, and what are possible alternatives? I think Lundin's use case is correct but the motivation for this macro is surely _avoiding repetition._ A common programming problem is how to abort cleanly from the middle of a chain of successively dependent processing steps. There is a classical thread here: http://stackoverflow.com/questions/46586/goto-still-considered-harmful. Whether we have multiple returns or gotos or pseudo-breaks, wrapping it in a macro improves readability dramatically. It's not easy to achieve that alternatively. – Peter - Reinstate Monica Apr 04 '14 at 17:26

5 Answers5

5

A qualified guess is that the macro is meant to be used like this:

err_t func (void)
{
  err_t iErr;

  ReturnErr(some_function());
  ...

  return NO_ERRORS;
}

In that case the macro expands to:

err_t func (void)
{
  err_t iErr;

  if(iErr = some_function(), iErr != NO_ERRORS) { return iErr; }
  ...

  return NO_ERRORS;
}

which in turn is just a needlessly obfuscated way of writing

err_t func (void)
{
  err_t iErr;

  iErr = some_function();
  if(iErr != NO_ERRORS)
  { 
    return iErr; 
  }
  ...

  return NO_ERRORS;
}

In other words, the macro is likely an attempt from repeating the same error handling code over and over.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • if(iErr = some_function(), iErr != NO_ERRORS)-> is this undefined behaviour because iErr is not initialised to a default value and when iErr = some_function() is evaluated,though the result may be NO_ERRORS itl be discarded and iErr != NO_ERRORS will be checked. This may or may not be true based on the garbage value of iErr right? – AlphaGoku May 05 '16 at 10:40
  • @Vector9 No, it is ugly code but perfectly safe. The order of evaluation of the comma operator is guaranteed left to right. There is a sequence point between the evaluation of 1st and 2nd operator. – Lundin May 05 '16 at 19:47
  • But wont the result of iErr = some_function() be discarded before iErr != NO_ERRORS is processed? – AlphaGoku May 06 '16 at 04:47
  • @Vector9 No it won't. – Lundin May 06 '16 at 19:17
2

Macros are a text substitution. It means that if someone writes, for example,

ReturnErr(x)

then their code will be processed as:

if ( iErr = (x), (iErr != NO_ERRORS) )
{
    return iErr;
}

This is bad style, but they probably want to have their function return when a failure occurs and save some typing over copying out that code at each point they need to check an error code.

M.M
  • 138,810
  • 21
  • 208
  • 365
2

The macro takes a single argument named fCAll. The macro expands to the following code:

if (iErr = (fCall), (iErr != NO_ERRORS)) {
   return iErr;
}

I guess you are confused by the usage of the , operator in the if statement.

In the C and C++ programming languages, the comma operator (represented by the token ,) is a binary operator that evaluates its first operand and discards the result, and then evaluates the second operand and returns this value (and type).

This is quote from the wikipedia article btw.

Thus the statement in the body will be executed if and only if iErr != NO_ERRORS i.e. there are errors.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
1

The macro wants to use the value iErr twice, once in the if and once in the return, but it wants to execut fCall only once. It uses the comma which evaluates both its operands but is equal only to the right-most.

Thus if we expand by hand and do a little refactoring, we get:

iErr = (... macro argument here ...);
if((iErr != NO_ERRORS)) {
    return iErr;
}
Adrian Ratnapala
  • 5,485
  • 2
  • 29
  • 39
0

Lundin and others have explained what the author of the macro intended. However, the way it is written it can have unintended consequences too, because it doesn't follow the canonical way of writing multi-statement macros, which is: wrap it in a do..while(0) loop.

Why? Consider:

if( someCondition)  ReturnErr(fCall); else doSomethingElse();

This fails syntactically because the programmer's semicolon behind the expanded macro's curly brace is an empty statement, making the else dangling.

The programmer may choose to remove the superfluous semicolon in order to mollify the compiler, writing

if( someCondition)  ReturnErr(fCall) else doSomethingElse();

This is probably not what s/he intended, because the else part is now silently attached to the macro's if. That's pretty bad because it's totally invisible.

The canonical way is:

#define ReturnErr(fCall) do { if (iErr = (fCall), (iErr != NO_ERRORS)) \
                              return iErr; \
                            } while(0)

This allows, even requires the syntactically natural semicolon at the end and counts as a single statement whenever one is needed.

@Jens: :-)

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
  • It is indeed bad practice for a lot of reasons. However, this doesn't answer the question. – Lundin Apr 04 '14 at 12:35
  • @Lundin: You are right, an "answer" should probably _answer_ the OP's question. If you think it's not worth keeping I can delete it. I think though it's important to spread the word, and this mistake is made often. What can be said in a comment like Jens' is perhaps not enough for somebody struggling to understand macros as such. – Peter - Reinstate Monica Apr 04 '14 at 12:55
  • @PeterSchneider, I was actually thinking that I should put that in an answer to have more place. But then why not using my solution directly (without comma operator) ? – Jens Gustedt Apr 04 '14 at 13:23
  • @Jens Gustedt: Yes, I think your solution is preferred. I didn't want to distract from the point which was important to me. Btw, the macro "caller" may depend on iErr being set by the fCall in other scenarios. A new local variable works here only because any value apart from NO_ERRORS aborts anyway (and the return stack doesn't care where its values came from). – Peter - Reinstate Monica Apr 04 '14 at 13:33
  • I personally don't think it is worth keeping, for the reasons stated in the comments to the OP. However, my personal opinion is irrelevant, [SO policy](http://stackoverflow.com/help/deleted-answers) is that answers should only answer the question. If you want to enlighten others of the benefit of do-while(0), you should do it in a separate post, for example as a "Q-A style" question [where you answer your own question](http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/). However, I'm pretty sure there's already a post like that somewhere... – Lundin Apr 04 '14 at 13:49
  • Why not simply scope the block in which the code is contained instead of using a do-while(0). – Mo Beigi Apr 04 '14 at 15:45