If you're going to write a long lecture to people, it's helpful to do some Googling to fact-check your own understanding.
The claim that macros do not exist in other languages is incorrect. Every sensible assembly language compiler has macros to help the programmer with boilerplate.
The claim that multiline macros are a problem is correct. Suggesting that a workaround is to use a naming convention for such macros is crazy-talk. There is a time-honored pattern you use to fix this in the macro itself: wrap the macro with "do { <macro content> } while (0)".
The goal of this article is not to provide some genuine insight to the topic, but to serve as a publicity stunt for the largest code academy in Bulgaria, dubiously called "Software University". On numerous other occasions their lecturers and trainers have made ridiculously misleading claims about trivial programming stuff. One of their lecturers insisted that the second return in "return; return;" was probably needed just in case the first one did not work:
In short, kids with little to no programming experience teach other disorientated desperate kids with few career opportunities in a country with a primitive economy.
> The claim that macros do not exist in other languages is incorrect
Technically incorrect, but how many of the top 20 languages (https://www.tiobe.com/tiobe-index/) have c style macros? I don't think many of them have a pre-processor at all.
This isn't wrong but I've got issues with some of the examples, especially the lack of a solution for #2 and #4.
#2 is referred to as an unsafe macro because of the multiple evaluation of its arguments. In some cases this is fine but it may need to be documented that the macro is unsafe for future users. It is also entirely possible to convert these macros into safe ones if you're willing/able to use GNU C extensions like typeof. The SEI CERT secure C wiki has a good reference for this at [0].
#3 presumes you want to compose functions but I'd suggest that by doing so you are writing bad C. Because of the lack of any type of side-band error reporting (exceptions etc) you must presume that your functions will always return a sane/non-error value. That's not a good assumption even for your own code and definitely not for the C library or any other libs. In addition, this is just another unsafe macro which could be made safe as above.
#4 doesn't explain the solution to the problem it poses which is annoying because it's the most important one IMO. There are a number of ways to make a multi-line macro safe for use in an unbraced expression body. My preferred way is to define the macro with a always false-post check while loop. e.g.
#define MY_NAME_JEF(x) do{ \
const char jef[] = "jef"; \
printf("my name %s, not %s\n", jef, x); \
}while(0)
Note the lack of a trailing semi-colon. This is because we expect the macro to be terminated with its own semi-colon. Stupid example but it gets the point across I suppose.
Check out the secure C wiki, it's full of gems which can help you avoid issues with macros, and much more.
> #2 is referred to as an unsafe macro because of the multiple evaluation of its arguments. In some cases this is fine but it may need to be documented that the macro is unsafe for future users.
In the early days of C programming, a naming convention was adopted to warn about this. If you wrote a macro that might have multiple evaluation of its arguments, you named it in UPPERCASE as a warning:
#define MIN( a, b ) ( (a) < (b) ? (a) : (b) )
This practice spread into pretty much all macro definitions. If you defined a macro of any sort, you gave it an uppercase name, even if the macro didn't take any arguments at all and therefore warning about multiple evaluation was moot:
#define MAX_SIGNED_SHORT 32767
From there the notion spread into other languages that all constants ought to have UPPER_CASE_NAMES. So this is why you see JavaScript code like:
People who know Lisp are unlikely to be writing blogs about using macros in C.
C and C++ blogs generally tend to be of poor quality, because most of the people writing such things are excited newbies who have fewer years of experience than real experts have abstinence.
Lisp macros directly manipulate the program's abstract syntax tree (in the case of Lisp, it happens to hava a 1-1 relationship to the source code structure). It's more comparable to a web app's html templater than a syntactic macro system like Lisp's.
The article is an okay start, but it wasn't written by an expert. It lists some real pitfalls but doesn't provide commonly accepted solutions. Some examples...
multiple lines:
wrap in do { } while (0)
- can use a multiline macro like a function call, terminated with ;
or ({foo; bar;})
- multiple lines evaluate to 'bar'...
Function calls:
- in general anything with side effects could be harmful if evaluated more than once.
- But possible to write MIN()/MAX() with single evaluation of 'a' and 'b'.
#define MAX(a, b) \
({typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b;})
And other important things like stringify, variadics, etc...
The golden rule of macros is "always read the definition of macros before using." If you know it's a single value you're probably fine `#define MASK 0xf7`, but if it's a function macro or a normal macro that you know is doing more than inserting a literal in your code, it's best to glance at the macro to see what it does. Macros are useful for writing "shorthand C", but C should not be turned into a DSL, so you should think in actual C by converting the "shorthand C" in your head as you read/write it.
> macros can’t be debugged. When you use a function, that function can be stepped through by the debugger. The macro cannot.
Looks like the debugger could use some improvement.
> A macro is faster than a function.
This is false. Macros may be faster in some cases, but I've seen them being slower. I suspect this is because functions have more type information that can help the optimiser. I've tried it with Monocypher: replacing serialisation/de-serialisation functions (load and store) by equivalent macros slowed the whole thing down.
> Looks like the debugger could use some improvement.
Try `gcc -g3` (the default is 2). But because C-style macros are context-sensative string operations, the result is not necessarily especially pleasant/useful and can be rather large (slowing down the debugger for everything else).
I find that using `static inline` functions and `static const` variables in the header can just be much nicer to work with all around (some argument type-checking, better stack frame management means alloca is usable, no accidental syntax clashes requiring lots of extra parens to avoid, and real debug info). And, if necessary, use a tiny macro just to form the call to the real function or form the declarations for repeated tabular data. I wouldn't have expected macros to be slower in any case, however.
Also, C99 `inline` functions calls do not neccessarily compile to inlined code, and non-inline functions may or may not be inlined, and in certain cases (most notably tail-call optimizations), will not create new stack frames.
An article that is an outspring of the hugely-overhyped IT industry in Sofia (Bulgaria). Code academies, recruitment agencies, startup organisations and conference organisers are literally larger than the entire IT sector and they all want to make a quick buck from desperate young people with very limited choices for a prospective career in other fields.
"We will make you a rich programmer in 3 months", they say.
> When you add the inline keyword in front of a function, you are hinting the compiler to embed the function body inside the caller (just like a macro).
That is not quite the case. The inline keyword has changed to mean multiple definitions are allowed. To quote [1]:
" Because the meaning of the keyword inline for functions came to mean 'multiple definitions are permitted' rather than 'inlining is preferred' "
The problem with those explanations is that they are pointless. Unless you're still in college taking a C course, most people understand that macros are substitutions.
> Some compilers offer debug strings in macros, which cannot be used in functions: __FILE__, __LINE__, __func__.
But... Of course they can. They are just symbols defined by the compiler, and have nothing to do with being in a macro expansion or not. Heck, they are probably implemented as macro's themselves inside the preprocessor.
There are many valid use cases of macros in C (and other languages) which you might not have seen, like those which use __LINE__ etc, those which expand a string into a variable type or function, those which process `-DNAME=x` flags.
[+] [-] cjensen|8 years ago|reply
The claim that macros do not exist in other languages is incorrect. Every sensible assembly language compiler has macros to help the programmer with boilerplate.
The claim that multiline macros are a problem is correct. Suggesting that a workaround is to use a naming convention for such macros is crazy-talk. There is a time-honored pattern you use to fix this in the macro itself: wrap the macro with "do { <macro content> } while (0)".
[+] [-] bluetomcat|8 years ago|reply
https://www.youtube.com/watch?v=_ZwiMlyeQNU&t=15s
In short, kids with little to no programming experience teach other disorientated desperate kids with few career opportunities in a country with a primitive economy.
[+] [-] flukus|8 years ago|reply
Technically incorrect, but how many of the top 20 languages (https://www.tiobe.com/tiobe-index/) have c style macros? I don't think many of them have a pre-processor at all.
[+] [-] ecma|8 years ago|reply
#2 is referred to as an unsafe macro because of the multiple evaluation of its arguments. In some cases this is fine but it may need to be documented that the macro is unsafe for future users. It is also entirely possible to convert these macros into safe ones if you're willing/able to use GNU C extensions like typeof. The SEI CERT secure C wiki has a good reference for this at [0].
#3 presumes you want to compose functions but I'd suggest that by doing so you are writing bad C. Because of the lack of any type of side-band error reporting (exceptions etc) you must presume that your functions will always return a sane/non-error value. That's not a good assumption even for your own code and definitely not for the C library or any other libs. In addition, this is just another unsafe macro which could be made safe as above.
#4 doesn't explain the solution to the problem it poses which is annoying because it's the most important one IMO. There are a number of ways to make a multi-line macro safe for use in an unbraced expression body. My preferred way is to define the macro with a always false-post check while loop. e.g.
Note the lack of a trailing semi-colon. This is because we expect the macro to be terminated with its own semi-colon. Stupid example but it gets the point across I suppose.Check out the secure C wiki, it's full of gems which can help you avoid issues with macros, and much more.
[0] https://wiki.sei.cmu.edu/confluence/display/c/PRE31-C.+Avoid...
[+] [-] Stratoscope|8 years ago|reply
In the early days of C programming, a naming convention was adopted to warn about this. If you wrote a macro that might have multiple evaluation of its arguments, you named it in UPPERCASE as a warning:
This practice spread into pretty much all macro definitions. If you defined a macro of any sort, you gave it an uppercase name, even if the macro didn't take any arguments at all and therefore warning about multiple evaluation was moot: From there the notion spread into other languages that all constants ought to have UPPER_CASE_NAMES. So this is why you see JavaScript code like: instead of something easier on the eyes like:[+] [-] phoe-krk|8 years ago|reply
But again, Lisps generally have more powerful macros than C does - they allow for arbitrary compile-time computation.
[+] [-] Stratoscope|8 years ago|reply
> One strange phenomenon when coding in C is using macros. This is not something which can be seen in other programming languages (other than C++).
Instead of indenting a quote, use:
or if you want italics to set it off more:[+] [-] naavis|8 years ago|reply
[+] [-] kazinator|8 years ago|reply
C and C++ blogs generally tend to be of poor quality, because most of the people writing such things are excited newbies who have fewer years of experience than real experts have abstinence.
[+] [-] dangerbird2|8 years ago|reply
[+] [-] jeff571|8 years ago|reply
multiple lines: wrap in do { } while (0) - can use a multiline macro like a function call, terminated with ; or ({foo; bar;}) - multiple lines evaluate to 'bar'...
Function calls: - in general anything with side effects could be harmful if evaluated more than once. - But possible to write MIN()/MAX() with single evaluation of 'a' and 'b'. #define MAX(a, b) \ ({typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b;})
And other important things like stringify, variadics, etc...
[+] [-] colanderman|8 years ago|reply
So is "typeof"; "__typeof__" is the standard spelling.
[+] [-] vortico|8 years ago|reply
[+] [-] loup-vaillant|8 years ago|reply
Looks like the debugger could use some improvement.
> A macro is faster than a function.
This is false. Macros may be faster in some cases, but I've seen them being slower. I suspect this is because functions have more type information that can help the optimiser. I've tried it with Monocypher: replacing serialisation/de-serialisation functions (load and store) by equivalent macros slowed the whole thing down.
[+] [-] manwe150|8 years ago|reply
Try `gcc -g3` (the default is 2). But because C-style macros are context-sensative string operations, the result is not necessarily especially pleasant/useful and can be rather large (slowing down the debugger for everything else).
I find that using `static inline` functions and `static const` variables in the header can just be much nicer to work with all around (some argument type-checking, better stack frame management means alloca is usable, no accidental syntax clashes requiring lots of extra parens to avoid, and real debug info). And, if necessary, use a tiny macro just to form the call to the real function or form the declarations for repeated tabular data. I wouldn't have expected macros to be slower in any case, however.
[+] [-] unknown|8 years ago|reply
[deleted]
[+] [-] dangerbird2|8 years ago|reply
[+] [-] unwind|8 years ago|reply
[+] [-] vortico|8 years ago|reply
[+] [-] messe|8 years ago|reply
[+] [-] bluetomcat|8 years ago|reply
"We will make you a rich programmer in 3 months", they say.
[+] [-] rocqua|8 years ago|reply
That is not quite the case. The inline keyword has changed to mean multiple definitions are allowed. To quote [1]: " Because the meaning of the keyword inline for functions came to mean 'multiple definitions are permitted' rather than 'inlining is preferred' "
[1] http://en.cppreference.com/w/cpp/language/inline
[+] [-] kkmx|8 years ago|reply
[+] [-] bluetomcat|8 years ago|reply
[+] [-] tomsmeding|8 years ago|reply
But... Of course they can. They are just symbols defined by the compiler, and have nothing to do with being in a macro expansion or not. Heck, they are probably implemented as macro's themselves inside the preprocessor.
[+] [-] jstimpfle|8 years ago|reply
[+] [-] jstewartmobile|8 years ago|reply
[+] [-] Fronzie|8 years ago|reply
[+] [-] vortico|8 years ago|reply
[+] [-] flavio81|8 years ago|reply
[+] [-] bligh____|8 years ago|reply
[+] [-] jwilk|8 years ago|reply
Please don't complain that a submission is inappropriate. If a story is spam or off-topic, flag it.