13

I want to declare pin definition in global header as a simple line like:

#define STATUS_LED B,7

Then I want to pass this pin definition to function above:

CMBset_out(STATUS_LED);

I don't know how to approach this - MY_PIN is in proper format to be replaced during precompilation phase.

#define CMBsbi(port, pin) (PORT##port) |= (1<<pin)
#define CMBset_out(port,pin) (DDR##port) |= (1<<pin)
// define pins 
#define STATUS_LED B,7

Then, I want to pass this pin definition to function above (hw_init_states() is declared in the same header file called from main C file):

// runtime initialization
void hw_init_states(){
#ifdef STATUS_LED
    CMBset_out(STATUS_LED);
#endif
}

But I get a compilation error:

Error   1   macro "CMBset_out" requires 2 arguments, but only 1 given   GENET_HW_DEF.h  68  23  Compass IO_proto
t3m2
  • 366
  • 1
  • 15
bajtec
  • 145
  • 6

2 Answers2

7

It is possible, but you need another level of macros to expand the argument:

#define CMBset_out_X(port,pin) (DDR##port) |= (1<<pin)
#define CMBset_out(x) CMBset_out_X(x)

Of course this means that you can't use the CMBset_out macro with two explicit arguments.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • @mosvy , can you elaborate more ? how this construction works ? – bajtec Jul 26 '19 at 09:06
  • Can you explain how exactly that works ? why do i need two level macro that seems doing nothing? – bajtec Jul 26 '19 at 09:07
  • 4
    @bajtec: because the preprocessor runs in passes; the first pass replaces `CMBset_out(STATUS_LED)` with `CMBset_out_X(B,7)`, and then the 2nd pass replaces the `CMBset_out_X` macro. – vgru Jul 26 '19 at 09:16
7

An improvement upon the previous answer, which also allows you to call the macro with two explicit arguments.

It should work with any c99 (or better) compiler:

#define CMBset_out_X(port,pin) (DDR##port) |= (1<<pin)
#define CMBset_out(...) CMBset_out_X(__VA_ARGS__)

#define STATUS_LED B,7
CMBset_out(STATUS_LED)
CMBset_out(B, 7)
  • @bajtec basically, the arguments to a function-like macro are replaced as-is (without being macro-expanded) in the macro's "replacement list" (right side), and then the result is recursively scanned for _other_ macros. The arguments are "lazily evaluated" ie they're expanded after, not before they were passed to a macro. Sorry if that's not the best explanation ;-) –  Jul 26 '19 at 09:24
  • Almost... "the arguments to a function-like macro are replaced as-is (without being macro expanded)" ...if the parameter in the replacement list is being pasted/stringified. E.g. in this CMBset_out, `__VA_ARGS__` is neither, `STATUS_LED` is expanded (6.10.3.1) yielding `B,7` before the result's rescanned, which is what makes this solution work (without this, you would have `CMBset_out_X(STATUS_LED)` being rescanned, which we already know doesn't work!) – H Walters Jul 26 '19 at 13:11
  • @HWalters You're right, I've skipped a step. But the base is right -- the arguments are still expanded _after_ they were passed to a macro -- otherwise it would be too late to forego expanding them when they're pasted/stringified in the replacement list. –  Jul 27 '19 at 18:34