The best answer is probably in actual usage. Personally, I consider the Linux kernel as an aggregation of experience, being most insightful in answering many coding principles.
It seems appropriate first to outline the types of macros in the C language:
- Object-like , that provide symbolic naming (aliasing), usually of constants.
- Function-like, that perform an operation on one or more inputs.
Object macros are used to describe constants in a more descriptive manner, making the code more readable. They may also be used as compiler predefined macros that describe at compile time the line number, function name, file name and even the compiler capabilities.
Function-like macros usage range from just simple wrappers to full function alternative, including ones that are impossible to implement through conventional functions.
The Macro comes lastWhen trying to define a default rule for prioritizing between macros and functions, I usually reach the following order:
- Function.
- Inline Function. (be careful)
- Macro.
This will fit most common usage with a simple reasoning: functions are safer, they are evaluated in both compilation and linkage, easier to debug, do not increase code footprint and easier to scale.
Considering modern compiler optimizations and the inline option, this fits well even when considering CPU and memory constraints.
Only Macro
Nevertheless, there are still occasions where macros are very useful, and functions, of any type, cannot be an alternative. Usually, these macros either are generic enough to ignore the input types or accept as input the type on which the work is to be done.
A simple example is the min() macro which is consider "safe":
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; })
- It is usable in expression context. (allowing: if(x) min(a,b); )
- Only evaluates its arguments once. (supporting: min(a++,b); )
- Requires that both of its arguments are of the same type (i.e. no implicit casts).
In fact, list_entry() is a wrapper for the container_of() macro:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \Its objective is simple: Given a node pointer, the data type in which it lives and the data type member name that represents the node, return the pointer to the data structure. Note that the node may be placed anywhere in the structure.
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
Here is a simple example of how it is used:
struct my_data_s
{
int id;
struct list_head node;
char *name;
}
struct my_data_s test_data[10];Obviously, such a macros cannot be converted in any way to a function, and at the same time, it presents a valuable functionality of generalizing usage without loosing "safety".
struct my_data_s *tmp_data;
struct list_head *p_node = &test_data[5].node;
tmp_data = container_of(p_node, struct my_data_s, node);
/* tmp_data == &test_data[5] */
Creator Macro
Another interesting usage of macros is the static creation & initialization of objects.
One such example, also in the list module is LIST_HEAD(), which creates a link list and initializes it. In some cases, the modules provide both static and dynamic (*alloc) object/instance creation, where the macro is used for creating the static ones.
Wrapper Macro
Wrappers can actually be implemented as functions, however, many times they are so basic that it seems wasteful to use a call function. Other times, it is required to control such operation at compile time (and not run-time).
Usage includes mocking of functions in unit tests, expanding an existing function (adding arguments) without touching the callers, switching between debug and production code through compilation flags (see the assert() macro), etc.

Summary
We should prefer to "function", "inline" if necessary and "macro" when appropriate, in this order.
Reasoning the use of each function-like macro is a good start.
(and when such a reason is found, please share so I could add to my list :-))
