【问题标题】:Using static_assert to verify bitfields in a typedef union of a struct and an uint32_t使用 static_assert 验证结构和 uint32_t 的 typedef 联合中的位域
【发布时间】:2021-04-01 20:17:22
【问题描述】:

我正在尝试将 static_assert 用于 FPGA 的寄存器,并定义了以下带有位域和所有变量的结构联合。但是每当我尝试编译时, static_assert 都不会编译,并且我收到一条错误消息,指出变量没有命名类型。如果我尝试转发声明,它并不能解决问题。我不确定让 static_assert 工作的正确模式是什么。对编写以下代码的正确方法有任何想法吗?

so.h:

#include <stdint.h>

#define t_f2bits_addr (0x0000)

typedef union {
    struct {
        uint32_t do_it : 1;
        uint32_t fault : 1;
    } field;
    uint32_t all;
} t_f2bits_type;
#define t_f2bits_ptr (*(volatile t_f2bits_type *)t_f2bits_addr)

t_f2bits_type myVar; 
myVar.field.do_it = 0x1;
myVar.field.fault = 0x1;
static_assert(myVar.all == 0x3, "Not equal");

so.c

#include "so.h"

int main() 
{
    return 0;
}

运行:

g++ so.c
In file included from so.c:1:0:
so.h:15:1: error: ‘myVar’ does not name a type
 myVar.field.do_it = 0x1;
 ^~~~~
so.h:16:1: error: ‘myVar’ does not name a type
 myVar.field.fault = 0x1;
 ^~~~~
so.h:17:1: error: non-constant condition for static assertion
 static_assert(myVar.all == 0x3, "Not equal");
 ^~~~~~~~~~~~~
so.h:17:1: error: the value of ‘myVar’ is not usable in a constant expression
so.h:14:15: note: ‘myVar’ was not declared ‘constexpr’
 t_f2bits_type myVar;
               ^~~~~

【问题讨论】:

  • 请注意,您的代码在 C++ 中是非法的。 C++ 不允许通过联合进行类型双关语,除非它要访问联合内的标准布局类共享的公共初始成员序列。
  • 您不能在全局范围内编写代码。把它放在一个函数中(或main)。然后你会发现你不能以这种方式使用static_assert。它只执行编译时检查。加上@NathanOliver 所说的话。
  • 尽管在 C/C++ 中通过联合进行类型双关是非法的,但每个人都这样做:)
  • 我绝对不是打算颠覆 C++ 语言哈哈……但是有没有一种模式可以让我使用 assert(最好是 static_assert)来测试位域的功能?

标签: c++ typedef unions bit-fields static-assert


【解决方案1】:

在 C++20 中,您可以使用 std::bit_cast:

struct field {
    uint32_t do_it : 1;
    uint32_t fault : 1;
};
constexpr field myVar = [](){
    field myVar;
    myVar.do_it = 0x1;
    myVar.fault = 0x1;
    return myVar;
}();
static_assert(std::bit_cast<uint32_t>(myVar) == 0x3, "Not equal");

Demo(只有 msvc 有std::bit_cast)。

请注意,位域实际上是特定于实现的(考虑字节序),static_assert 可能通过或不通过取决于编译器/架构/...

【讨论】:

    【解决方案2】:

    对编写以下代码的正确方法有任何想法吗?

    没有正确的方法。无法使用static_assert 来检查位域是否在特定位置。

    您正在使用 C++。不要使用工会。编写一个带有访问器的普通类,该访问器使用掩码访问特定位 - 这种方式清晰、可移植并保证工作。或者使用std::bitset

    【讨论】:

      【解决方案3】:

      不要使用位域,使用老式位掩码

      namespace FpgaRegister
      {
          constexpr uint32_t do_it = 1 << 0;
          constexpr uint32_t fault = 1 << 1;
      
          constexpr uint32_t all = do_it | fault;
      }
      
      static_assert(FpgaRegister::all == 0x3, "Not equal");
      

      【讨论】:

      • 位域是一个不能删除的要求
      • 那为什么不改变需求呢?绑定到实现定义的特性是一种错误的解决问题的方法。
      • 我不是故意这么松散地使用它,但它是问题的要求。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多