如何将任何用户定义的类型转换为 std::bitset?
Posted
技术标签:
【中文标题】如何将任何用户定义的类型转换为 std::bitset?【英文标题】:How can I convert ANY user defined type to an std::bitset? 【发布时间】:2022-01-03 11:38:09 【问题描述】:我想要实现的是一种将任意大小和格式的类型转换为 std::bitset 的方法。像这样:
#include<bitset>
#include<bit>
#include<cstdlib>
#include<cstdint>
#include<array>
#include<iostream>
template<typename T, std::size_t SIZE = (sizeof(T) * CHAR_BIT)>
std::bitset<SIZE> as_bits(const T var) noexcept
if constexpr (SIZE < 32)//Size in bits
int32_t temp = 0;
std::memmove(&temp, &var, sizeof(T));
std::bitset<SIZE> bits = var;
return bits;
//End if
else
std::bitset<SIZE> bits = std::bit_cast<std::bitset<SIZE>, T>(var);
return bits;
//End else
//End of as_bits
用法:
float x = 4.5f;
std::cout << x << " as bits: " << as_bits(x) << "\n";
#pragma pack(push)
struct Y
std::array<int32_t, 4> z;
float x;
int8_t y;
;
#pragma pack(pop)
Y y = 1,2,3,4, 3.5, 'a';
std::cout << "struct as bits: " << as_bits(y) << "\n";
std::cout << "size of bitset: " << as_bits(y).size() << " bits long.\n";
输出:
4.5 as bits: 01000000100100000000000000000000
struct as bits: 000000000000000000000000011000010100000001100000000000000000000000000000000000000000000000000100000000000000000000000000000000110000000000000000000000000000001000000000000000000000000000000001
size of bitset: 192 bits long.
这适用于浮点数,但转换后的结构输出 192 位,而它的大小应该只有 168 位。我有#pragma 包是怎么回事?
如何防止填充?我还应该吗? 有没有办法使用概念或类型特征来锁定填充类型? 这是未定义的行为吗? 字节序重要吗? 有没有更好的办法?我目前正在使用 MSVC,但跨平台实现将是理想的。
在 MSVC 上将 #pragma pack(push) 更改为 #pragma pack(push, 1) 导致以下错误: 错误 C2783 '_To std::bit_cast(const _From &) noexcept':无法推断出 '__formal' 的模板参数
bit_cast 是否需要默认填充和对齐方式?
针对宽度小于 32 位的类型进行了更新。
【问题讨论】:
你应该指定你的实现,因为#pragma pack
是一个非标准的扩展。
你的程序不能为我编译:godbolt.org/z/G31vW1dTq(也请保留头文件,这样我们就不需要花时间自己添加它们)
顺便说一句,您不应该指定新的包装对齐方式吗? #pragma pack(push,1)
之类的东西?在您的情况下,仅推送当前对齐,但未设置新对齐。您可以通过打印sizeof(Y)
来验证它。现场演示:godbolt.org/z/8KEW44hsv.
此外,std::bit_cast
要求这两种类型都是可简单复制的,而std::bit_set
似乎无法保证这一点。
为什么是std::bitset
?此类用于对一堆位执行逻辑操作。如果您想对数据进行二进制序列化,std::array<char, N>
是更好的选择。
【参考方案1】:
您想要的通常是不可能的。任何不可简单复制的用户定义类型都将立即被排除在外,因为bit_cast
仅适用于可简单复制的类型。
说到这一点,bitset
本身 并没有被标准要求被简单地复制。我的意思是,几乎没有理由不实现它,但是标准中没有任何内容要求实现者使其易于复制。因此,虽然您的代码可能在某些特定实现(或可能全部)上运行,但不能保证您可以将 bit_cast
转换为 bitset
。
至于为什么可以用 padding 打断,这很可能是因为bit_cast
也要求这两种类型的大小相同,而bitset<N>
的大小不需要是N/8
字节。 bitset
的许多实现将位存储在 32 位整数类型的数组中。所以bitset<24>
可能仍会占用 4 个字节的存储空间。如果给你一个 3 字节的类型,那么你不能bit_cast
他们。
您真正想要的是std::array<std::byte, sizeof(T)>
的可能性很大。虽然这种类型是可简单复制的(所以bit_cast
可以处理它),实际上并不要求这种数组的大小等于sizeof(T)
。 通常会,但你不能保证。大小将取决于实现,因此来自可简单复制的T
的bit_cast
ing 是否有效将取决于实现。
我有#pragma 包是怎么回事?
#pragma pack
不能破坏 C++ 的规则。这里有两条重要的 C++ 规则:
sizeof(T)
也是T
数组中从一个T
到另一个T
的字节数。
每个T
都必须与其alignof(T)
对齐方式对齐。即使T
是数组中的一个元素。
pack
不能违反这些规则。由于您的数组和 float
无疑都对齐到 4 个字节,所以 T
也必须对齐到 4 个字节。并且由于 21 字节的数组增量不会达到 T
所需的 4 字节对齐,因此必须将 T
的大小填充为 24。
#pragma pack
只在 C++ 要求的规则内进行打包。
【讨论】:
以上是关于如何将任何用户定义的类型转换为 std::bitset?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Python 将任何字符串转换为有效的自定义模式?