Vincent
Vincent

Reputation: 45

Can you feed a macro to another macro as an argument, without the initial macro being expanded?

Background: My code, which I cannot post here will eventually run on a microcontroller, and the macros just offer a way to create multiple pin definition functions, via 1 single macro define mechanic. I use windows and gcc to experiment around with those.

I tried to abstract the problem as much as possible. I use the std console functions cause it is convenient for me to display it in the console window. As such, I also save the file as .cpp and compile it with g++ on windows.

Say I set up my code like this:

#define MACRO2(_x)  foo##_x(_x)
#define MACRO1(_x)  MACRO2(_x)
#define BAR         3   

void fooBAR(int num)
{
    std::cout << num << std::endl;
}

If I run the following code (working example)

int main()
{
    MACRO2(BAR);
    return 0;
}

first BAR gets inserted into ##_x and thus defines the function name which is to be called and then BAR gets inserted as the argument of that function and gets expanded to its value, so we get fooBAR(3). The code works, there are no errors.

Now if I try to add a macro in between (and this is the real world situation I am faced with for reasons I cannot go into), my code looks like this:

int main()
{
    MACRO1(BAR);
    return 0;
}

But this code throws an error, because when MACRO1(BAR) gets substituted with MACRO2(BAR), (BAR) then gets expanded into 3, and MACRO2(3) leads to foo3(3) which isn't defined, as confirmed by the error log:

error: 'foo3' was not declared in this scope

So the requirements are:

  1. I need to pass BAR into MACRO1 and it needs to be passed to MACRO2 without being expanded
  2. The word BAR has to stay exactly as it is, I know I could use ## in order to prevent it from expanding, but then I would need to add a char to BAR and the function call wouldn't work anymore.

Is it possible to somehow get this done? Pass a macro to another macro as an argument, without the initial macro being expanded in the process?

Upvotes: 2

Views: 2293

Answers (2)

n. m. could be an AI
n. m. could be an AI

Reputation: 119877

One way to accomplish this is to change slightly the definition of BAR.

#define MACRO2(_x)  foo##_x(_x())
#define MACRO1(_x)  MACRO2(_x)
#define BAR()       3   

Upvotes: 2

John Bollinger
John Bollinger

Reputation: 180351

But this code throws an error, because when MACRO1(BAR) gets substituted with MACRO2(BAR), (BAR) then gets expanded into 3, and MACRO2(3) leads to foo3(3)

Yes. This is the specified preprocessor behavior for your particular set of macros.

After they are identified, the arguments to a function-like macro are fully macro-expanded before being substituted into the macro's replacement text, except where they are operands of the ## or # preprocessor operator. Any appearances of those operators are evaluated, and then the resulting text is rescanned, along with any following text as appropriate, for additional macros to expand.

Is it possible to somehow get this done? Pass a macro to another macro as an argument, without the initial macro being expanded in the process?

Only where the argument is the operand of a ## or # operator. The latter doesn't help you, but the former affords a workaround: you can pass an additional, empty argument so that you can perform a concatenation without changing the wanted argument:

#define MACRO2(_x)        foo##_x(_x)
#define MACRO1(_x,dummy)  MACRO2(_x##dummy)
#define BAR               3   

int main()
{
    MACRO1(BAR,);
    return 0;
}

That expands to

int main()
{
    fooBAR(3);
    return 0;
}

If you want to avoid the extra comma, then you can do so by making MACRO1 variadic:

#define MACRO2(_x)      foo##_x(_x)
#define MACRO1(_x,...)  MACRO2(_x##__VA_ARGS__)
#define BAR             3   

int main()
{
    MACRO1(BAR);
    return 0;
}

That expands to the same thing as the other.

Do note that both of these approaches afford the possibility of an error being introduced by providing unwanted extra argument values to the top-level macro. One would probably suppose that most such errors would be caught at compile time, as the expansion would result in broken code, like the attempt in the question. But it is hard to rule out the possibility that such an error would coincidentally expand to something that happened to be valid, but wrong.

Upvotes: 3

Related Questions