Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explanation of C++ FAQ's unsafe macro?

According to the C++ FAQ, macros are evil:

[9.5] Why should I use inline functions instead of plain old #define macros?

Because #define macros are evil in 4 different ways: evil#1, evil#2, evil#3, and evil#4. Sometimes you should use them anyway, but they're still evil. Unlike #define macros, inline functions avoid infamous macro errors since inline functions always evaluate every argument exactly once. In other words, invoking an inline function is semantically just like invoking a regular function, only faster:

// A macro that returns the absolute value of i #define unsafe(i)  \         ( (i) >= 0 ? (i) : -(i) )  // An inline function that returns the absolute value of i inline int safe(int i) {   return i >= 0 ? i : -i; }  int f();  void userCode(int x) {   int ans;    ans = unsafe(x++);   // Error! x is incremented twice   ans = unsafe(f());   // Danger! f() is called twice    ans = safe(x++);     // Correct! x is incremented once   ans = safe(f());     // Correct! f() is called once } 

Also unlike macros, argument types are checked, and necessary conversions are performed correctly.

Macros are bad for your health; don't use them unless you have to.

Can someone explain why is unsafe(x++) increments x twice? I am not able to figure out.

like image 402
Gaurav K Avatar asked Apr 26 '13 19:04

Gaurav K


People also ask

Should you avoid macros in C++?

"Macros are unsafe, have no type checking and should be avoided whenever possible. An alternative to macro is inline function for some use cases." He is right that a macro doesn't respect a namespace. That's just a caveat of using one.

How do C macros work?

Overview. Macro in C programming is known as the piece of code defined with the help of the #define directive. Macros in C are very useful at multiple places to replace the piece of code with a single value of the macro. Macros have multiple types and there are some predefined macros as well.

What is Argumented macro in C?

Function-like macros can take arguments, just like true functions. To define a macro that uses arguments, you insert parameters between the pair of parentheses in the macro definition that make the macro function-like. The parameters must be valid C identifiers, separated by commas and optionally whitespace.

What do you understand by macros in C Explain with examples?

A macro is a piece of code in a program that is replaced by the value of the macro. Macro is defined by #define directive. Whenever a macro name is encountered by the compiler, it replaces the name with the definition of the macro. Macro definitions need not be terminated by a semi-colon(;).


2 Answers

Running it through the preprocessor shows the problem. Using gcc -E (can also use cpp -P, where the -P option also suppresses generated # lines),

inline int safe(int i) {   return i >= 0 ? i : -i; }  int f();  void userCode(int x) {   int ans;    //    increment 1      increment 2 (one of these)   //        |             |     |   //        V             V     V   ans = ( (x++) >= 0 ? (x++) : -(x++) );   ans = ( (f()) >= 0 ? (f()) : -(f()) );    ans = safe(x++);   ans = safe(f()); } 

As artless noise notes, the function f() is also called twice by the unsafe macro. Perhaps it's pure (has no side-effects) so it's not wrong, per se. But still suboptimal.

So, since inline functions are generally safer than function-like macros because they work on the same semantic level with the other basic elements: variables and expressions; and for manifest constants, enums can often be more tidy; what are the good uses of macros?

Setting constants known only at compile-time. You can define a macro from the command-line when compiling. Instead of

#define X 12 

in the source file, you can add

-DX=12 

to the cc command. You can also #undef X from the command-line with -UX.

This allows things like conditional-compilation, eg.

#if X    do this; #else    do that; #endif    while (loop); 

to be controlled by a makefile, itself perhaps generated with a configure script.

X-Macros. The most compelling use for X-Macros, IMO, is associating enum identifiers with printable strings. While it make look funny at first, it reduces duplication and synchronization issues with these kinds of parallel definitions.

#define NAMES(_) _(Alice) _(Bob) _(Caravaggio) _(DuncanIdaho) #define BARE(_) _ , #define STRG(_) #_ , enum { NAMES(BARE) }; char *names[] = { NAMES(STRG) }; 

Notice that you can pass a macro's name as an argument to another macro and then call the passed macro by using the argument as if it were itself a macro (because it is one). For more on X-Macros, see this question.

like image 168
luser droog Avatar answered Oct 03 '22 01:10

luser droog


Macros effectively do a copy/paste before the program is compiled.

unsafe(x++) 

Would become

( (x++) >= 0 ? (x++) : -(x++) ) 
like image 42
Drew Dormann Avatar answered Oct 03 '22 02:10

Drew Dormann