Background:
Macros originated from the C language as a powerful means of automating repetitive code sections at the preprocessing stage. In C++, their use provided flexibility but also brought many hidden dangers due to the lack of type checking and the non-obvious workings of the preprocessor.
Problem:
The main risks of using macros:
Solution:
In modern C++ standards, it is recommended to use inline functions, templates, constexpr, enum class, and constexpr variables instead of macros.
Code example:
// Bad: #define MAX(a, b) ((a) > (b) ? (a) : (b)) // Good: template<typename T> constexpr T max(T a, T b) { return a > b ? a : b; }
Key features:
Can a macro be more dangerous than an inline function?
Yes. A macro does not follow syntax and type rules. Unexpected results can occur when passing parameters with side effects.
#define SQUARE(x) ((x) * (x)) int y = 5; int z = SQUARE(y++); // y is incremented twice!
Is #include also a macro?
No, #include is a preprocessor directive, but the use of macros and include is related: you can modify the list of included files through a macro (which is highly discouraged).
Can you debug a macro like a regular function?
No, the debugger unfolds the macro and shows the substituted text, there are no separate named entities.
In old code, numerous computational macros with effects (e.g., increment) were defined, leading to elusive bugs when new features were introduced.
Pros:
Cons:
During refactoring, macros were replaced with template and constexpr functions, and enum class was used instead of flag macros.
Pros:
Cons: