Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c preprocessor passing multiple arguments as one

The C preprocessor is passing multiple arguments as if they were a single argument. I feel pretty sure the problem is how we are calling the untouchable macro, but every attempt we have made to alter the first macro has failed to produce the desired result. Here is a complete code sample with comments that explain what happens and what we want to happen:

//this can be changed, but it must remain a #define to be of any use to us
#define first a,b,c

// v all code below this line cannot be altered (it's outside of our control)

#define untouchable(name, c1, c2, c3) \
    wchar_t name[] = \
    { \
        quote(c1), \
        quote(c2), \
        quote(c3) \
    }

#define quote(c) L#@c

// ^ all code above this line cannot be altered (it's outside of our control)

int _tmain(int argc, _TCHAR* argv[])
{
    static untouchable(mrNess, first);

    // the line above is precompiled to:
    // static wchar_t mrNess[] = { L'a,b,c', L, L };

    // whereas we want:
    // static wchar_t mrNess[] = { L'a', L'b', L'c' };

    return 0;
}

We are compiling in Windows under VisualStudio.

like image 649
alice Avatar asked Jul 12 '16 19:07

alice


People also ask

What is __ Va_args __ C++?

macro expansion possible specifying __VA_ARGS__ The '...' in the parameter list represents the variadic data when the macro is invoked and the __VA_ARGS__ in the expansion represents the variadic data in the expansion of the macro. Variadic data is of the form of 1 or more preprocessor tokens separated by commas.

What is __ Va_opt __?

__VA_OPT__ is a new feature of variadic macros in C++20. It lets you optionally insert tokens depending on if a variadic macro is invoked with additional arguments. An example usage is comma elision in a standardized manner.

Can you supply more than one argument in a macro call?

The invocation of the macro need not be restricted to a single logical line—it can cross as many lines in the source file as you wish. The number of arguments you give must match the number of parameters in the macro definition.

What is parameterized macro in C?

A parameterized macro is a macro that is able to insert given objects into its expansion. This gives the macro some of the power of a function. As a simple example, in the C programming language, this is a typical macro that is not a parameterized macro: #define PI 3.14159.


2 Answers

The problem seems to be how you define first. You pass first to the untouchable macro, but it is obviously not expanded at that time.

So do not use the first macro like that and pass a, b, and c as single arguments to the macro. Then you will get what you want.

Update

But there is some hope:

#define UT_HELPER(a,b,c,d) untouchable(a,b,c,d)
#define UT(x, y) UT_HELPER(x, y)
#define first a, b, c
static UT(mrNess, first);

It is similar to the solution found in this stack overflow answer, so I can't claim having thought it up myself.

Note that

#define quote(s) L#@s

doesn't work in my compiler, so I can't fully test it. I simply tried with

#define quote(s) L###s

and that worked.

I hope this gets you going for the real, more complicated macros you seem to have.

like image 112
Rudy Velthuis Avatar answered Oct 06 '22 01:10

Rudy Velthuis


When the preprocessor encounters the invocation of a function-like macro, it first identifies the arguments, next completely macro-expands them*, and last replaces the macro invocation with its replacement list, with the expanded arguments inserted at the appropriate places. The expansion of macro first is (or would be) performed too late for the preprocessor to recognize in the expansion separate arguments to macro untouchable(). You'll need to find another way.

One possibility is to insert a level of indirection:

#define untouch_wrap(name, args) untouchable(name, args)

static untouch_wrap(mrNess, first);

There, first is expanded before untouch_wrap, so that when the result is re-scanned for further macros to replace, the resulting invocation of untouchable() has the correct number of arguments.

That does rely on the second argument to untouch_wrap() to expand to a comma-separated list of exactly three members. It would be cleaner and more flexible to define untouch_wrap() like this:

#define untouch_wrap(name, ...) untouchable(name, __VA_ARGS__)

... but MSVC's handling of variadic macros is non-conforming, and I suspect that it would trip over that.


* Expansion is suppressed for arguments that are operands of the preprocessor's stringification (#) and token-pasting (##) operators.

like image 45
John Bollinger Avatar answered Oct 05 '22 23:10

John Bollinger