9

According to the C FAQ, there are basically 3 practical methods for "inlining" code in C:

#define MACRO(arg1, arg2) do { \
    /* declarations */ \
    stmt1;   \
    stmt2;   \
    /* ... */  \
    } while(0)    /* (no trailing ; ) */

or

#define FUNC(arg1, arg2) (expr1, expr2, expr3)

To clarify this one, the arguments are used in the expressions, and the comma operator returns the value of the last expression.

or

using the inline declaration which is supported as an extension to gcc and in the c99 standard.

The do { ... } while (0) method is widely used in the Linux kernel, but I haven't encountered the other two methods very often if at all.

I'm referring specifically to multi-statement "functions", not single statement ones like MAX or MIN.

What are the pros and cons of each method, and why would you choose one over the other in various situations?

Robert S. Barnes
  • 39,711
  • 30
  • 131
  • 179
  • The middle example doesn't make a lot of sense, the macro takes arguments named arg1 and arg2, and in the macro body you use expr1, expr2 and expr3? – unwind Oct 15 '09 at 10:06
  • 1
    It makes sense, because the `argN` ones could be nested in sub-expressions on the right side. Like `(arg1 ^= arg2, arg2 ^= arg1, arg1 ^= arg2)`, i think. – Johannes Schaub - litb Oct 15 '09 at 10:10
  • 3
    Unless you've measured your code and know you need to optimize, don't bother: write normal functions for everything. – pmg Oct 15 '09 at 10:12
  • @litb: Aha, I guess that makes sense. Thanks. – unwind Oct 15 '09 at 10:12
  • 1
    @unwind, the second one will use the macro arguments in the expressions. The first one creates a block that allows the MACRO to be on a line ending with a semicolon. The second one can be used in place of a value (expression 3 returns the value used.) The 3rd one uses an extention to a compiler. I would choose the 1st one in most cases as the second one is more restricted in its use. The 3rd one I would not use because moving to another OS / compiler / etc. could mean loss of that particular extension. – rsp Oct 15 '09 at 10:14
  • @litb: Will that kind of swap actually work portably? I know the C FAQ says the single statement version is problematic. See: http://c-faq.com/expr/xorswapexpr.html – Robert S. Barnes Oct 15 '09 at 14:10
  • @pmg: Obviously, but this is a question about what to do once I've decided that "inlining" is needed, not a question about evaluating whether or not "inlining" is needed. – Robert S. Barnes Oct 15 '09 at 14:13
  • Possible duplicate of [Inline functions vs Preprocessor macros](https://stackoverflow.com/questions/1137575/inline-functions-vs-preprocessor-macros) – Jim Fell Aug 09 '17 at 18:17

3 Answers3

16

Talking about that specific use of macros, i.e. macros that act as "functions", I'd mention the following advantages of macros that can't be had in an inline function:

Lazy argument evaluation. For example, a macro like this

#define SELECT(f, a, b) ((f) ? (a) : (b))

would preserve the lazy argument evaluation properties of the ternary operator: only the selected parameter is evaluated, while the other is not. A straightforward inline-function analogue would evaluate both arguments in advance, thus doing the extra unnecessary work.

Access to context. Macros can be used to implement some resemblance of "local functions", i.e. repetitive pieces of code that have access to the local variable and parameters of the enclosing function.

Type independence (and type parameters). Macros allow you to write type-independent "functions" (see above example). And in case you can't get rid of type-dependence, you can pass types as parameters to the macro.

The above properties of the macros, which I presented as their pros, could be misused to lead to major failures (and thus could be presented as cons as well). But that's something that can be said of many language features in C.

Makoto
  • 104,088
  • 27
  • 192
  • 230
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 1
    In an example like #define MAX(A, B) ((A)>(B)?(A):(B)) if A or B includes an action (like i++), it is even worst because it will be evaluated twice. – eyalm Oct 15 '09 at 12:50
  • @AndreyT: Perhaps you could spell out some of those cons? – Robert S. Barnes Oct 15 '09 at 13:58
  • @eyalm: Actually, I think the behavior is undefined when a variable is modified more than once in a single expression, unless you use a short circuit operator ( &&, ||, ',' ) to introduce a sequence point. By the way, the multiple eval problem applies to any statement, including a function call I believe, i.e. foo(i++, i++) will also give you undefined behavior since you don't know what order the function arguements will be evaluated in. – Robert S. Barnes Oct 15 '09 at 14:00
  • @AndreyT What I'm interested in is how can I pass types as parameter to the macro ? I'm not familiar with such a feature in C99? – Lazureus Nov 03 '13 at 20:19
5

One pro of using the inline keyword is that you get a check of the types of the arguments via the function prototype. Using macros you get nothing like that, so macros are liable to create strange errors if you put things of the wrong type in them. (Not nearly as horrible as template errors in C++ though.)

One pro of using macros is that you can do funky things like concatenations and turning the macro argument into a string using #arg. Another plus of using preprocessor macros is you can easily check how they expand using cpp to unwind them. This is how you debug those errors.

Another useful point of macro-defined functions is that you can stick a return statement into them to halt the parent function if you need to. With an inline function you have to return a value and then check the return value.

0

The only pro I can see in using any of the constructs is for making it faster code.

So, choose the one method that gives fastest code!

If it's all the same, then I find it more legible

  1. a 'standard' function
  2. a inline function
  3. the #define ... do {} while(0) approach
  4. the macro with comma separated expressions
Community
  • 1
  • 1
pmg
  • 106,608
  • 13
  • 126
  • 198