C: Pass two values ​​separated by commas using one #define

Problem

Programming reusable modules for microcontrollers (AVR in my case) requires flexibility in common IO pins. Each pin is identified by a letter (AG) and a number (0-7). However, it is controlled by one bit in the same position in three registers. Thus, the configuration file should contain four entries (3 pointers to registers + 1 position), which is not so elegant.

An easy solution is to simply accept it, but since it is such a common problem, it deserves at least a little attention.


Idea

It would be nice if the precompiler did the repetitive work as follows:

//CONFIGURATION #define IO_NAME B5 //MACRO #define PORT_(ID) #define PIN_(ID) #define DDR_(ID) #define BIT_(ID) 

The results should look like this:

 PORT_(IO_NAME) => PORTB PIN_(IO_NAME) => PINB DDR_(IO_NAME) => DDRB BIT_(IO_NAME) => 5 

The resulting expressions are defined in AVR Studio.


What i tried

I could not figure out how to ignore either the letter and not the number, so instead I tried concatention:

 #define PORT_(REG, BIT) PORT_2(REG, BIT) #define PIN_(REG, BIT) PIN_2(REG, BIT) #define DDR_(REG, BIT) DDR_2(REG, BIT) #define PORT_2(REG, BIT) (PORT ## REG) #define PIN_2(REG, BIT) (PIN ## REG) #define DDR_2(REG, BIT) (DDR ## REG) #define BIT(REG, BIT) (BIT) 

The extra layer should use any #defined value as REG or BIT.

The following code works as intended:

 #define IO_NAME_REG B #define IO_NAME_BIT 5 PORT_(B, 5) => PORTB PORT_(IO_NAME_REG, IO_NAME_BIT) => PORTB 


When I, however, try

 #define IO_NAME B, 5 PORT_(IO_NAME) 

this leads to an error:

 macro "PORT_" requires 2 arguments, but only 1 given 

As I understand it, the comma is interpreted as a comma operator, and therefore the left value is ignored. I did this in this experiment:

 #define IO_NAME B, 5 PORT_(IO_NAME,) => PORT_5 


Replacing a comma with a definition:

 #define comma , #define IO_NAME B comma 5 PORT_(IO_NAME,) => PORT_5 

leads to the same result


Bypass

Of course, you can divide “B” and “5” into two separate ones. Although this improvement is over four, it is still not so convenient.


Question

... is not limited to the title. Any solution that is shorter or simpler than

 #define IO_NAME_REG B #define IO_NAME_BIT 5 PORT_(IO_NAME_REG, IO_NAME_BIT) => PORTB 

Greetings to me.

If this is simply not possible, I would like to know the reasons why.

+5
source share
2 answers

This should do what you want:

 #include <stdio.h> #define IO_NAME B, 5 #define PORT_(arg) PORT_2(arg) #define PIN_(arg) PIN_2(arg) #define DDR_(arg) DDR_2(arg) #define BIT_(arg) BIT_2(arg) #define PORT_2(reg, bit) (PORT ## reg) #define PIN_2(reg, bit) (PIN ## reg) #define DDR_2(reg, bit) (DDR ## reg) #define BIT_2(reg, bit) (bit) #define PORTB 1 #define PINB 2 #define DDRB 3 int main() { printf("%d\n", PORT_(IO_NAME)); printf("%d\n", PIN_(IO_NAME)); printf("%d\n", DDR_(IO_NAME)); printf("%d\n", BIT_(IO_NAME)); return 0; } 

This leads to the following conclusion:

 1 2 3 5 

## in macro objects forces the preprocessor to create new tokens by concatenating operands.

This is almost what you had. The difference is that top-level macros must take one argument, and secondary macros must take two.

+1
source

Instead, you can use macros with a variable argument, just with changes you can achieve your goal.

 #define PORT_(...) PORT_2(__VA_ARGS__) #define PIN_(...) PIN_2(__VA_ARGS__) #define DDR_(...) DDR_2(__VA_ARGS__) 
0
source

Source: https://habr.com/ru/post/1247309/


All Articles