编写一个variadic宏,用于设置整数中的特定位(位掩码)
我正在尝试编写一个宏,它简化了在整数中设置多个位的过程.初始化配置寄存器时,这通常发生在微控制器代码中.例如,可以通过在寄存器TCCR0A中设置3位来配置8位定时器,如下所示:
// WGM01,WGM00 and COM0A1 are constants between 0 and 7 // There are hundreds of these constants defined in avr-libc TCCR0A |= (1<<WGM01) | (1<<WGM00) | (1<<COM0A1); // Another way to write this: #define _BV(bit) (1 << (bit)) // <-- defined in avr-libc TCCR0A |= _BV(WGM01) | _BV(WGM00) | _BV(COM0A1); 但是,我发现写这样的东西要容易得多: TCCR0A |= BITS(WGM01,WGM00,COM0A1); // <- Variable # of arguments please! 由于我无法想象没有人想到这一点,我搜索了一下,但没有发现任何确实如此.我想知道这是否可能,但我在阅读https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html和https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-and-idioms的时候还是试了一下. 这是我到目前为止所尝试的内容.我想解决方案必须是递归宏,但是在尝试使其正确扩展时并没有走得太远.由于我的所有寄存器都是8位长,因??此8次扩展通道就足够了(第一次尝试). #define BITS_EVAL(...) BITS_EVAL1(BITS_EVAL1(BITS_EVAL1(__VA_ARGS__))) #define BITS_EVAL1(...) BITS_EVAL2(BITS_EVAL2(BITS_EVAL2(__VA_ARGS__))) #define BITS_EVAL2(...) __VA_ARGS__ #define BITS(bit,...) ((1 << bit) | BITS_EVAL(BITS(__VA_ARGS__))) 以上并不是很有效.它目前的作用是: // BITS(2,5,7) --> ((1 << 2) | BITS(5,7)) 但是,我想要实现的是其中之一(或等效的): // BITS(2,7) --> ((1 << 2) | (1 << 5) | (1 << 7)) // BITS(2,7) --> ((1 << 2) | ((1 << 5) | ((1 << 7)))) 任何人都可以帮助我完成我的任务,或者告诉我这是不可能的吗? 解决方法
因此,在使用Paul的answers和github wiki中的宏进行了更多的讨论之后,我实际上设法生成了一个可以实现我想要的工作BITS(…)宏.它是一个递归宏,可以多次扫描以扩展递归替换.它处理可变数量的参数,并支持最多64位的整数. // test.c #include "bits.h" int a = BITS(1,7); int b = BITS(3); int c = BITS(); // This case is broken but irrelevant 使用gcc -E test.c -o test.txt扩展为: int a = (0 | (1ull<<1) | (1ull<<5) | (1ull<<7)); int b = (0 | (1ull<<3)); int c = (0 | (1ull<<)); // This case is broken but irrelevant 0 |在开头是实现的工件,但显然不会影响表达式的结果. 这是实际的实现,包括评论: // bits.h // Macros partially from https://github.com/pfultz2/Cloak #define EMPTY(...) // Defers expansion of the argument by 1,2 or 3 scans #define DEFER(...) __VA_ARGS__ EMPTY() #define DEFER2(...) __VA_ARGS__ DEFER(EMPTY)() #define DEFER3(...) __VA_ARGS__ DEFER2(EMPTY)() // Concatenate the arguments to one token #define PRIMITIVE_CAT(a,...) a ## __VA_ARGS__ // Apply multiple scans to the argument expression (>64 to allow uint64_t masks) #define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) #define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) #define EVAL4(...) __VA_ARGS__ // Always expand to the second token after expansion of arguments. // One level of indirection to expand arguments before selecting. #define SELECT_2ND(...) SELECT_2ND_INDIRECT(__VA_ARGS__,) #define SELECT_2ND_INDIRECT(x1,x2,...) x2 // Expands to a comma (which means two empty tokens in a parameter list). // Thus,SELECT_2ND will expand to an empty token if this is the first argument. #define BITS_RECURSIVE__END_RECURSION,// Adds the END_RECURSION parameter,which marks the end of the arguments #define BITS(...) (0 EVAL(BITS_RECURSIVE(__VA_ARGS__,END_RECURSION,))) // When hitting END_RECURSION,the CAT will expand to "," and SELECT_2ND // will select the empty argument instead of the recursive call. #define BITS_RECURSIVE(bit,...) SELECT_2ND(PRIMITIVE_CAT(BITS_RECURSIVE__,bit), | (1ull<<(bit)) DEFER3(BITS_INDIRECT)()(__VA_ARGS__)) // Needed to circumvent disabling contexts for recursive expansion #define BITS_INDIRECT() BITS_RECURSIVE 还有一些代码来测试极端情况: // test2.c #include "bits.h" #include <inttypes.h> #include <stdio.h> uint8_t u8 = BITS(0,1,2,3,4,6,7); uint32_t u32 = BITS(0,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31); uint64_t u64 = BITS(0,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63); uint64_t a64 = BITS(0,55); int main(void) { printf("0x%02" PRIX8 "n",u8); // Prints 0xFF printf("0x%08" PRIX32 "n",u32); // Prints 0xFFFFFFFF printf("0x%016" PRIX64 "n",u64); // Prints 0xFFFFFFFFFFFFFFFF printf("0x%016" PRIX64 "n",a64); // Prints 0x00FF00FF00FF00FF return 0; } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |