Definining a C macro that expands to a variable number of elements -


i'm writing usb report descriptors, sequence of bytes: tag byte (in lower bits tell how many data bytes follow) followed 0, 1, 2 or 4 data bytes. e.g. define logical ranges of input:

uint8_t report_descriptor[] = {     ...     0x15, 0x00,                     //   logical minimum (0)     0x26, 0xff, 0x03,               //   logical maximum (1023)     ... }; 

since 0 fits 1 byte, use tag type 0x15 (logical minimum 1 data byte). 1023 requires 2 bytes, tag type 0x26 (logical maximum 2 data bytes).

i had hoped define macros make more readable (and avoid having comment every line):

uint8_t report_descriptor[] = {     ...     logical_minimum(0),     logical_maximum(1023),     ... }; 

however, i've hit snag: macro needs expand different number of elements depending on value. don't see easy way achieve this. i've tried tricks value > 255 ? (value & 0xff, value >> 8) : value, gets expanded 1 byte.

i think spec allows use 4-byte tags, wasteful, i'd rather not that.

is i'm after possible preprocessor?

i don't think c preprocessor powerful enough in clean way. if willing resort m4 macro processor, can done easily. m4 should readily available on vast majority of gnu/linux systems , portable implementations should available platforms.

let's define m4 macros in separate file , name macros.m4.

define(`extract_byte', `(($1 >> (8 * $2)) & 0xff)')  dnl don't want define these m4 macros c preprocessor dnl macros in header files.  define(`tag_1_bytes', `0x15') define(`tag_2_bytes', `0x26') define(`tag_3_bytes', `0x37') define(`tag_4_bytes', `0x48')  define(`expand_1_bytes', `tag_1_bytes, extract_byte($1, 0)') define(`expand_2_bytes', `tag_2_bytes, extract_byte($1, 1), extract_byte($1, 0)') define(`expand_3_bytes', `tag_3_bytes, extract_byte($1, 2), extract_byte($1, 1), extract_byte($1, 0)') define(`expand_4_bytes', `tag_4_bytes, extract_byte($1, 3), extract_byte($1, 2), extract_byte($1, 1), extract_byte($1, 0)')  define(`encode',   `ifelse(eval($1 < 256), `1', `expand_1_bytes($1)',     `ifelse(eval($1 < 65536), `1', `expand_2_bytes($1)',       `ifelse(eval($1 < 16777216), `1', `expand_3_bytes($1)',       `expand_4_bytes($1)')')')') 

now, writing c files straight forward. put following code in file test.c.m4:

include(`macros.m4')  `static unint8_t report_descriptor[] = {'     encode(50),     encode(5000),     encode(500000),     encode(50000000), `};' 

in makefile, add following rule

test.c: test.c.m4 macros.m4     ${m4} $< > $@ 

where m4 set m4 processor (usually m4).

if m4 run on test.c.m4, – omitting excess white space – produce following test.c file:

static unint8_t report_descriptor[] = {   0x15, ((50 >> (8 * 0)) & 0xff),   0x26, ((5000 >> (8 * 1)) & 0xff), ((5000 >> (8 * 0)) & 0xff),   0x37, ((500000 >> (8 * 2)) & 0xff), ((500000 >> (8 * 1)) & 0xff), ((500000 >> (8 * 0)) & 0xff),   0x48, ((50000000 >> (8 * 3)) & 0xff), ((50000000 >> (8 * 2)) & 0xff), ((50000000 >> (8 * 1)) & 0xff), ((50000000 >> (8 * 0)) & 0xff), }; 

you'll find more convenient keep test.c.m4 file minimal possible , #include in ordinary c file.

if don't know m4, can learn basics rather quickly. if using gnu autoconf, might find convenient use m4sugar m4 macro library instead of plain m4 i've used above.


Comments

Popular posts from this blog

cakephp - simple blog with croogo -

How to group boxplot outliers in gnuplot -

bash - Performing variable substitution in a string -