C Preprocessor: Evaluate macro early

后端 未结 2 477
猫巷女王i
猫巷女王i 2021-01-11 11:50

Consider the following setup:

a.h

#define A 5
#define B A
#undef A
#define A 3

a.c



        
相关标签:
2条回答
  • 2021-01-11 12:26

    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 Bto 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 #ifs 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).

    0 讨论(0)
  • 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.

    0 讨论(0)
提交回复
热议问题