Search code examples
c++cc-preprocessor

C preprocessor macro specialisation based on an argument


Is it possible to have one macro expanded differently for one specific argument value and differently for all other arguments?

Say I define a current user:

#define CURRENT_USER john_smith

What I want to be able to do is to have a macro that will be expanded differently if the user passed matches CURRENT_USER. Mind you, I don't know all possible users a priori. The most basic case:

#define IS_CURRENT_USER(user)                   \
    /* this is not valid preprocessor macro */  \
    #if user == CURRENT_USER                    \
        1                                       \
    #else                                       \
        0                                       \
    #endif                                      

With a macro like that, every other macro relying on the username could be done the following way:

#define SOME_USER_SPECIFIC_MACRO(user) SOME_USER_SPECIFIC_MACRO_SWITCH_1(IS_CURRENT_USER(user))

#define SOME_USER_SPECIFIC_MACRO_SWITCH_1(switch)   SOME_USER_SPECIFIC_MACRO_SWITCH_2(switch) // expand switch ...
#define SOME_USER_SPECIFIC_MACRO_SWITCH_2(switch)   SOME_USER_SPECIFIC_MACRO_##switch         // ... and select specific case
    
#define SOME_USER_SPECIFIC_MACRO_0  ... // not current user
#define SOME_USER_SPECIFIC_MACRO_1  ... // current user

Is this possible?

EDIT: Let me clarify. Say each programmer defines a different CURRENT_USER in their config header. I want user-specific macros to expand to something meaningful if and only if their user argument matches CURRENT_USER. As I would like those macros to contain _pragmas, it can't be runtime check (as proposed in some answers below).

EDIT: Again, clarification. Say there's a macro to disable the optimisation of some sections of code:

#define TURN_OPTIMISATION_OFF __pragma optimize("", off)

Some programmers want to turn optimisation off for different sections of code but not all at one time. What I'd like is to have a macro:

#define TURN_OPTIMISATION_OFF(user) /* magic */

That will match the user argument against the CURRENT_USER macro, taken from the per-programmer config file. If the user matches, the macro is expanded into pragma. If not, to nothing.


Solution

  • It turns out it is possible. This answer is based on Paul's macros, but it is much simpler and does not need a definition for each user.

    #define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
    #define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
    
    #define IIF(c) PRIMITIVE_CAT(IIF_, c)
    #define IIF_0(t, ...) __VA_ARGS__
    #define IIF_1(t, ...) t
    
    #define PROBE(x) x, 1 
    

    Now, because of the MSVC bug, I had to modify the CHECK macro a bit.

    #define MSVC_VA_ARGS_WORKAROUND(define, args) define args
    #define CHECK(...) MSVC_VA_ARGS_WORKAROUND(CHECK_N, (__VA_ARGS__, 0))
    #define CHECK_N(x, n, ...) n
    

    Instead of defining CURRENT_USER, I switched to the following macros.

    #define ENABLE_USER_gwiazdorrr () // gwiazdorrr is now enabled
    #define ENABLE_USER_foo ()        // foo is also enabled
    // #define ENABLE_USER_bar ()     // bar is NOT enabled
    

    It actually gives more flexibility because one can enable multiple users at the same time. The parenthesis is required. The macros below actually detect, whether ENABLE_USER_<user> is expanded into parenthesis or not.

    #define USER_ENABLED_PROBE(user)            USER_ENABLED_PROBE_PROXY( ENABLE_USER_##user ) // concatenate prefix with user name
    #define USER_ENABLED_PROBE_PROXY(...)       USER_ENABLED_PROBE_PRIMIVIE(__VA_ARGS__)       // expand arguments
    #define USER_ENABLED_PROBE_PRIMIVIE(x)      USER_ENABLED_PROBE_COMBINE_##x                 // merge
    #define USER_ENABLED_PROBE_COMBINE_(...)    PROBE(~)                                       // if merge successful, expand to probe
    
    USER_ENABLED_PROBE(gwiazdorrr) // expands to ~, 1
    USER_ENABLED_PROBE(bar)        // expands to USER_ENABLED_PROBE_COMBINE_bar
    

    From now it is child's play:

    #define IS_USER_ENABLED(user) CHECK(USER_ENABLED_PROBE(user))
    
    IS_USER_ENABLED(gwiazdorrr)   // expands to 1
    IS_USER_ENABLED(bar)          // expands to 0
    

    Having this macro and IIF (thanks Paul!) I decided to implement the optimisation macro mentioned in the original question:

    #define TURN_OPTIMISATION_OFF(user) IIF( IS_USER_ENABLED(user) ) \
        (\
            __pragma optimize("", off),\
            /* nothing */ \
        )
    
    TURN_OPTIMISATION_OFF(gwiazdorrr) // expands into __pragma optimize("", off)
    TURN_OPTIMISATION_OFF(foo)        // expands into __pragma optimize("", off)
    TURN_OPTIMISATION_OFF(bar)        // nothing emitted
    

    Thanks for input!

    EDIT: here's the GCC version: http://ideone.com/129eo