Embedded Programmer
Embedded Programmer

Reputation: 35

C Macro Wizardry for multiplying a statement N times

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 ifs

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

Answers (2)

Jekyll
Jekyll

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

Jens Gustedt
Jens Gustedt

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

Related Questions