8

I have this bit of code (part of an interpreter for a garbage-collected Forth system, actually):

#define PRIMITIVE(name) \
    do \
    { \
        VocabEntry* entry = (VocabEntry*)gc_alloc(sizeof(VocabEntry)); \
        entry->code = name; \
        entry->name = cstr_to_pstr(#name); \
        entry->prev = latest_vocab_entry; \
        latest_vocab_entry = entry; \
    } \ 
    while (false)

PRIMITIVE(dup);
PRIMITIVE(drop);
PRIMITIVE(swap);
// and a lot more

but there's a problem: in the line

entry->name = cstr_to_pstr(#name);

the name field is substituted for dup, drop, swap, and the rest. I want the field name to not be substituted.

So, is there any way to solve this, other than simply renaming the macro argument?

For an answer, please explain if there is, in general, a way to suppress the substitution of a macro argument name in the macro body. Don't answer "just do it this way" (please).

a3f
  • 8,517
  • 1
  • 41
  • 46
feralin
  • 3,268
  • 3
  • 21
  • 37
  • Change the *structure* field name? (gotta rename something before this is over, one way or another). – WhozCraig Aug 11 '13 at 01:59
  • 2
    @JimBalter I was only offering up n alternative since the OP placed a restriction on the potential solutions. Not the first time I've seen that, and although I concur renaming the argument is *correct*, it is certainly not exclusively so. I also agree that down-voting answers by the OP is not good move, to be sure. – WhozCraig Aug 11 '13 at 02:04
  • Back to the question, since the preprocessor is doing all of this, I'm not ashamed to say I can think of no way to "tell" the cpp that it should *not* substitute one or more occurrences of the very thing it is *designed* for. In short, either the name of the macro param can be changed or the item being substituted with offense can be so. Note: Elimination is also possible, but *highly* discouraged (i.e., you can skirt with the boundaries of the standard, though still stay within it, with some indirection manipulation if the structure field in question is the *first* member of the structure. – WhozCraig Aug 11 '13 at 02:09
  • @JimBalter do you really expect me to remove my downvote just because _you_ think you answered my question? You clearly did not; otherwise I would not have downvoted. – feralin Aug 11 '13 at 02:10
  • @WhozCraig I downvoted Jim Balter's answer because it did not answer my question. There is no other reason. – feralin Aug 11 '13 at 02:11
  • That's not the answer that you originally gave me, which you have now edited. I will remove the downvote, because the current version clearly has the correct answer. – feralin Aug 11 '13 at 02:13
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/35207/discussion-between-feralin-and-whozcraig) – feralin Aug 11 '13 at 02:19

3 Answers3

18

You can define a different macro to expand to name, like this:

#define Name name

and change the name field in the PRIMITIVE macro to use the new macro, like this:

#define PRIMITIVE(name) \
    do \
    { \
        VocabEntry* entry = (VocabEntry*)gc_alloc(sizeof(VocabEntry)); \
        entry->code = name; \
        entry->Name = cstr_to_pstr(#name); \
        entry->prev = latest_vocab_entry; \
        latest_vocab_entry = entry; \
    } \ 
    while (false)

Other than using something different from the parameter name in the macro body or changing the parameter name, there is no other way to do this in the C language. Per C 2011 (N1570) 6.10.3.1 1, when a function-like macro is recognized, parameter names are immediately substituted except when # or ## is present, and there no other exceptions:

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.

The # token changes the parameter name to a string, which is no use in this situation. The ## token expands the parameter name and pastes it together with an adjacent token, which is also no use in this situation.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
5

No, there is not.

To see why, you need to consider the way macro expansion actually happens. Expanding a function-like macro requires three main steps:

  1. the arguments to the macro are fully expanded, unless the macro uses the # or ## operators on them (not relevant in the example since they're single tokens)
  2. the entire replacement list is scanned, and any occurrence of a parameter name is replaced by the corresponding argument
  3. after step 2 is complete, the expanded replacement list is itself rescanned, and any macros appearing are expanded at this point

This is outlined in standard section 6.10.3 (C11 and C99).

The upshot of this is that it is impossible to write some kind of macro that can take name and abuse the suppression-rules of '##' or anything like that, because the replacement step in the body of PRIMITIVE must run completely before any of the macros within the body are allowed their turn to be recognised. There is nothing you can do to mark a token within the replacement list for suppression, because any marks you could place upon it will only be examined after the replacement step has already run. Since the order is specified in the standard, any exploit you find that would let you mark a token in this way is a compiler bug.

Best I can suggest if you're really set on not renaming the macro argument is to pass na and me as separate arguments to a concatenation macro; the token name will only be formed after replacement is done and the list is no longer being examined for parameter names.

EDIT wish I typed faster.

Alex Celeste
  • 12,824
  • 10
  • 46
  • 89
1

No, there is no way to suppress the replacement within a macro's body of a token identical to that of a declared argument to said macro. Every possible solution short of jumping into the preprocessor code will require you to rename something, either the argument name or the name of the field (potentially just for purposes of that macro, as Eric's answer does).

Kevin
  • 53,822
  • 15
  • 101
  • 132