【问题标题】:How to write a bitmask in c++14 using (something like?) variadic templates如何使用(类似于?)可变参数模板在 C++14 中编写位掩码
【发布时间】:2017-07-29 21:37:14
【问题描述】:

我想写一个有效的方法来在一个字节(或任何其他类型)中写入 0 和 1。

例如,在 C 语言中我们可以这样写:

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

在第2位和第4位写1。(当然,你不用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;
}

非常感谢。

【问题讨论】:

  • 您是否检查过您迄今为止所做的汇编程序输出?你会惊讶于编译器有多好

标签: c++ templates avr bitmask variadic


【解决方案1】:

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

#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

【讨论】:

  • 非常感谢。但我不明白的含义: void(expand{ 0, ((i |= (Int(1)
  • @Antonio ... 被称为省略号。它的功能是扩展可变参数。我强制转换为 void 以避免编译器警告关于未使用的表达式。 expand 的使用是必要的,直到 c++17 我们将使用折叠运算符。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-05
  • 1970-01-01
  • 2011-05-11
相关资源
最近更新 更多