I was wondering why we can't use token concatenation outside of define
s.
This comes up when I want these at the same time:
define
for this then the whole code gets merged into a line and the debugger will only show the line where the define
was usedSome people might want an example (actual question is below that):
lib.inc:
#ifndef NAME #error includer should first define NAME #endif void NAME() { // works } // void NAME##Init() { // doesn't work // }
main.c:
#define NAME conflictfree #include "lib.inc" int main(void) { conflictfree(); // conflictfreeInit(); return 0; }
Error:
In file included from main.c:2:0: lib.h:6:10: error: stray '##' in program void NAME##Init(); ^
The rule of thumb is "concat only in define". And if I remember correctly: The reason is because of the preprocessor-phases. Question: Why does it not work. The phases-argument sounds like it was once an implementation-limitation (instead of a logical reason) and then found its way into the standard. What could be so difficult about accepting NAME##Init()
if NAME()
works fine?
The double-number-sign or token-pasting operator (##), which is sometimes called the merging or combining operator, is used in both object-like and function-like macros. It permits separate tokens to be joined into a single token, and therefore, can't be the first or last token in the macro definition.
Stringizing operator (#) The number-sign or "stringizing" operator (#) converts macro parameters to string literals without expanding the parameter definition. It's used only with macros that take arguments.
Concatenation means joining two strings into one. In the context of macro expansion, concatenation refers to joining two lexical units into one longer one. Specifically, an actual argument to the macro can be concatenated with another actual argument or with fixed text to produce a longer name.
The #define creates a macro, which is the association of an identifier or parameterized identifier with a token string. After the macro is defined, the compiler can substitute the token string for each occurrence of the identifier in the source file.
Why was it is not an easy question. Maybe it's time to ask the standard committee why were they as crazy as to standardize (the now removed) gets()
function as well?
Sometimes, the standard is simply brain-dead, whether we want it or not. The first C was not today's C. It was not "designed" to be today's C, but "grew up" into it. This has led to quite a few inconsistencies and design flaws on the road. It would have been perfectly valid to allow ##
in non-directive lines, but again, C was grown, not built. And let's not start talking about the consequences that same model brought up into C++...
Anyway, we're not here to glorify the standards, so one way to get around this follows. First of all, in lib.inc
...
#include <stdio.h> #ifndef NAME #error Includer should first define 'NAME'! #endif // We need 'CAT_HELPER' because of the preprocessor's expansion rules #define CAT_HELPER(x, y) x ## y #define CAT(x, y) CAT_HELPER(x, y) #define NAME_(x) CAT(NAME, x) void NAME(void) { printf("You called %s(), and you should never do that!\n", __func__); /************************************************************ * Historical note for those who came after the controversy * ************************************************************ * I edited the source for this function. It's 100% safe now. * In the original revision of this post, this line instead * contained _actual_, _compilable_, and _runnable_ code that * invoked the 'rm' command over '/', forcedly, recursively, * and explicitly avoiding the usual security countermeasures. * All of this under the effects of 'sudo'. It was a _bad_ idea, * but hopefully I didn't actually harm anyone. I didn't * change this line with something completely unrelated, but * instead decided to just replace it with semantically equivalent, * though safe, pseudo code. I never had malicious intentions. */ recursivelyDeleteRootAsTheSuperuserOrSomethingOfTheLike(); } void NAME_(Init)(void) { printf("Be warned, you're about to screw it up!\n"); }
Then, in main.c
...
#define NAME NeverRunThis #include "lib.inc" int main() { NeverRunThisInit(); NeverRunThis(); return 0; }
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