13

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.

2
  • What does @ do in L#@c? Commented Jul 12, 2016 at 20:05
  • 3
    It surrounds its argument in single quotes as opposed to double quotes. It's Microsoft specific. Commented Jul 12, 2016 at 20:09

3 Answers 3

5

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.

Sign up to request clarification or add additional context in comments.

Comments

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.

4 Comments

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.
What do you mean you can't do that? What hinders you?
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 see here for a practical example
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.

2 Comments

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?
Yes. If you try untouchable(foo,first) you get an error because the macro only sees two parameters but was expecting 4.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.