C Macro Token Concatenation involving a variable -

2020-02-11 02:29发布

问题:

I'm trying to define a macro to generate a token name, containing a variable.

Basically, what I'm trying is this:

#define GLUER(x,y,z) x##y##z
#define PxDIR(x) GLUER(P,x,DIR)

int main() {
  int port;
  port = 2;
  PxDIR(port) |= 0x01;
}

I'm hoping to generate the token P2DIR in the above statement, but according to my compiler output, it's generating the token PportDIR, which is NOT what I wanted. Any help here? Or is what I'm attempting to do impossible?

回答1:

I don't think what you're trying to do is possible. C macros are really preprocessor macros that are expanded before compilation. The variable port, doesn't get set until runtime.



回答2:

It is impossible. C preprocessors work by processing tokens, and they do not do any resolution or substitution that would require knowledge of the mechanics of the language (except basic arithmetic involving integer literals, off the top of my head). Consider, for example, the docs for GCC's preprocessor regarding tokenisation. Only a compiler will know what to do with the variable "port."

One solution is to do something like:

#define PxDIR(var, portnum) do { \
    var = portnum; \
    P##portnum##DIR |= blah; \
} while(0)

...and later...

int port;
PxDIR(port, 2);

I leave it to you to make this not as ugly or hacky as it is here (and more general, depending on what you need) :)



回答3:

... or just make PORT also a macro:

#define PORT 2
#define GLUER(x,y,z) x##y##z
#define PxDIR(x) GLUER(P,x,DIR)

int main() {
    PxDIR(PORT) |= 0x01;
    return 0;
}


回答4:

What you're trying to do doesn't make sense.

#define GLUER(x,y,z) x##y##z
#define PxDIR(x) GLUER(P,x,DIR)

int main() {
  int port;
  port = 2;
  PxDIR(port) |= 0x01;
}

The preprocesser is run at (before) compile time. Therefore it can't know anything about the contents of the variable port. The preprocessor requires that any values passed as arguments to macros be constants. For example, you could do the following:

#define GLUER(x,y,z) x##y##z
#define PxDIR(x) GLUER(P,x,DIR)

int main() {
  PxDIR(2) |= 0x01; //setup port 2
}

Otherwise, if you want to be able to pass a variable to this macro really the only way is to make sure the code to do so is explicitly generated:

#define GLUER(x,y,z) x##y##z
#define PxDIR(x) GLUER(P,x,DIR)

uint16_t* get_port_pointer(uint8_t port_id) {
  if (port == 0) {
    return &PxDIR(0);
  } else if (port == 1) {
    return &PxDIR(1);
  } else if (port == 2) {
    return &PxDIR(2);
  } else if (port == 3) {
    return &PxDIR(3);
  } else {
    return &0;
  }
}

int main() {
  int port;
  port = 2;

  *(get_port_pointer(port)) |= 0x01;
}

In this way we make sure there is code for any port from 0 to 3 to be accessed. Also, now we have to watch out for null pointers getting returned from the get_port_pointer function.