如何在C++ 14中使用(类似于?)可变参数模板编写位掩码

问题描述:

我想写一个有效的方法来在字节(或任何其他类型)中写入0和1。如何在C++ 14中使用(类似于?)可变参数模板编写位掩码

例如,在C,我们可以写类似:

uint8_t x = 0x00; 
x|= (1 << 2) | (1 << 4); 

写在第2位1和4(当然,你不使用2和4,但使用宏记住位2和4的含义)。

我不喜欢这些做法,所以我写了下面的可变参数模板:

template<typename T> 
T bitmask(T p0) 
{ 
    return (1 << p0); 
} 

template<typename T, typename...Position> 
T bitmask(T p0, Position... p1_n) 
{ 
    return (1 << p0)|bit_mask(p1_n...); 
} 



template<typename T, typename... Position> 
T& write_one(T& x, Position... pos0_n) 
{ 
    x|= bit_mask(pos0_n...); 

    return x; 
} 

这些做工精细。你可以写这样的东西:

uint8_t x = 0x00; 
write_one(x, 2, 4); 

但我喜欢另一种解决方案。我想写类似:

write_one<uint8_t>(x, 2, 4); // if x is uint8_t 
write_one<uint16_t>(x, 2, 4); // if x is uint16_t 

类型write_one的是x的类型(好吧,我知道你不需要写类型uint8_t和uint16_t,我写它的清晰度)。其他参数始终是数字(实际上,它们是uint8_t)。

我该如何实现这些目标?

我想写如下代码:

template<typename T> 
T bitmask(uint8_t p0) 
{ 
    return (1 << p0); 
} 

template<typename T> 
T bitmask(T p0, uint8_t... p1_n) 
{ 
    return (1 << p0)|bit_mask<T>(p1_n...); 
} 

template<typename T> 
T& write_one(T& x, uint8_t... pos0_n) 
{ 
    x|= bit_mask<T>(pos0_n...); 

    return x; 
} 

非常感谢你。

+0

你检查你迄今为止做了什么汇编输出?你会惊讶编译器有多好 –

这两种方法都产生相同的高度优化的汇编:

#include <utility> 

template <int...bits, class Int> 
constexpr auto set_bits_a(Int i) 
{ 
    using expand = int[]; 
    void(expand{ 
     0, 
     ((i |= (Int(1) << bits)),0)... 
    }); 
    return i; 
} 

template <class Int, class...Bits> 
constexpr auto set_bits_b(Int i, Bits...bits) 
{ 
    using expand = int[]; 
    void(expand{ 
     0, 
     ((i |= (Int(1) << bits)),0)... 
    }); 
    return i; 
} 

int get_value(); 
volatile int x, y; 

int main() 
{ 
    x = set_bits_a<1, 3, 5>(get_value()); 
    y = set_bits_b(get_value(), 1, 3, 5); 
} 

输出:

main: 
     sub  rsp, 8 
     call get_value() 
     or  eax, 42     ; <-- completely optimised 
     mov  DWORD PTR x[rip], eax 
     call get_value() 
     or  eax, 42     ; <-- completely optimised 
     mov  DWORD PTR y[rip], eax 
     xor  eax, eax 
     add  rsp, 8 
     ret 
y: 
x: 

https://godbolt.org/g/CeNRVw

+0

非常感谢。但我不明白以下含义:void(展开{ 0, ((i | =(Int(1) Antonio

+0

@Antonio这个'...'被称为elipsis。它的功能是扩大可变参数。 为了避免关于未使用的表达式的编译器警告,我将其设置为void。 使用'expand'是必要的,直到C++ 17时才会有折叠运算符。 –

+0

谢谢,理查德。 – Antonio