基于constexpr的模式创建位掩码

时间:2017-07-20 22:02:22

标签: c++ c++11 bit-manipulation c++14 constexpr

我想实现一个模板函数,它在编译时为整数类型生成位掩码。这些掩模应基于8位模式,其中模式将连续重复以填充整数。以下示例完全符合我的要求,但在运行时:

#include <iostream>
#include <type_traits>
#include <cstring>

template<typename Int>
typename std::enable_if<std::is_integral<Int>::value, Int>::type
make_mask(unsigned char pattern) {
    Int output {};
    std::memset(&output, pattern, sizeof(Int));
    return output;
}

int main() {
    auto mask = make_mask<unsigned long>(0xf0);
    std::cout << "Bitmask: '" << std::hex << mask << "'" << std::endl;
}

上面代码的输出是:

Bitmask: 'f0f0f0f0f0f0f0f0'

我知道优化器可以在上面的代码中消除整个函数调用,但我正在寻找constexpr解决方案,并且可选择。< / p>

6 个答案:

答案 0 :(得分:12)

直观地说,我会做一个字节转发器:

template<class Int, int count, int byte>
struct byte_repeater;

template<class Int, int byte>
struct byte_repeater<Int, 1, byte> {
    static const Int value = byte;
};

template<class Int, int count, int byte>
struct byte_repeater {
    static const Int value = (byte_repeater<Int, count-1, byte>::value << CHAR_BIT) | byte;
};

易于使用的界面:

template<class Int, int mask> 
struct make_mask {
    static const Int value = byte_repeater<Int, sizeof(Int), mask>::value;
};

这适用于C ++ 03。也许更老。 Compilation Here

在较新版本的C ++中,可能有一些方法可以使这更简单。哎呀,即使在C ++ 03中,也可以简化它。

答案 1 :(得分:4)

你可以写出来:

template<typename Int, typename = std::enable_if_t<std::is_integral<Int>::value>>
constexpr Int make_mask(unsigned char pattern) {
    constexpr auto numBytes = sizeof(Int);
    Int result = 0;

    for (std::size_t i = 0; i < numBytes; i++) {
        result |= static_cast<Int>(pattern) << (i*8);
    }

    return result;
}

Demo

这仅适用于未签名类型,但您可以通过调用无符号版本并将其转换为签名类型来使其适用于签名类型:

template<typename Int, std::enable_if_t<std::is_integral<Int>::value && std::is_unsigned<Int>::value, int> = 0>
constexpr Int make_mask(unsigned char pattern) {
    constexpr auto numBytes = sizeof(Int);
    Int result = 0;

    for (std::size_t i = 0; i < numBytes; i++) {
        result |= static_cast<Int>(pattern) << (i*8);
    }

    return result;
}

template<typename Int, std::enable_if_t<std::is_integral<Int>::value && std::is_signed<Int>::value, int> = 0>
constexpr Int make_mask(unsigned char pattern) {
    return static_cast<Int>(make_mask<std::make_unsigned_t<Int>>(pattern));
}

Demo

答案 2 :(得分:4)

我不确定签名类型是否有明确定义的解决方案。对于无符号类型,我会选择:

template<class Int>
constexpr typename std::enable_if</* std::is_integral<Int>::value && */ std::is_unsigned<Int>::value,
Int>::type make_mask(const unsigned char pattern) {
    return ((std::numeric_limits<Int>::max() / std::numeric_limits<unsigned char>::max()) * pattern);
}

如果std::numeric_limits<Int>::max()std::numeric_limits<unsigned char>::max()的倍数,这应该有用;你可以为std::enable_if条件添加一个检查,如果检查失败,则使用另一个解决方案。

答案 3 :(得分:2)

如何将make_mask()声明为constexpr,修改它添加默认参数,使用shift-bit,bit-or和recursion?

我的意思是

#include <climits>
#include <iostream>

template <typename Int>
constexpr typename std::enable_if<std::is_integral<Int>::value, Int>::type
      make_mask (unsigned char pattern, std::size_t dim = sizeof(Int))
 { 
   return dim ? ((make_mask<Int>(pattern, dim-1U) << CHAR_BIT) | pattern)
              : Int{};
 }

int main ()
 {
   constexpr auto mask = make_mask<unsigned long>(0xf0);
   std::cout << "Bitmask: '" << std::hex << mask << "'" << std::endl;
 }

P.S。:也适用于C ++ 11。

答案 4 :(得分:1)

它只是一个乘法。此外,您需要确保不要遇到任何陷阱:

  • 防御is_integral<bool>为真
  • 这个函数在任何没有8位字节的机器中都有完全不同的含义,所以只是拒绝为这些机器编译
  • 防止签名溢出,所以只需使用uintmax_t

将所有这些检查填充到函数签名上是不可读的,所以我使用了static_assert()

template <typename IntType>
constexpr IntType
make_mask ( unsigned char pattern )
{
    static_assert ( CHAR_BIT == 8, "" );
    static_assert ( std::is_integral<IntType>::value, "" );
    static_assert ( not std::is_same<typename std::decay <IntType>::type, bool>::value, "" );

    enum : uintmax_t { multiplier = std::numeric_limits <uintmax_t>::max ( ) / 0xFF };
    return static_cast <IntType> ( pattern * multiplier );
}

答案 5 :(得分:-3)

你真的需要所有这些并发症吗?如何使用宏呢?

#define PATTERN(A) 0x##A##A##A##A##A##A##A##A

cout << hex << PATTERN(f0) << endl;

将在c ++ 98中工作(在没有cout的普通'c'中): - )

或者如果你真的想要c11 / ++,这也有效:

constexpr long long int pattern(const long long unsigned  pattern)  {
    return (pattern << 56) | (pattern << 48) | (pattern << 40) 
           | (pattern << 32) | (pattern << 24) | (pattern << 16) 
           | (pattern << 8) | pattern ;
}