I have a problem with macro expansion deferral. Here is an example:
#include <stdio.h>
#define CONST_ABC 15
#define CONST_5 7
#define ABC 5
#define PRINT(x) printf("CONST=%d\n", CONST_ ## x)
// The problematic macro
#define PRINT2(x) PRINT(x)
int main(int argc, char *argv[])
{
PRINT(ABC); // Prints 15 - OK
PRINT2(ABC); // Prints 7 - Not OK.
}
How to define PRINT2 macro so that it will use PRINT and result would be 15? I'm getting:
CONST=15
CONST=7
And want to get:
CONST=15
CONST=15
It requires you to have at least a C99 compiler, since C99 allows empty macro arguments. However some compilers may allow them as an extension, even in C89 mode. Here is the code:
#include <stdio.h>
#define CONST_ABC 15
#define CONST_5 7
#define ABC 5
#define PRINT(x) printf("CONST=%d\n", CONST_ ## x)
// The problematic macro
#define PRINT2(x, y) PRINT(x ## y)
int main(int argc, char *argv[])
{
PRINT(ABC); // Prints 15 - OK
PRINT2(ABC,); // Prints 7 - Not OK.
}
The second argument (i.e. the y) is empty, making it an empty preprocessing token. The ## operator prevents argument expansion, so the result of the concatenation is the same as x argument.
C11 6.10.3.1/p1 Argument substitution (emphasis mine):
After the arguments for the invocation of a function-like macro have
been identified, argument substitution takes place. A parameter in the
replacement list, unless preceded by a # or ## preprocessing token or
followed by a ## preprocessing token (see below), is replaced by the
corresponding argument after all macros contained therein have been
expanded. Before being substituted, each argument’s preprocessing
tokens are completely macro replaced as if they formed the rest of the
preprocessing file; no other preprocessing tokens are available.
The macro replacement, basically, proceeds as follows:
A token is found, which is a macro name
The arguments of the macro are collected
The arguments are substituted for the formal parameters in the body of the macro definition
Thus substituted parameters are completely macro replaced, not counting the rest the of input; stringification may be performed at this stage as well;
The token paste operators are performed
Thus substituted sequence is re-scanned, together with the rest of the input for further macro replacements
(plus some not well defined rules when a macro is forbidden for replacement)
The only way to prevent a macro argument from being macro-replaced in 4.
is for it to be followed or preceded by a token paste operator (##).
However, in 5. the paste operator has to perform the operation with
the argument under discussion and a special placemarker token. A placemarker token is inserted only for empty argument substitution.
Check this, it might give you an idea for your real code:
#define PRINT2(noreplace,x) PRINT(noreplace ## x)
PS. and yeah, "noreplace" is meant to be empty :)
PRINT2(,ABC)
Related
I come up with a small example of the problem
Assume I have this macro
#define and ,
And I use it like sum(a and b) which should be expanded to sum(a, b).
My problem here is that if sum is defined by a macro the usage example encounters a too few arguments ... error.
Another problem that I think probably will be related and I guess would be solved by the same trick, is when I define an empty macro and place it between function name and argument list. For example
#define of
Now when I use sum of(1, 2), the compiler treats sum as a function and if it is a macro, then, linker throws an undefined reference error.
#define of
#define sum(a, b) a + b
int main()
{
sum of(a, b); // undefined reference to `sum'
}
Issue with and macro is that a and b is identified as macro argument before replacing and with , happens.
6.10.3.1 Argument substitution
After the arguments for the invocation of a function-like macro have been identified, argument
substitution takes place. A parameter in the replacement list, unless preceded by a # or ## prepro-
cessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding
argument after all macros contained therein have been expanded. Before being substituted, each
argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the
preprocessing file; no other preprocessing tokens are available.
Your second problem is similar but not related. After of is expanded, it is too late to expand sum because it is no longer scanned by the preprocessor:
6.10.3.4 Rescanning and further replacement
After all parameters in the replacement list have been substituted and # and ## processing has
taken place, all placemarker preprocessing tokens are removed. The resulting preprocessing token
sequence is then rescanned, along with all subsequent preprocessing tokens of the source file, for
more macro names to replace.
This program gives output as 5. But after replacing all macros, it would result in --5. This should cause an compilation error, trying to decrement the 5. But it compiles and runs fine.
#include <stdio.h>
#define A -B
#define B -C
#define C 5
int main()
{
printf("The value of A is %d\n", A);
return 0;
}
Why is there no error?
Here are the steps for the compilation of the statement printf("The value of A is %d\n", A);:
the lexical parser produces the preprocessing tokens printf, (, "The value of A is %dn", ,, A, ) and ;.
A is a macro that expands to the 2 tokens - and B.
B is also a macro and gets expanded to - and C.
C is again a macro and gets expanded to 5.
the tokens are then converted to C tokens, producing errors for preprocessing tokens that do not convert to proper C tokens (ex: 0a). In this example, the tokens are identical.
the compiler parses the resulting sequence according to the C grammar: printf, (, "The value of A is %d\n", ,, -, -, 5, ), ; matches a function call to printf with 2 arguments: a format string and a constant expression - - 5, which evaluates to 5 at compile time.
the code is therefore equivalent to printf("The value of A is %d\n", 5);. It will produce the output:
The value of A is 5
This sequence of macros is expanded as tokens, not strictly a sequence of characters, hence A does not expand as --5, but rather as - -5. Good C compilers would insert an extra space when preprocessing the source to textual output to ensure the resulting text produces the same sequence of tokens when reparsed. Note however that the C Standard does not say anything about preprocessing to textual output, it only specifies preprocessing as one of the parsing phases and it is a quality of implementation issue for compilers to not introduce potential side effects when preprocessing to textual output.
There is a separate feature for combining tokens into new tokens in the preprocessor called token pasting. It requires a specific operator ## and is quite tricky to use.
Note also that macros should be defined with parentheses around each argument and parentheses around the whole expansion to prevent operator precedence issues:
#define A (-B)
#define B (-C)
#define C 5
Two consecutive dashes are not combined into a single pre-decrement operator -- because C preprocessor works with individual tokens, effectively inserting whitespace around macro substitutions. Running this program through gcc -E
#define A -B
#define B -C
#define C 5
int main() {
return A;
}
produces the following output:
int main() {
return - -5;
}
Note the space after the first -.
According to the standard, macro replacements are performed at the level of preprocessor tokens, not at the level of individual characters (6.10.3.9):
A preprocessing directive of the form
# define identifier replacement-list new-line
defines an object-like macro that causes each subsequent instance of the macro name to be replaced by the replacement list of preprocessing tokens that constitute the remainder of the directive.
Therefore, the two dashes - constitute two different tokens, so they are kept separate from each other in the output of the preprocessor.
Whenever we use #include in C program, compiler will replace the variable with its value wherever it is used.
#define A -B
#define B -C
#define C 5
So when we print A ,
it will execute in following steps.
A=>-B
B=>-C
A=>-(-C)=>C
So when we print value of A, it comes out to be 5.
Generally these #define statements are used to declare value of constants that are to be used through out the code.
For more info see
this link on #define directive
This is a follow-up question to this one (and also closer to the actual problem at hand).
If I have the following case:
#include <stdio.h>
#define FOO_ONE 12
#define FOO_TWO 34
#define BAR_ONE 56
#define BAR_TWO 78
#define FOO 99
#define STRINGIFY(mac) #mac
#define CONCAT(mac1, mac2) STRINGIFY(mac1) STRINGIFY(mac2)
#define MAKE_MAC(mac) CONCAT(mac##_ONE, mac##_TWO)
#define PRINT(mac) printf(#mac ": " MAKE_MAC(mac) "\n")
void main(int argc, char *argv[])
{
PRINT(FOO);
PRINT(BAR);
}
As can be seen, the stringified, concatenated macros are then substituted inside a printf() statement which itself is inside a macro.
Because FOO is defined (as 99), it happens that it is expanded before the concatenation with _ONE and _TWO, effectively creating the tokens 99_ONE and 99_TWO.
This program outputs:
FOO: 99_ONE99_TWO
BAR: 5678
How can I defer the expansion of the FOO macro (effectively, eliminating it altogether, to get the required output of:
FOO: 1234
BAR: 5678
NOTE: assume the PRINT() macro signature cannot be changed (i.e., can't add a parameter, etc.). However its implementation can be changed. Also, FOO, FOO_* and BAR_* definitions cannot be modified as well.
If you can do something each time FOO is used, you could first undefine the macro inorder for it expand as you want and then redefine it as in
#undef FOO
PRINT(FOO);
#define FOO 99
in which case it will expand to
printf("FOO" ": " "12" "34" "\n");
printf("BAR" ": " "56" "78" "\n");
to print what you want.
How can I defer the expansion of the FOO macro ... NOTE: assume the PRINT() macro signature cannot be changed (i.e., can't add a parameter, etc.)
You can't.
Macro expansions go through a series of steps:
Argument substitution
Paste and stringification in no particular order
Rescan and further replacement
Argument substitution occurs with your arguments; in the case of the invocation PRINT(FOO), FOO; and it's the very first step. By the time you even get the preprocessor to recognize that something in your replacement list is a macro, you've long past argument substitution.
The rule for argument substitution is that if any of your parameters are mentioned in your replacement list, and those parameters are being neither stringified nor pasted, then the corresponding argument is fully evaluated and those mentions of the parameters are replaced with the result. In this case, PRINT(FOO), after argument substitution, results in the replacement list:
printf(#mac ": " MAKE_MAC(99) "\n")
Again, MAKE_MAC's definition doesn't matter; it's not even recognized as a macro until rescan and further replacement.
Now, without your restriction, you could defer FOO from expanding... by adding a second parameter to PRINT and pasting to it (that disqualifies it from a.s., and by the time rescan and replacement comes along it'd be invoking your next macro). But with your restriction, you're DOA.
The actual expansion of FOO to 99 happens at the moment when PRINT is expanded and before its body is re-scanned one more time. The relevant part of the ANSI standard is:
6.10.3.1 Argument substitution
1 After the arguments for the invocation of a function-like macro have been identified,
argument substitution takes place. A parameter in the replacement list, unless preceded
by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is
replaced by the corresponding argument after all macros contained therein have been
expanded. Before being substituted, each argument’s preprocessing tokens are
completely macro replaced as if they formed the rest of the preprocessing file; no other
preprocessing tokens are available.
In your case you can avoid expansion of FOO (inside mac) by replacing
#define PRINT(mac) printf(#mac ": " MAKE_MAC(mac) "\n")
with
#define PRINT(mac) printf(#mac ": " CONCAT(mac##_ONE, mac##_TWO) "\n")
I came across one more piece of code that is even more confusing..
#include "stdio.h"
#define f(a,b) a##b
#define g(a) #a
#define h(a) g(a)
int main(void)
{
printf("%s\n",h(f(1,2)));
printf("%s\n",g(1));
printf("%s\n",g(f(1,2)));
return 0;
}
output is
12
1
f(1,2)
My assumption was
1) first f(1,2) is replaced by 12 , because of macro f(a,b)
concantenates its arguments
2) then g(a) macro replaces 1 by a string literal "1"
3) the output should be 1
But why is g(f(1,2)) not getting substituted to 12.
I'm sure i'm missing something here.
Can someone explain me this program ?
Macro replacement occurs from the outside in. (Strictly speaking, the preprocessor is required to behave as though it replaces macros one at a time, starting from the beginning of the file and restarting after each replacement.)
The standard (C99 §6.10.3.2/2) says
If, in the replacement list, a parameter is immediately preceded by a # preprocessing
token, both are replaced by a single character string literal preprocessing token that
contains the spelling of the preprocessing token sequence for the corresponding
argument.
Since # is present in the replacement list for the macro g, the argument f(1,2) is converted to a string immediately, and the result is "f(1,2)".
On the other hand, in h(f(1,2)), since the replacement list doesn't contain #, §6.10.3.1/1 applies,
After the arguments for the invocation of a function-like macro have been identified,
argument substitution takes place. A parameter in the replacement list, unless preceded
by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is
replaced by the corresponding argument after all macros contained therein have been
expanded.
and the argument f(1, 2) is macro expanded to give 12, so the result is g(12) which then becomes "12" when the file is "re-scanned".
Macros can't expand into preprocessing directives. From C99 6.10.3.4/3 "Rescanning and further replacement":
The resulting completely macro-replaced preprocessing token sequence
is not processed as a preprocessing directive even if it resembles
one,
Source: https://stackoverflow.com/a/2429368/2591612
But you can call f(a,b) from g like you did with h. f(a,b) is interpreted as a string literal as #Red Alert states.
I am trying to understand what is the flow of replacement of same2, same1 and concatenate in:
#include<stdio.h>
#define concatenate(a,b) a##b
#define same1(a) #a
#define same2(a) same1(a)
main()
{
printf("%s\n",same2(concatenate(1,2)));
printf("%s\n",same1(concatenate(1,2)));
}
I have tried to understand this from many places but I am not able to get it. Can somebody please explain it more clearly?
With
#define concatenate(a,b) a##b
#define same1(a) #a
#define same2(a) same1(a)
when you have same2(concatenate(1,2)), the argument of same2 is expanded before passing it to same1, so there, concatenate(1,2) is replaced by its result, 12 that then is stringified by same1 to produce "12".
With same1, no expansion of the macro argument occurs, since it's preceded by the stringification token #:
After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.
(section 6.10.3.1 (1) in n1570)
Your concatinate(a,b) becomes concatinate(1,2) when 1 and 2 are passed as parameters. That in turn becomes 1##2 which translates to 12 because ## is a concatenating operator. So parameters 1 and 2 are concatenated to become 12
Same1(a) becomes simply #a, where # is stringisizing operator (See http://c-faq.com/ansi/stringize.html). So Same1(12) becomes "12" and when printed out to console
Same2(a) is the same as Same1(a), which is simply #a, which just outputs parameter a as-is; so the output of Same2(a) where parameter a is 'Concatenate(1,2)' is just a string "Concatenate(1,2)"