c++ 在编译时生成位掩码

lf3rwulv  于 2023-04-01  发布在  其他
关注(0)|答案(3)|浏览(135)

我想在编译时生成各种位掩码:
0x111111110x1111111111111111
0xF0F0F0F00xF0F0F0F0F0F0F0F0
大小取决于类型,32位或64位。
例如:

template <typename T> genMask(unsigned char templt) {
  ....
};

genMask<uint32_t>(0xF0);
genMask<uint64_t>(0xF0);

第一次调用应该生成0xF0F0F0F0,而第二次调用genMask应该生成0xF0F0F0F0F0F0F0F0
目前我有这些硬编码。
任何想法在这里将不胜感激。
编辑:这是一个老派的方法:
#define MASK(b) ((T(-1) / 0xFF) * (b))
其中T是类型。

ohtdti5x

ohtdti5x1#

使用c++20,您可以使用std::bit_cast完成此操作:

template <std::unsigned_integral T>
constexpr T genMask(unsigned char templt) {
  std::array<unsigned char, sizeof(T)> mask;
  mask.fill(templt);
  return std::bit_cast<T>(mask);
}

如果你想确保这个函数只返回编译时常量,你可以使用consteval specifier,但是通过在constexpr变量的初始化器中调用上面的函数,或者作为一个非类型的模板参数,就足以达到同样的效果。

uhry853o

uhry853o2#

一种方法是构建一个所需字节数组,然后使用std::bit_cast将这些位强制转换为目标类型。

template <typename T> 
constexpr T genMask(unsigned char val) 
{
    unsigned char bytes[sizeof(T)];
    for (auto& e : bytes)
        e = val;
    return std::bit_cast<T>(bytes);
};

int main()
{
    std::cout << std::hex << genMask<uint32_t>(0xF0) << " " << genMask<uint64_t>(0xF0);
}

其输出

f0f0f0f0 f0f0f0f0f0f0f0f0

如果你不能使用std::bit_cast,那么你可以自己做位操作,比如

template <typename T> 
constexpr T genMask(unsigned char val) 
{
    T ret{val};
    for (std::size_t i = 0; i < sizeof(T) - 1; ++i)
    {
        ret <<= 8;
        ret |= val;
    }
    return ret;
};
ltskdhd1

ltskdhd13#

因此,您需要将1字节模式乘以0x01010101...(重复以获得适当的类型长度)。
你会注意到,如果你的模式是0xFF,那么你最终会得到0xFFFFFFFF...,或者该类型的最大值,所以除以0xFF将得到0x01的模式。
他说:

template <typename T> constexpr T genMask(unsigned char templt) {
    constexpr T multiplier{std::numeric_limits<std::make_unsigned_t<T>>::max() / 0xFFu};
    return multiplier * T{templt};
}

static_assert(genMask<std::uint32_t>(0xF0) == 0xF0F0F0F0);
static_assert(genMask<std::uint64_t>(0xA2) == 0xA2A2A2A2A2A2A2A2);

相关问题