如何将任何用户定义的类型转换为 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&lt;char, N&gt; 是更好的选择。 【参考方案1】:

您想要的通常是不可能的。任何不可简单复制的用户定义类型都将立即被排除在外,因为bit_cast 仅适用于可简单复制的类型。

说到这一点,bitset 本身 并没有被标准要求被简单地复制。我的意思是,几乎没有理由不实现它,但是标准中没有任何内容要求实现者使其易于复制。因此,虽然您的代码可能在某些特定实现(或可能全部)上运行,但不能保证您可以将 bit_cast 转换为 bitset

至于为什么可以用 padding 打断,这很可能是因为bit_cast 也要求这两种类型的大小相同,而bitset&lt;N&gt; 的大小不需要是N/8 字节。 bitset 的许多实现将位存储在 32 位整数类型的数组中。所以bitset&lt;24&gt; 可能仍会占用 4 个字节的存储空间。如果给你一个 3 字节的类型,那么你不能bit_cast 他们。

真正想要的是std::array&lt;std::byte, sizeof(T)&gt; 的可能性很大。虽然这种类型是可简单复制的(所以bit_cast 可以处理它),实际上并不要求这种数组的大小等于sizeof(T)通常会,但你不能保证。大小将取决于实现,因此来自可简单复制的Tbit_casting 是否有效将取决于实现。

我有#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 将任何字符串转换为有效的自定义模式?

如何将自定义 json 转换为自适应卡片 json 格式

将 Scala 中的任何类型转换为 Array[Byte] 并返回

如何将自定义类型转换为原始类型?

如何自定义将列表转换为不同的类型?

使用标准 .Net 功能/BCL 将任何类型的编码输入字符串转换为 UTF-8