Jul3k
Jul3k

Reputation: 1096

Check for concatenated define in C preprocessor

I access registers by concatenated defines unsing the function GETREG

#define NUMBER 1 //changes

#define REG1 register.N1
#define REG2 register.N2
#define REG8 register.N8

#define GETREG_(N) REG ## N
#define GETREG(N) GETREG_(N)

Sometimes the registers for that NUMBER are not defined so i want to make sure the macro correctly expands before inserting it in the code so i tried to do:

#define NUMBER 5

#ifdef GETREG(NUMBER)
GETREG(NUMBER) = 0
#endif

However this always seems to evaluate as true and the compiler prints

Warning:extra tokens at end of #ifdef directive


Background Story:

In my projects I create libraries to interface with a HAL to abstract the hardware level. Often from one project to another the codebasis in the HAL stays exactly the same but just the location of pins changes. For that reason i would like to use macro-expansion to access the pins. The following macro does the job for me at adressing the analog features of the pin:

#define ANSEL_(Pin)  _ANS ## Pin
#define ANSEL(...) ANSEL_(__VA_ARGS__)

that way i can turn on or off analog features by:

#define PIN_RX A0
ANSEL(PIN_RX)= 0;

and other registers by similar macros. The problem i am facing now is that some pins for example do not have analog features (e.g. Pin A5). Because of that i would like to test if the define in the library esists. I was tryin to do this by:

#ifdef ANSEL(PIN_RX)
ANSEL(PIN_RX)= 0;
#endif

However this simple approach is not working.

The microcontroller (PIC33) lib: Edit: Shipped from the manufactorer

#define ANSELA ANSELA
extern volatile unsigned int  ANSELA __attribute__((__sfr__));
typedef struct tagANSELABITS {
  unsigned ANSA0:1;
  unsigned ANSA1:1;
  unsigned ANSA2:1;
  unsigned ANSA3:1;
  unsigned ANSA4:1;
  unsigned :4;
  unsigned ANSA9:1;
} ANSELABITS;
extern volatile ANSELABITS ANSELAbits __attribute__((__sfr__));

/* ANSELA */
#define _ANSA0 ANSELAbits.ANSA0
#define _ANSA1 ANSELAbits.ANSA1
#define _ANSA2 ANSELAbits.ANSA2
#define _ANSA3 ANSELAbits.ANSA3
#define _ANSA4 ANSELAbits.ANSA4
#define _ANSA9 ANSELAbits.ANSA9

Upvotes: 1

Views: 495

Answers (2)

rici
rici

Reputation: 241971

You can do more or less what you want if you are prepared to use two #defines instead of one for each pin:

#define HAS_ANS_A0 1
#define ANS_A0 ANSELAbits.ANSA0
#define HAS_ANS_A1 1
#define ANS_A1 ANSELAbits.ANSA1
#define HAS_ANS_A5 1
#define ANS_A5 ANSELAbits.ANSA5

#define HAS_ANSEL_(Pin)  HAS_ANS_ ## Pin
#define HAS_ANSEL(...) HAS_ANSEL_(__VA_ARGS__)
#define ANSEL_(Pin)  ANS_ ## Pin
#define ANSEL(...) ANSEL_(__VA_ARGS__)

#if HAS_ANSEL(PIN_RX)
ANSEL(PIN_RX)= 0;
#endif

This works because in an #if, an undefined identifier token (that is, an identifier token which has not been #defined) has the value 0. It is not an error, or even a warning.

Note: I changed the symbols starting _ANS to conform with §7.1.3 (Reserved Names), paragraph 1:

All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.

Upvotes: 0

John Bollinger
John Bollinger

Reputation: 181932

You have the wrong expectation for the behavior of the #ifdef directive. That directive is equivalent to #if defined, where defined is a preprocessing operator in that context. Although the condition of an overall #if directive is evaluated only after the line is fully macro-expanded, the defined operator operates on the next preprocessing token, which must have the form of an identifier, before macro expansion:

Prior to evaluation, macro invocations in the list of preprocessing tokens that will become the controlling constant expression are replaced (except for those macro names modified by the defined unary operator) [...]

[C2011, 6.10.1/4]

Thus, your

#ifdef GETREG(NUMBER)

tests whether GETREG is a defined macro, and the (NUMBER) constitutes a sequence of unexpected extra tokens.

The defined operator therefore cannot serve your purpose. It is conceivable that there is a way to use a more general #if directive to achieve your aim, but I'm not seeing it at the moment. Instead, I'm inclined to suggest letting the compiler find such errors for you, instead of expecting the preprocessor to do it. If compile time is too late, then perhaps you need to look to a build configuration system such as the Autotools or CMake could help you create.

Upvotes: 0

Related Questions