6

There is no shortage of examples of bad or dangerous function-like macros in C/C++.

#define SQUARE(x) x * x
printf("%d", SQUARE(4));   //16
printf("%d", SQUARE(3+1)); //7
#undef SQUARE

#define SQUARE(x) (x) * (x)
printf("%d", 36/4);                //9
printf("%d", SQUARE(6)/SQUARE(2)); //36
#undef SQUARE

#define SQUARE(x) ((x) * (x))
int x = 3;
++x;
printf("%d", SQUARE(x)); //16

int y = 3;
printf("%d", SQUARE(++y)); //?
#undef SQUARE

Given the problems with function-like macros, what examples are there of good/prudent/recommended uses for them?

Are there any times when a function-like macro would be preferrable to a function?

I imagine there must be some really good cases of this, or else the preprocessor makers would have made their job easier and left them out.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Paul Draper
  • 78,542
  • 46
  • 206
  • 285
  • 4
    The only one I have off the top of my head is one that inserts a combination of `__FILE__`, `__LINE__`, and `__func__` for you. If you offer that functionality in some logging class, I find it very tedious to type that into every call. – chris Sep 09 '12 at 18:17
  • @mfontanini, Indeed. There's no way to get a string of the literal assertion otherwise. – chris Sep 09 '12 at 18:19
  • 1
    The question is whether `assert` is "function-like" -- it doesn't guarantee to evaluate its argument even once. – Fred Foo Sep 09 '12 at 18:21
  • 1
    @larsmans: It's "function-like" in the sense defined by the preprocessor. The preprocessor distinguishes between macros that take parameters from those that don't. – Adrian McCarthy Sep 09 '12 at 18:25
  • C95 did not have `inline`, and I guess that's the main reason it was implemented. – mic_e Sep 09 '12 at 18:26
  • 1
    The `offsetof()` macro cannot be implemented as a function. – Jonathan Leffler Sep 09 '12 at 18:46
  • Try doing anything non-trivial in C. Because C lacks templates containers in C are normally implemented as macros. – Maxim Egorushkin Sep 09 '12 at 18:51
  • See also: http://stackoverflow.com/questions/653839/what-are-c-macros-useful-for – Kev Sep 09 '12 at 23:01

6 Answers6

3

The comments have captured most of it.

Some types of debugging and instrumentation can be accomplished only by use of a function-style macro. The best example is assert(), but function-style macros can also be used for instrumenting code for profiling as well. Magic macros like __FILE__ and __LINE__ as well as features like # for quoting and ## for token-pasting make function-like macros valuable for the debugging and profiling.

In C++, with templates and typically more aggressive inlining, there are few, if any, other reasons to use a function-style macro. For example, the template function std::max is a much better solution than a MAX macro for the reasons illustrated in the question.

In C, it's sometimes necessary in optimization to ensure the a small piece of code is inlined. Macros, with all their caveats, are still occasionally useful in this context. The ALLCAPS naming convention is there to warn programmers that this is actually a macro and not a function because of all the problems with simple text substitution. For example, if you had several places where you needed the equivalent of std::max in a performance-critical piece of code, a macro--with all its dangers--can be a useful solution.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • 1
    I have macros for throwing exceptions and logging: because both cases best capture the function name, file name and line number which is best accomplished by a macro. On the other hand, they *immediately* hand all that down to a function, and only ever cite their arguments once. – Matthieu M. Sep 09 '12 at 18:57
2

Most of the time you don't need to use macros. However there are some cases that are legitimate (although there is room for discussion).

You should not use macros when you can use enums. You should not have macros that depend on having a local variable with a magic name. You should not have macros that can be used as l-values. You should not have macros that have side effects on the code around the expanded macro. Using macros instead of inline functions is most of the time a bad idea, in any case the list is endless.

You could use a macro to fake an iterator though, in C and in particular the Linux Kernel you will see the following:

#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
     pos = n, n = pos->next)

There are numerous other similar type of macros that are used throughout the Linux kernel source.

Also offsetof(3) is typically implemented as a macro, as well as assert(3) etc.

dmp
  • 439
  • 3
  • 6
1

I generally agree with dmp here. If you can use something else you use it and forget about macros.

