基于模式创建位掩码作为 constexpr
Create bitmask based on a pattern as 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'
我知道优化器可以消除上面代码中的整个函数调用,但我正在寻找一种使用 c++14 和可选的 c++11 的constexpr
解决方案。
直观地说,我会做一个字节中继器:
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 中有效。也许更老。 编译在这里。
在较新版本的C++中,可能有办法使这更简单。哎呀,即使在 C++03 中,它也可能被简化。
我不确定有符号类型是否有明确定义的解决方案。 对于无符号类型,我会选择:
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
条件中添加一个检查,如果该检查失败,则使用另一种解决方案。
你可以把它写出来:
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;
}
演示
这仅适用于无符号类型,但您可以通过调用无符号版本并将其强制转换为有符号类型来使其适用于有符号类型:
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));
}
演示
将make_mask()
声明为constexpr
,使用 shift-bit、bit-or 和递归添加默认参数对其进行修改怎么样?
我的意思是
#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;
}
附言:也适用于C++11。
这只是一个乘法。此外,您需要确保不会遇到任何陷阱:
- 防御
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 );
}
你真的需要所有这些并发症吗?为此仅使用宏怎么样?
#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 ;
}
- 位移操作和位掩码未检测到重复字符
- OpenCV - 带有掩码的absdiff
- 生成前缀位掩码
- 如何从__m64值的 lsb 创建 8 位掩码?
- 如何对无符号长 int 进行位掩码?
- 删除K的背景掩码-意味着Python或C++中的集群/
- 如何在C++中优雅地处理位掩码
- 将uint64_t位掩码转换为 std::布尔数组
- 使输入二进制掩码适应 ITK 网格生成器
- 如何从 getifaddr 读取子网掩码
- 优化从子位掩码生成父位掩码
- 基于模式创建位掩码作为 constexpr
- 使用二进制掩码 C++ ITK 获取感兴趣区域
- C++中的运行时位复制(位掩码)
- 根据 IP 和掩码C++打印所有 IP
- C++设置"blank"或重置 ifstrean (ios) 的异常掩码
- OpenCV 检测带有掩码的斑点
- OPENCV:如何创建多边形形状的掩码
- 递归函数,用于使用位掩码 c++ 显示集合的所有子集
- 模拟 AVX-512 掩码指令