用位域模拟非字节对齐的整型(uint4_t,int4_t等)

与硬件打交道的时候,会存在不是字节对齐的整型,比如4位无符号整型(uint4_t)、24位有符号整型(int24_t)这种,如果直接用using uint4_t = uint8_t; using int24_t = int32_t; 这种别名的话,虽然也可以用用,但是当值溢出的时候,我们代码是没法发觉的,最终可能使用一个在表示范围外的值。此外当硬件传过来的数如果是一个负数,比如int4_t的-8(0x8),我们用int8_t变量接收的话,用int8_t的方法来解释,必然不是-8,而是8(0x08,符号位为0)。这就要求我们有一个扩展符号位的方法: 根据最高位是否为1,决定将不将多余的位补一,比如这里就要将int8_t的高4位补一,这样才是-8(0xf8),含义与硬件的一致。或者用一个位域结构体:

struct int4_t{
    int8_t value:4;
};

这样给它赋值0x8再取value的值,也是-8。

通过上面的这个,就容易想到,如果我不用using别名把不对齐的类型实际上定位到与它最接近的原生类型,而是用位域,或许就能弥补上面说到的两个问题了。由于我们需要把无效的位置零,所以可以用struct 或者union来做。先讲union,P就是对齐到的类型,即承载类型,N就是有多少位。raw用来初始化,把非有效位初始化为0(这个是为了尽量与C语言兼容,下面会说)。

template <typename P, size_t N, 
        std::enable_if_t<std::is_integral_v<P>> * = nullptr>
    union BitWrapper {
        inline constexpr operator P() const {
            return value;
        }
        constexpr BitWrapper(const P in) :raw(in & ((1 << N) - 1)) {}
        constexpr BitWrapper() = default;
    private:
        P value : N;
        P raw{};
    };

using uint4_t = BitWrapper<uint8_t, 4>;
using int4_t = BitWrapper<int8_t, 4>;

但是用union有一个弊端就是,无法用它来作为真正的常量,给模板参数用,因为我们在常量构造函数里面激活的都是raw域,而对外的接口用的都是value域,编译期间不允许这么用的。

所以要用struct来做,存两个成员,一个叫value,另一个是无效的,就叫dummy。还有一个需要注意的是,构造函数接收的应该是一个任意的数值或者枚举类型,而不是P类型,因为如果接收P类型,而常量in的位宽比P类型高的话,是会报常量截断错误的,也可以简单粗暴用uint64_t,但有些标准可以有int128,uint128的,所以最好用模板。另外,在内建类型里面,低精度可以隐式转换为高精度类型,但是高精度转成低精度的时候,编译器会警告,除非用static_cast转一下。我们这里也可以利用explicit关键字,实现这个特点,不过高精度隐式构造低精度的时候,这种写法更严格——必须static_cast

template <typename P, uint16_t N>
    struct BitWrapper {
        inline constexpr operator P() const {
            return value;
        }

        template<typename T, std::enable_if_t<std::is_arithmetic_v<T> || std::is_enum_v<T>> * = nullptr>
        constexpr BitWrapper(const T in) :value(P(in)) , dummy(0){}

        // 高精度BitWrapper往低精度构造,必须显式
        template<typename T, uint16_t U, std::enable_if_t< N<U > * = nullptr>
            constexpr explicit BitWrapper(const BitWrapper<T, U> & in) : value(P(in)), dummy(0) {}

        // 低精度往高精度构造,可以隐式
        template<typename T, uint16_t U, std::enable_if_t< N>=U > * = nullptr>
            constexpr BitWrapper(const BitWrapper<T, U> & in) : value(in), dummy(0) {}
        
        // 平凡的默认构造函数,如果声明变量的时候不带{},dummy可能为非0
        constexpr BitWrapper() = default
    private:
        P value : N;
        P dummy : sizeof(P) * 8 - N;  // 非有效位永远为0
    };

这样子的话,没办法像原生类型那么使用赋值类的运算符,像是++、+&#

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值