Is there a way to control macro expansion order

2019-02-25 09:51发布

I am hoping that someone may have an idea on how to control/specify the order of macro expansion. Here is the context:


// 32 bit increments, processor has registers for set, clear and invert
#define CLR_OFF 1
#define SET_OFF 2
#define INV_OFF 3


#define SET(reg,bits) *((volatile unsigned long*)(& reg+SET_OFF)) = bits
//Now if I use this I can do it quite nicely with
#define STATUS_LED 0x0040;
SET(LATB, STATUS_LED); // LATB is port of the LED.

I've actually had to move hardware around quite a bit as of late so I decided to group the LATB info with the STATUS_LED like so...


#define STATUS_LED_PORT LATB
#define STATUS_LED_MASK 0x0040;
#define STATUS_LED STATUS_LED_PORT, STATUS_LED_MASK

//And I try to use it via
SET( STATUS_LED );

But alas, LATB,0x0040 is passed to argument 1 of the SET macro. When not used as a macro this method works properly:


inline void SET(u32_t *reg, u32_t bits) { ((volatile u32_t *) (((u32_t)reg) + SET_OFF*4 )) = bits; }
//Change the STATUS_LED macro to
#define STATUS_LED &STATUS_LED_PORT, STATUS_LED_MASK
SET( STATUS_LED); //Works great!

But unfortunately my compiler doesn't see the need to inline the function and causes 6 instructions to set the register as opposed to 4, so for use while bit-banging it is unpredictable.

I am hoping someone may know of a way to expand the STATUS_LED macro first, something like: SET( ##STATUS_LED )

Currently my solution to move on is to have two macros SET, and SETRM (set register, mask) but I feel like there should be a solution because the code for SET looks like...


#define SETRM(reg,bits) ...
#define SET(args) SETRM(args) //WHY WOULD THIS GET EXPANDED HERE??

And lastly, my processor's compiler does not support n-arguments to a macro, I thought I might be able to play with that but alas :(.

Thank you very much for your time, and I would appreciate any thoughts, I can move forward, but it would be so much cleaner if I could just use SET everywhere.

2条回答
淡お忘
2楼-- · 2019-02-25 10:01

Substitution of parameters in the expansion of function-like macros happens in a set way. All arguments which don't appear after the # operator or either side of a ## are fully macro expanded when they are replaced, not before the function-like macro is expanded.

This means that to make a single macro become two macro arguments a round of macro substitution must occur before the required function like macro is itself expanded.

This means that the solution of a second function like macro that expands to the desired function-like macro is the simplest solution.

i.e. given your original SET definition

#define SET(reg,bits) *((volatile unsigned long*)(& reg+SET_OFF)) = bits

and a macro that expands to two potential arguments

#define STATUS_LED_PORT LATB
#define STATUS_LED_MASK 0x0040;
#define STATUS_LED STATUS_LED_PORT, STATUS_LED_MASK

You have to use another function-like macro to get the substitution that you need.

e.g.

#define SET2(x) SET(x)

Then SET2( STATUS_LED ) expands as follows.

SET( LATB , 0x0040; )

then

*((volatile unsigned long*)(& LATB + 2 )) = 0x0040;

This isn't valid as there are not enough arguments to the SET macro; the parameters are matched to arguments before any expansion of the argument occurs. My compiler generates an error; the behaviour isn't defined.

SET( STATUS_LED )
查看更多
放我归山
3楼-- · 2019-02-25 10:24

If the root name is always the same you could use:

#define SET_COMPOSITE(root) SET(root##_PORT, root##_MASK)
查看更多
登录 后发表回答