Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
136 views
in Technique[技术] by (71.8m points)

c++ - 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 user passed matches CURRENT_USER. Mind you that I don't know all possible user 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 macro like that every other macro relying on the username could be done 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 different CURRENT_USER in their config header. I want user specific macros to exand 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 anwsers below).

EDIT: Again, clarification. Say there's macro to disable 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 user argument against CURRENT_USER macro, taken from per-programmer config file. If the user matches macro is expanded into pragma. If not, to nothing.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Well first, you can do pattern matching with the preprocessor using the ##. This is how an IIF macro could be defined:

#define IIF(cond) IIF_ ## cond
#define IIF_0(t, f) f
#define IIF_1(t, f) t

However there is one problem with this approach. A subtle side effect of the ## operator is that it inhibits expansion. Heres an example:

#define A() 1
//This correctly expands to true
IIF(1)(true, false) 
// This will however expand to IIF_A()(true, false)
// This is because A() doesn't expand to 1,
// because its inhibited by the ## operator
IIF(A())(true, false) 

The way to work around this is to use another indirection. Since this is commonly done, we can write a macro called CAT that will concatenate without inhibition.

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__

So now we can write the IIF macro:

#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t

#define A() 1
//This correctly expands to true
IIF(1)(true, false) 
// And this will also now correctly expand to true
IIF(A())(true, false)

With pattern matching we can define other operations, such as COMPL which takes the complement:

// A complement operator
#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0
// An and operator
#define BITAND(x) PRIMITIVE_CAT(BITAND_, x)
#define BITAND_0(y) 0
#define BITAND_1(y) y

Next, detection techniques can be used to detect if the parameter is a certain value or if it is parenthesis. It relies on variadic arguments expanding to different number of parameters. At the core of detection is a CHECK macro with a PROBE macro like this:

#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
#define PROBE(x) x, 1,

This is very simple. When the probe is given to the CHECK macro like this:

CHECK(PROBE(~)) // Expands to 1

But if we give it a single token:

CHECK(xxx) // Expands to 0

So with this, we can create some detection macros. For instance, if we want to detect for parenthesis:

#define IS_PAREN(x) CHECK(IS_PAREN_PROBE x)
#define IS_PAREN_PROBE(...) PROBE(~)
IS_PAREN(()) // Expands to 1
IS_PAREN(xxx) // Expands to 0

Next, we need to do a comparison of two tokens, we can rely on the fact that macros don't expand recursively. We force the macro to expand recursively inside of the other macro. If the two tokens are the same then the it will be expanding the macros recursively, which we will detect by trying detect if they expanded to parenthesis or not, here is the COMPARE macro:

#define COMPARE(a, b) PRIMITIVE_COMPARE(a, b)
#define PRIMITIVE_COMPARE(a, b) 
    IIF( 
        BITAND 
            (IS_PAREN(COMPARE_ ## a(()))) 
            (IS_PAREN(COMPARE_ ## b(()))) 
    )( 
        COMPL(IS_PAREN( 
            COMPARE_ ## a( 
                COMPARE_ ## b 
            )(()) 
        )), 
        0 
    ) 

Each token you want to compare you would define like this:

// So you would define one for each user
#define COMPARE_john_smith(x) x
#define COMPARE_another_user_name(x) x

Now, I don't fully understand the final output you want generated, so say you have a macro for generating code for the current user and one for other users:

#define MACRO_CURRENT_USER(user) ...
#define MACRO_OTHER_USER(user) ...

Then you can write something like this:

// Detects if its the current user
#define IS_CURRENT_USER(user) COMPARE(user, CURRENT_USER)
// Your macro
#define MACRO(user) IIF(IS_CURRENT_USER(user))(MACRO_CURRENT_USER, MACRO_OTHER_USER)(user)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...