cc-preprocessorpreprocessor

Is there any way to get the C precompiler to use a mathematical result when concatenating symbols?


I have a C macro that concatenates bits together in order to resolve to other macros. I'll demonstrate using an admittedly pointless macro called MULT which multiplies two numbers. So long as I pass in an actual literal integer to its first parameter it works. But passing in a mathematical expression triggers an error.

For example:

#define MULT_2(X) X+X
#define MULT_3(X) X+X+X
#define MULT_4(X) X+X+X+X

#define EXPAND_(A) A
#define MULT__(N, X) EXPAND_(MULT_ ## N)(X)
#define MULT_(N, X) MULT__(N, X)
#define MULT(N, X) MULT_(EXPAND_(N), X)

int main() {
    int num;

    // This works:
    num = MULT(4, 1);
    printf("num: %d\n", num);

    // This does not:
    num = MULT(2+2, 1);
    printf("num: %d\n", num);

    return 0;
}

(To be honest, I don't fully understand why getting this macro to work with just a literal integer requires three extra macros, MULT_, MULT__, and EXPAND_. But regardless...)

Is there anyway to rewrite this so that passing in an expression like 2+2 or 1 * 4 or 1 << 2 to its first argument will actually work?

I'm curious about the answer mainly to broaden my knowledge of C and its preprocessor. But I'm also hoping to make use of this for a project that's being written in C89, which means no variadic macros, so if there's a solution that avoids using those, I'd love to see it!


Solution

  • This is, for all intents and purposes, impossible. C macro token concatenation (##) does not evaluate expressions, it only merges tokens, as the name implies.

    This is what the macro you've created expands to when trying to add a mathematical expression in:

    #define MULT_2(X) X + X
    #define MULT_3(X) X + X + X
    #define MULT_4(X) X + X + X + X
    
    #define EXPAND_(A)   A
    #define MULT__(N, X) EXPAND_(MULT_##N)(X)
    #define MULT_(N, X)  MULT__(N, X)
    #define MULT(N, X)   MULT_(EXPAND_(N), X)
    
    // MULT(2+2, 1)
    MULT_2 + 2(1)
    

    This is obviously not the desired effect.