Reputation: 35
I have the following macros that take a statement and multiply it
#define X2(s) do { s; s; } while (0)
#define X3(s) do { s; s; s; } while (0)
#define X4(s) do { s; s; s; s; } while (0)
#define X5(s) do { s; s; s; s; s; } while (0)
#define X6(s) do { s; s; s; s; s; s; } while (0)
etc.
so the pre-processor expands
X2(i++)
to
do { i++; i++; } while (0)
I would like to have a macro
#define CODE_MULTIPLIER(x,s)
which expands a statement s
, x
times. so
CODE_MULTIPLIER(3,j--)
would be expanded to
do { j--; j--; j--; } while (0)
The ugly idea i came up with is:
#define CODE_MULTIPLIER(x,s) do { \
if ((x) == 1) { s; } \
else if ((x) == 2) { s; s; } \
else if ((x) == 3) { s; s; s; } \
else if ((x) == 4) { s; s; s; s; } \
else if ((x) == 5) { s; s; s; s; s; } \
etc. \
else assert(0); \
} while (0)
hoping the compiler will optimize out the if
s
Why would you ever want macros like this? One reason (but not the only one) is the need for exact delays in embedded programming. using while/for loops slightly change the timing. Compiler optimization of loops may not preserve delicate timing requirements.
Here is a typical usage example:
#define CLOCK_FREQ_Hz (16000000L)
#define US_TO_NOPS(us) ((us) * CLOCK_FREQ_Hz / 1000000L)
#define OFF_ON_DELAY_US (4) // units: microseconds
#define ON_OFF_DELAY_US (2) // units: microseconds
#define OFF_ON_DELAY_NOPS US_TO_NOPS(OFF_ON_DELAY_US) // units: instructions
#define ON_OFF_DELAY_NOPS US_TO_NOPS(ON_OFF_DELAY_US) // units: instructions
PIN = OFF;
CODE_MULTIPLIER(OFF_ON_DELAY_NOPS,asm("nop")); // 4us delay
PIN = ON;
CODE_MULTIPLIER(ON_OFF_DELAY_NOPS,asm("nop")); // 2us delay
PIN = OFF;
I would appreciate any suggestions as to how to create this macro. High preference to compiler-independent macro wizardry (Us embedded dudes don't always have the luxury of using GCC)
Thanx
Upvotes: 1
Views: 306
Reputation: 1434
As recursion cannot be generally used in macros, and you may not have "extended unrolling features" the only way to have a "always valid solution" is to do some manual work like following one:
#define X1(s) do { s;} while (0)
#define X2(s) do { s; s; } while (0)
#define X3(s) do { s; s; s; } while (0)
#define X4(s) do { s; s; s; s; } while (0)
#define X5(s) do { s; s; s; s; s; } while (0)
#define X6(s) do { s; s; s; s; s; s; } while (0)
#define CODE_MULTIPLIER(s, i) X##i(s)
And you just use CODE_MULTIPLIER(s, i)
in your code. In this way you avoid the if/else if conditional expressions. For example you will use CODE_MULTIPLIER(i++,3)
.
Another trick can be applied if you aren't going to use X1,X2.... but only CODE_MULTIPLIER(s,i):
#define X1(s) s
#define X2(s) s; X1(s)
#define X3(s) s; X2(s)
#define X4(s) s; X3(s)
#define X5(s) s; X4(s)
#define X6(s) s; X5(s)
#define CODE_MULTIPLIER(s, i) do { X##i(s); } while (0)
which has a nicer sintax.
Upvotes: 1
Reputation: 78903
Yes such things are possible if you don't stretch things too much. P99 has a whole series of macros to accomplish different sorts of such unrolling. An easy one here would be
#define toto(I) f(I)
P99_UNROLL(toto, 3); // => f(0); f(1); f(2);
But beware that for using P99 you'd need a compiler that implements C99 compliant preprocessor.
Upvotes: 1