user1159503
user1159503

Reputation: 51

Token pasting in C with structure dereference operator

I have a question about '##' for pre-processor pasting with a dereference operator. Could anyone tell me why the code below will not compile?

typedef struct
{
    char data;
} MY_STRUCT;

MY_STRUCT  My_Instance;
MY_STRUCT* My_PInstance;

#if 1
#define GET_MEMBER(membername)         (My_PInstance->##membername)
#else
#define GET_MEMBER(membername)         (My_Instance.##membername)
#endif

Then later when I call:

char value = GET_MEMBER(data);  // Where My_PInstance is properly instantiated.

I get a compile error.

error: pasting "->" and "data" does not give a valid preprocessing token

Upvotes: 3

Views: 1771

Answers (2)

Der Schley
Der Schley

Reputation: 476

Just for anybody else who comes this way. I had the same problem, but with the '.' variant of the GET_MEMBER(..) macro that wasn't discussed, but I think the problem is the same.

I accepted cwyang's answer hesitantly, as what was really getting pasted was the text

My_PInstance->

with

data

With -- per my (and user...'s) thinking -- the result being a proper token.

My_PInstance->data

So, I persisted through more google results. Eventually I came to this guy, who gives a historical note that was a real blow. Summary:

Back in olden tymes, there existed an ancient, alternative syntax, but no compilers recognize that nonsense any more. Don't even go there.

Man. This guy says there's no hope....

Well, what the heck? I went there. The alternative syntax worked, and I'm hardly using an ancient compiler!

g++ (GCC) 4.7.3
Copyright (C) 2012 Free Software Foundation, Inc.

So, for all you intrepid stringifiers and concatenators and tokenizers (!) out there, here is my lesson for you.

Before:

#define PROFILING_START_CLK(STAT, n)                        \
    time0##STAT##n = PRF_CLK_FN();                          \
    profilingStatsIndex##STAT##n = STAT##.count % PROFILE_SIZE; \
    STAT#.count++;

called thus:

PROFILING_START_CLK(tst, 0);

Returns 2 intances of the following error for each place it was used:

error: pasting "tst" and "." does not give a valid preprocessing token

Just like user1159503.

Moving on, though.... The following works:

#define PROFILING_START_CLK(STAT, n)                          \
    time0##STAT##n = PRF_CLK_FN();                            \
    profilingStatsIndex##STAT##n = STAT/**/.count % PROFILE_SIZE;\
---------------------------------------^^^^
    STAT/**/.count++;
--------^^^^

The before/after versions worked failed/compiled, whether I used the g++ -ansi switch or not.

The reason it works is because the preprocessor strips comments before getting into macros, so the order is something like this.

  1. STAT gets abutted to .count when '/**/' gets stripped.
  2. The '##' process is avoided. In particular, its check for a valid resulting token is avoided at this point in the code.
  3. STAT gets stringified to 'tst' (in my case)
  4. Subsequent parsing/token checks find 'tst.count' quite proper and agreeable.

Should '##' work to assemble arbitrary structs &/or members in a macro? I don't know. I would think so, but it also seems correct for the pasting process to not span operators.

Otherwise, the question would be, "Is it reasonable for pasting process to span only "joining" operators like we're talking about here. ('.', '->', '::', ....)"

Upvotes: 2

Chul-Woong Yang
Chul-Woong Yang

Reputation: 1243

You need not paste. Just do (My_Pinstance->membername)

'##' should paste two tokens into one valid token. However ->foo is not valid token. (foo is for example)

Upvotes: 6

Related Questions