Consider the following setup:
a.h
#define A 5
#define B A
#undef A
#define A 3
a.c
#include "a.h"
#include <stdio.h>
int main()
{
printf("%d\n", B);
return 0;
}
While this very reasonably prints 3, is there a way to make it print 5, i.e. do the substitution of 5 for A already at line two of a.h?
No, there's no way to do that. Unless you know all the possible values of A
, and they are always integers, in which case you can laboriously test each one in turn:
#if A == 0
# define B 0
#elif A == 1
# define B 1
#elif A == 2
# define B 2
/* ... and a very long etc. */
#endif
If your use case only involves integers, you have more options. You could, for example, declare B
to be static const int
or enum
(depending on language) instead of a macro, which would obviously use the current value of the macro. If you really really want macros, the Boost preprocessing library has an implementation of the laborious sequence of #if
s above (with some cleverness to reduce the number of preprocessor statements needed to log(N) instead of N).
There is no macro substitution in the #define
preprocessor directive; this fact is covered by §6.10 para. 7 of the C standard (§16 para. 6 of the C++ standard, with identical wording):
The preprocessing tokens within a preprocessing directive are not subject to macro expansion unless otherwise stated.
In the description of the #if
and #include
directives, the standard specifies that macro replacement does occur, which is why the #if
solution above works (and the Boost implementation, which also uses a computed #include
).
Yes. Boost's preprocessor library (a set of portable includes, not an extended preprocessor) includes support for "mutable" macro definitions. Instead of defining a macro to expand to a value directly, you can define it to expand to a reference a mutable slot, which can be changed because it expands the value "assigned" to it early. In this case you're less interested in the ability to change the value, than in the fact that this early expansion means it can grab the value out of A
at a point ahead of both the use of B
or the redefinition of A
.
#include <boost/preprocessor/slot/slot.hpp>
#define A 5
#define B BOOST_PP_SLOT(1)
// "assign" A to B
#define BOOST_PP_VALUE A
#include BOOST_PP_ASSIGN_SLOT(1)
#undef A
#define A 3
#include "a.h"
#include <stdio.h>
int main()
{
printf("%d\n", B); // 5
return 0;
}
Support is limited to integers. It takes advantage of the fact that #if
directives force expansion of any contained macros (so do #line
and #error
, although those are not very useful for this purpose), and uses them to build up an equivalent integer value for the slot being assigned to, stored in hidden backend macros. This way it can "extract" a value from A
, and B
can then refer to the value itself even if A
changes or is removed.