I have read in many books & tutorials that I should avoid macros in c++. Fine, but why? I don't get it. They are very useful and often used in C.
Could someone explain (very) detailed, why I should avoid them in C++?
Macros repeat poorly constructed code all over the place — so you really need to know what you are doing with your macros and test them thoroughly before deploying them everywhere, and you need to avoid jumping down a rabbit hole if you want to go and update them later.
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.
Macros are typically faster than functions as they don't involve actual function call overhead.
A good rule of thumb would be to only use macros in single-player games where the macros can help you automate certain tedious processes. Those would truly be the best macros for gaming as there are no downsides to using them for yourself or others.
Macros don't respect scoping rules and operate at the textual level, as opposed to the syntax level. From this arise a number of pitfalls that can lead to strange, difficult to isolate bugs.
Consider the following well-known example:
#define max(a, b) ((a) < (b) ? (b) : (a))
⋮
int i = max(i++, j++);
The preferred alternative in this case is a function template:
template <typename T>
T max(const T & a, const T & b) { return a < b ? b : a; }
Here's another case that leads to subtle problems:
#define CHECK_ERROR(ret, msg) \
if (ret != STATUS_OK) { \
fprintf(stderr, "Error %d: %s\n", ret, msg); \
exit(1); \
}
⋮
if (ready)
CHECK_ERROR(try_send(packet), "Failed to send");
else
enqueue(packet);
You might think that the solution is as simple as wrapping the contents of CHECK_ERROR
in { … }
, but this won't compile due to the ;
before the else
.
To avoid the above problem (the else
attaching to CHECK_ERROR
's if
instead of the outer if
), one should wrap such macros in do … while (false)
as follows:
#define CHECK_ERROR(ret, msg) \
do { \
if (ret != STATUS_OK) { \
fprintf(stderr, "Error %d: %s\n", ret, msg); \
exit(1); \
} \
while (false)
This has no effect on the meaning of the macro, but ensures that the entire block is always treated as a single statement and doesn't interact in surprising ways with if
statements.
Long story short, macros are hazardous at many levels and should thus be used only as a last resort.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With