14

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.

alice
  • 141
  • 1
  • 3

3 Answers3

6

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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
5

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.

Community
  • 1
  • 1
Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
  • If you mean call it using `untouchable(mrNess, a, b, c)`, that is precisely what we can't do. We need to use a `#define` in the call. – alice Jul 12 '16 at 20:04
  • What do you mean you can't do that? What hinders you? – Rudy Velthuis Jul 12 '16 at 20:06
  • We are refactoring a large codebase, and part of that is to make these macros easier to understand. If we use `a, b, c` it is more complex than using `first` (in reality I have simplified it here, there are actually 13 arguments, so it would be `a, b, c, d, e, f, ..., m` as opposed to simply `first`). – alice Jul 12 '16 at 20:12
  • @alice see [here](https://github.com/MirBSD/mksh/blob/03ae72e654c218cf85064da8b763e482b4218cba/mbsdint.h#L392) for a practical example – mirabilos Feb 26 '22 at 06:19
1

C11, 6.10.3.1 Argument Substitution:

After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.

Look at how this is expanded:

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

untouchable(foo,first,c,d)

into:

$ gcc -E foo.c
...

wchar_t foo[] = { quote(a,b,c), quote(c), quote(d) }  

First the arguments are identified, so the untouchable parameter first becomes c1. Only after they are identified are they are expanded and then substituted into the macro body. first -> c1 -> a,b,c. Now in the macro body c1 is replaced by a,b,c.

I skipped the quote macro from the question because it is illegal on my compiler.

evaitl
  • 1,365
  • 8
  • 16
  • This is what confuses me when I read the documentation. All those substitutions and expansions! If I am reading what you have written right though, are you saying at the point the `a,b,c` from `first` gets to `untouchable` they are unalterably a single token? – alice Jul 12 '16 at 20:20
  • Yes. If you try `untouchable(foo,first)` you get an error because the macro only sees two parameters but was expecting 4. – evaitl Jul 12 '16 at 20:23