That said I will mostly repeat what's in comments:

  1. You usually need function like macro when you need to use inside it some other preprocessor symbols (__LINE__, __FILE__, __func__ and so on). This usually means assert/debugging macros.
  2. Stringizing / pasting tokens (# and ## operators) in a macro.
  3. You CAN (if you feel adventures enough) use function-like macros to create enum->string mappings using some cleaver tricks like defining/undefining and redefining macros.
  4. Something like va_list/va_arg where you need access through pointer with simultaneous change of the place where it points to.

And I repeat myself - if you can avoid preprocessor just do that.

Tomek
  • 4,554
  • 1
  • 19
  • 19
0

The C standard has (or allows) a whole bunch of them, and I think anything that does macros in the same spirit should be legitimate:

  • character handling functions isspace and similar are often implemented as macros
  • CMPLX is declared to be a macro
  • UINT64_C and similar are macros
  • if you include tgmath.h, sin, cos and a lot of other functions become macros
  • the whole point of the new _Generic keyword is to write function-like macros
Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
0

In some cases, there will be compiler specific instructions which vary and are noisy to rewrite for each compiler. One macro that I use often which falls into this category is used to inform the compiler that a parameter is unused:

virtual size_t heightForItemAtIndex(const size_t& idx) const {
  MONUnusedParameter(idx); // << MONUnusedParameter() is the macro
  return 12; // << this type's items are all the same height
}

The problem is introduced better here: cross platform macro for silencing unused variables warning

Community
  • 1
  • 1
justin
  • 104,054
  • 14
  • 179
  • 226
  • On the other hand, just using a template function works too: `template void ignore_unused(T const&) {};`, so why use a macro ? – Matthieu M. Sep 09 '12 at 18:56
  • @MatthieuM. a) primarily because i want one name for multiple languages (e.g. the template would not apply to C), b) i would *assume* that with direct support from the compiler, that it is preferred approach c) increased compile times (presumed) and link times d) unnecessary debug-stepping. ||| if you use the template, perhaps you can comment on the effects on multiple compilers for b, c, and d in debug and release builds. the template seems a fine substitute, where it fits all the conditions (i.e. languages). – justin Sep 09 '12 at 19:25
  • I only ever use C++ (not C), and given that most of my files end up relying on Boost, you can guess that one more template function is not worth it. I would dare hope that compilers are clever enough to completely optimize out even in Debug mode (it's trivially empty). Nonetheless, I do not use it for unused parameters (I simply remove the parameter name if it is unused), but for unused variables, which crop up sometimes in C++ due to RAII constructs. – Matthieu M. Sep 10 '12 at 03:31
  • @MatthieuM. well that winds up being a lot of types and a lot of instantiations when it is used across an entire codebase (some with really complex template parameter names). i just checked; at `-O0` the functions remain in the exported symbol table, as well as the debug info when building using Clang and GCC-Apple. – justin Sep 10 '12 at 03:57
  • Of course, a simpler and very portable way to silence the unused parameter warning is to simply not name the parameter. No macros or templates needed. – Adrian McCarthy Sep 11 '12 at 16:09
  • @AdrianMcCarthy that's also an option (as well as the similar `virtual size_t heightForItemAtIndex(const size_t& /* idx */ ) const`). my preference is to use the macro to clearly document that the implementation ignores the parameter. at least, that's the clear and fast way as i read and write. – justin Sep 11 '12 at 18:00
  • @justin: I prefer to comment out the parameter name because it's more resilient to changes. If you change the function and start using that parameter, you might forget to remove the macro, which leaves you with a misleading `UNUSED_PARAMETER(IDX)` at the top of the function. With the comment approach, you cannot accidentally start using the parameter without un-commenting the name, since it'll never get past the compiler. – Adrian McCarthy Sep 11 '12 at 18:21
  • @AdrianMcCarthy my dominant toolchain+settings will flag both caveats you have presented. although, support of this on other toolchains varies. – justin Sep 11 '12 at 18:55
0

Google Style C++ Guide has one example of a pretty useful macro (I'm not sure if this is what you mean by 'function-like macro' - it takes an argument but it can not be replaced by a function call):

DISALLOW_COPY_AND_ASSIGN(ClassName);

Such macro can be put in a private section of a class to disable a copy constructor and an assignment operator that are automatically generated by a compiler. It is usually a good idea to disallow these two automatically generated methods unless a class really needs them. Without a macro disabling requires quite a lot of typing in every class:

ClassName(const ClassName&);
void operator=(const ClassName&);

The macro just automatically generates this code. This is pretty safe. I'm not aware of any cases in which such macro causes problems.

Jan Wrobel
  • 6,969
  • 3
  • 37
  • 53