加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

编写一个variadic宏,用于设置整数中的特定位(位掩码)

发布时间:2020-12-16 10:01:25 所属栏目:百科 来源:网络整理
导读:我正在尝试编写一个宏,它简化了在整数中设置多个位的过程.初始化配置寄存器时,这通常发生在微控制器代码中.例如,可以通过在寄存器TCCR0A中设置3位来配置8位定时器,如下所示: // WGM01,WGM00 and COM0A1 are constants between 0 and 7// There are hundreds
我正在尝试编写一个宏,它简化了在整数中设置多个位的过程.初始化配置寄存器时,这通常发生在微控制器代码中.例如,可以通过在寄存器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))))

任何人都可以帮助我完成我的任务,或者告诉我这是不可能的吗?

解决方法

Warning: Writing this was mostly a learning exercise.

DO NOT USE IN PRODUCTION CODE. People will rightly curse at you if you do.

因此,在使用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;
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读