【问题标题】:Create bitmask based on a pattern as constexpr基于模式创建位掩码为 constexpr
【发布时间】:2017-07-20 22:02:21
【问题描述】:

我想实现一个模板函数,它在编译时为整数类型生成位掩码。这些掩码应基于 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 解决方案。

【问题讨论】:

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


    【解决方案1】:

    直观地说,我会做一个字节中继器:

    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 中,它也可能会被简化。

    【讨论】:

    • 您的示例也适用于C++98。我认为byte_repeater 不是必需的,它的主模板中的count 参数可以初始化为count = sizeof(Int) 然后byte_repeater 可以与make_mask 一样使用,如果count 是最后一个参数.尽管如此,最好制作单独的模板来隐藏 count 参数,这只是一个实现细节。
    • 尽管其他答案也很有帮助,而且很难选择接受哪个答案,但由于向后兼容性,我接受了这个答案。
    【解决方案2】:

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

    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&lt;Int&gt;::max()std::numeric_limits&lt;unsigned char&gt;::max() 的倍数,这应该可以工作;您可以在std::enable_if 条件中添加一个检查,如果检查失败,请使用其他解决方案。

    【讨论】:

    • 不错的解决方案!但我想如果你检查std::is_unsigned,你可以避免检查std::is_integral。我的意思是:SFINAE 部分可以简化为typename std::enable_if&lt;std::is_unsigned&lt;Int&gt;::value, Int&gt;::type
    • std::is_unsigned&lt;Int&gt;::value == true 是否暗示std::is_integral&lt;Int&gt;::value == true?我可以相信,但我在短暂的搜索中没有看到这种保证。
    • 根据 ccpreference,std::is_unsignedtrue 仅适用于“无符号整数类型和 bool 类型”; bool 类型可能会失败。
    • 优秀的解决方案!结合@Justin 给出的solution,它也可以与有符号整数类型一起使用,也可以与C++11 一起使用。我喜欢你使用除法生成模式重复器的方式。
    • signed ints 的问题是构造掩码可能会将 1 推入或通过符号位(或只是溢出),立即触发未定义的行为。
    【解决方案3】:

    你可以直接写出来:

    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

    【讨论】:

    • 我喜欢您的解决方案如何处理 signedunsigned 整数类型。 C++14 及以上的一个非常容易理解的解决方案。谢谢!
    【解决方案4】:

    make_mask() 声明为constexpr 怎么样,修改它添加一个默认参数,使用移位位、位或和递归?

    我是说

    #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。

    【讨论】:

      【解决方案5】:

      这只是一个乘法。此外,您需要确保自己没有遇到任何陷阱:

      • 防止is_integral&lt;bool&gt; 是真的
      • 此函数在任何没有 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 );
      }
      

      【讨论】:

        【解决方案6】:

        您真的需要所有这些并发症吗?只用一个宏来做这个怎么样?

        #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 ;
        }
        

        【讨论】:

        • 这是一个糟糕的解决方案,但它确实有效。我不明白为什么会有反对票。
        • @HenriMenke 因为这不是解决方案;它仅适用于其中一种整数类型。
        • 这适用于问题中请求的类型。 BTW,从编译效率来看,这是最有效的解决方案。嗯,它有自己的缺点,比如没有范围,但它很轻量级并且很简单。
        • OP 要求“在编译时为整数类型生成位掩码的模板函数”,而不是针对特定类型,也不是针对固定大小的类型。
        猜你喜欢
        • 1970-01-01
        • 2011-10-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多