views:

550

answers:

9

Duplicate:

Good Programming Practices for Macro Definitions (#define) in C

C (and by extension C++) macros are laden with pitfalls and practical problems when not implemented properly.

Take, for example, the macro

#define cube(x) x*x*x
  • What is wrong with it?
  • Where will it fail?
  • When can unexpected results be returned?
  • What is the proper way to implement it, and why?

What other macro pitfalls should one avoid that may not be exemplified in the above?

Please don't just give the rule, but give an example of why the rule exists (ie, show what the failure is when the rule isn't followed).

+1  A: 

See This Question and This One.

dsm
+4  A: 

Oh ... So many bullets.

It doesn't wrap the argument on the right hand side in parenthesis, failing to protect them from the surrounding source code text. This leads to breakage when evaluation priroties conflict, such as cube(1+2).

It will in many cases evaluate its argument three times, which can fail if evaluating the argument has side effects. Consider cube(x++) for the classic case of that.

The proper way would be to implement it as a function, but I guess that's cheating ... It could be a call to pow(), perhaps with casting if the argument is assumed to have any particular type.

unwind
So the result would be #define cube(x) (x)*(x)*(x), which still fails the cube(x++) case.
Adam Davis
You can get rid of the problem with order of operations by defining cube(x) as (x)*(x)*(x), but this still leaves the problem with multiple evaluation.
Tyler McHenry
+4  A: 

The input and output needs to be isolated to ensure that unexpected expansions do not occur:

Example bad use:

cube(1+2)

results in

1+2*1+2*1+2

should be 27, actually gives 7

Another example for output:

#define double(x) x+x
a = double(5)*double(2);

results in

a = 5+5*2+2

should be 14, actually gives 17

In general this means the inputs and overall output should be bracketed like so:

#define cube(x)  ((x)*(x)*(x))

I hope this helps you understand macros a bit better and why some people avoid them.

edit: As someone else mentioned, if 'x' is complex and has a side effect the results can be somewhat undefined, however there is little you can do about that and you should be careful to avoid such use

DrHazzard
+2  A: 

Multiplication is low down on the operator precedence stack.

So if the code was such:

 class strangething
 {
     protected int priv;
     public int pub;
     public strangething operator*(strangething& x)
     {
      strangething ret;
      ret.priv = x.pub + x.priv;
      return ret;
     }
 }

 strangething a;
 ...

 cout<<CUBE(a)->pub;

This gets resolved as

 cout<<x*x*x->pub;

or

 cout<<x * x * (x->pub);

Which is not what you intended

Tom Ritter
+2  A: 

I recommend looking at what the comp.lang.c FAQ has to say about this subject.

Kristo
+1  A: 

Say you write 'cube(x + y)'. This expands to 'x + y * x + y * x + y', which certainly is different from what you want (i.e. '(x + y) * (x + y) * (x + y)').

The proper way to write this is:

#define cube(x) (x)*(x)*(x)

Or to stay on the safe side, even though in plain C operator precedence outside the macro call isn't in question here:

#define cube(x) ((x)*(x)*(x))

EDIT: As some people already pointed out, there are more problems with using macros in this case. Consider implementing such cases as (static) inlines rather than macros. You also get type safety and better isolation this way.

Eduard - Gabriel Munteanu
+3  A: 

For all but the simplest token replacements and helper functions, macros can be problematic. If you must use macros, here's some things to keep in mind, as well as why you should do them.

Surround each term of a macro's expansion with parentheses

// Example violation:
#define TWICE(x) x * 2   // Wrong!

// Consequences:
TWICE(3 + 5)             // This is "3 + 5 * 2" = 13, not 16.

Surround the whole macro expansion with parentheses

// Example violation:
#define TWICE(x) (x) * 2   // Still wrong!

// Consequences:
10 / TWICE(5)              // This expression yields 4 ("10 / 5 * 2"), not 1.

Beware of macros that create multiple sequence points

// Example violation:
#define SQUARE(x) ((x) * (x))  // Fine on the surface, but some inputs could be bad...

// Consequences:
int a = 5;
SQUARE(++a);                   // Uh-oh! Undefined behavior.

Avoid conditional expressions in macros

// Example violation:
#define SAFE_DELETE(p) if(p) delete (p), ((p) = 0)  // All kinds of horrible.

// Consequences:
if (resourcesFinished)
    SAFE_DELETE(s);     // Kablammo! Not bracketed, so the "else" is useless here.
else                    // Also not a good idea for other reasons (what if p's not
    return 0;           //    a pointer?).

Avoid multiple statements in macros

#define SAFE_DELETE(p) delete (x); (x) = 0

char* s = new char [...];
...
SAFE_DELETE(s);                       // This works.

if (some_condition) SAFE_DELETE(s);   // Kablammo!
                                      // Second macro statement is always evaluated.
John Feminella
You can do multiple statements, but if you do they need to be braced
DrHazzard
Right, but the tendency to forget this sort of thing is why I wrote "avoid this", not "never do this". ;)
John Feminella
Plus you ought to resort to the "do { ... } while(0);" trick sometimes.
Eduard - Gabriel Munteanu
+1  A: 
  • defines is not scopable. you can't use defines in namesapce or in class or any scope.
    problems:
    names conflict or long names of your macro
    logical noncompliance with all other language

  • define is usual substitution - you will get lot of problems:

    #define max(a, b) a < b ? a : b
    ...
    max( a + p, a + k ); // same as: a + p < a + k

    or for your cobe define we got Undefined behavoir if will try use with ++x
    problems:
    very easy make errors and very hard to catch it
    define code with many semicolons is unreadable
    some problems like twice modification is not avoidable with defines

  • once I found function call in my current project ( ~700mb sources ) and couldn't found this function definition about two day - function name was generated with define # concatenation;
    problems:
    unreadable code

  • you couldn't got address such as in case with function, because define - is substitution;
    problems: not compatible with stl algorithms, binders
    not flexible
    logical noncompliance with all other language

bb