Nasha Nasha - 2 months ago 8x
C Question

Why does avr-gcc tell me about missing argument while expanding my [fancy] macro?

I'm developing code for Atmel, which defines consistent register names to program port pins. For instance:

  • PORTC is traditionally used to set (high or low) any pin on port C

  • PINC is used to read the state of a particular pin on port C

  • DDRC is used to read/write the direction (0=input, 1=output) of any pin on port C

So I found some code — which I fully understand — that defines a PIN macro like this:

#define LED_PIN C,7

Then the following macros (restricted to the use case I'm talking about here):

#define _BV(bit) (1 << bit)
#define _SET(type, name, bit) type ## name |= _BV(bit)
#define _CLEAR(type, name, bit) type ## name &= ~ _BV(bit)
#define _TEST(type, name, bit) ( type ## name & _BV(bit) )
#define OUTPUT(pin) _SET(DDR, pin)
#define INPUT(pin) _CLEAR(DDR, pin)
#define HIGH(pin) _SET(PORT, pin)
#define LOW(pin) _CLEAR(PORT, pin)

So I can write a function
as follows:

main() {

While it is very convenient and prevents from defining three macros (e.g. PINC7, PORTC7 and DDRC7) for just one LED, my main concern about this is that is imposes to also redefine AVR macros to account for that kind of notation, bearing in mind AVR macros use SFR registers as arguments (from sfr_defs.h):

#define bit_is_set(sfr, bit) (_SFR_BYTE(sfr) & _BV(bit))

Also, AVR defines macros to convert from [what I'd call] bare registry names to actual memory-mapped special-function register names (sfr) for

#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr))

adds an offset 0 or 20 according to the target Atmel CPU. So in order to enhance compatibility with those AVR library functions, which take an SFR register plus an optional argument, I decided to rewrite the initial code and tried the following:

#define _SFR_BIT(type, name, bit) type ## name, bit
#define _PORT(pin) _SFR_BIT(PORT, pin)
#define _PIN(pin) _SFR_BIT(PIN, pin)
#define _DDR(pin) _SFR_BIT(DDR, pin)

#define set_bit(sfr, bit) _SFR_BYTE(sfr) |= _BV(bit)
#define set_output(pin) set_bit(_PIN(pin))

but then I face a compiler error message as soon as I write something like this in function


main.cpp:72:19: error: macro "set_bit" requires 2 arguments, but only 1 given

I get the same error if I try this, too:

#define set_output(pin) set_bit(_SFR_BIT(DDR, pin))

Why is that macro
, which passes only two out of of three arguments to
compiles fine and my macro doesn't?

As Jens Gustedt pointed to, the generic explanation lies in the order of which the compiler resolves macros.


The code is not yet in production. The code may be ugly, of course. It's mostly some base work under construction. Hence not yet finalized.


The decomposition of the arguments for set_bit into several takes place before the _SFR_BIT macro is expandend. So when it then sees the comma that resulted from the expansion of the latter, it is already too late.

A common trick is to expand the argument once more

#define set_bit0(...)   set_bit(__VA_ARGS__)
#define set_output(pin) set_bit0(_PIN(pin))

Here _PIN(pin) is expanded and passt to set_bit0. When passing the arguments from set_bit0 to set_bit it sees the already expandend sequence, including the comma.