【问题标题】:Using an already-defined struct as an anonymous member of a union使用已定义的结构作为联合的匿名成员
【发布时间】:2019-06-03 11:58:27
【问题描述】:

假设我有一个 32 位硬件寄存器 Reg,我希望能够以 32 位值(例如 Reg = 0x12345678)或位域(例如 Reg.lsw = 0xABCD)的形式访问它。我可以通过声明一个带有匿名结构成员的联合,以及向/从uint32_t 声明赋值和转换运算符来实现这一点。在 little-endian 环境中,代码可能如下所示:

#include <cstdint>
#include <cstdio>

typedef union
  {
  uint32_t val ;
  struct
    {
    uint32_t lsw : 16 ;
    uint32_t msw : 16 ;
    } ;

  operator = (uint32_t n) { val = n ; }
  operator uint32_t() const { return val ; }
  } HWR ;

int main()
  {
  HWR Reg ;
  Reg = 0x12345678 ;
  Reg.lsw = 0xABCD ;
  printf ("%X\n", uint32_t(Reg)) ;
  }

但是现在假设我有一大堆这样的寄存器,每个都有自己的位域布局,并且我有一个头文件FieldDefs.h,将这些位域布局声明为命名结构。如何在上述代码中使用这些命名结构,以便访问 32 位值以及各个位域?我可以这样做:

#include "FieldDefs.h" // Defines struct MyHWR
typedef union
  {
  uint32_t val ;
  struct MyHWR field ;

  operator = (uint32_t n) { val = n ; }
  operator uint32_t() const { return val ; }
  } MyHWRUnion ;

但现在我需要输入 Reg.field.lsw =... 而不是 Reg.lsw =...

有没有办法(在 C++17 中)将已定义的结构声明为联合的匿名成员?如果重要的话,我正在使用 g++ 版本 7.3.0。

【问题讨论】:

  • 当我再次查看问题定义和我的答案时,我觉得我有点过于复杂了。Reg.val 访问是界面的一部分吗?
  • @R2RT:好问题!在我的代码 sn-p 中,它不必是;但我也发现自己想做Reg.val &amp;= 0xFF 之类的事情,这需要val 才能访问。我想我可以写Reg = uint32_t(Reg) &amp; 0xFF
  • 好的,那么我的代码甚至可以简化为在不暴露val 的旧g++s 中工作。将其添加到答案中

标签: c++ c++17 anonymous-class


【解决方案1】:
union
{
// ...
    struct
    {
    // ...
    };

这是一个匿名结构。匿名结构在 C++ 中格式不正确。只有工会可能是匿名的。这与允许匿名结构的 C 不同(自 C11 起)。

有没有办法(在 C++17 中)将已定义的结构声明为联合的匿名成员?

没有。未命名的成员不能有命名的类型。

您需要在未命名成员和预先声明的类之间做出选择。鉴于匿名结构首先是非标准的,我建议使用命名成员和预定义类。也许给它起一个简短的名字以减少冗长。

【讨论】:

  • 我不太担心使用格式错误的代码,只要它可以编译和运行。毕竟,我已经在一个依赖机器的环境中,有硬件寄存器等等。但是你的最后一段在我看来是正确的——我想要的只是无法完成。
  • @TonyK 请记住,如果您使用编译器特定的功能,您不是在编写 C++,而是 C++ 的编译器特定变体。
【解决方案2】:

我想没有人会喜欢这个答案,OP(因为需要 g++ 9.1)和 C++ 大师(UB 闻起来?)都不会喜欢这个答案,但我仍然为修补它而感到有点自豪。

[[no_unique_address]] attribute 出现在 C++20 中,g++ 9.1 已经支持它(即使没有 -std=c++2a 标志)。

如何在这里使用它? By test and trials 似乎如果我们创建标有它的代理成员val 它将获取对象的地址1

因此我们可以创建Proxy 类,它具有operator=(uint32_t)operator uint32_t,将this 视为uint32_t。代理对象没有地址,不会增加使用它的结构的大小。

位域名称必须通过继承添加,它被包装在简单的模板中,以保持一致性,命名为HWR

瞧,我们有 HWR&lt;bitfield&gt; 对象,可以直接由 val 成员分配给 uint32_t,并提供对位域名称的访问权限。

https://godbolt.org/z/N2xEmz

#include <bits/stdint-uintn.h>

#include <cstddef>
#include <cstdint>
#include <cstdio>

// Example bifields, I assumed you have such in "FieldDefs.h"
struct bitfield {
  uint32_t lsw : 16;
  uint32_t msw : 16;
};

struct ThisProxy {
  uint32_t& operator=(uint32_t n) {
    auto& uint = *reinterpret_cast<uint32_t*>(this);
    uint = n;
    return uint;
  }
  operator uint32_t() const { return *reinterpret_cast<const uint32_t*>(this); }
};

template <typename Bitfield>
struct HWR : Bitfield {
  static_assert(sizeof(Bitfield) == 4, "Bad things would happen");
  HWR& operator=(uint32_t n) {
    this->val = n;
    return *this;
  }
  operator uint32_t() const { return this->val; }
  [[no_unique_address]] ThisProxy val;
};

int main() {
  HWR<bitfield> Reg;
  // Sanity check that proxy points at &Reg and does not increase size
  static_assert(offsetof(HWR<bitfield>, val) == 0, "");
  static_assert(sizeof(HWR<bitfield>) == 4, "");

  Reg = 0x12345678;
  Reg.val = 0x8765432A;
  Reg.lsw = 0xABCA;
  printf("%X\n%ld\n", uint32_t(Reg), sizeof(Reg));

  return 0;
}

编辑:

事实证明,Reg.val 的访问不是强制性的,继承的技巧 + reinterpret_cast 可以在 C++20 之前的代码中重用。

template <typename Bitfield> struct HWR : Bitfield {
  static_assert(sizeof(Bitfield) == 4, "Bad things would happen");
  HWR &operator=(uint32_t n) {
    *reinterpret_cast<uint32_t *>(this) = n;
    return *this;
  }
  operator uint32_t() const {
    return *reinterpret_cast<const uint32_t *>(this);
  }
};

仍然有reinterpret_cast 的味道,我需要找出合适的东西来完全推荐这段代码。每当位域可以被底层类型uint32_t 解释时。

1 我不确定P0840R2 是否保证偏移量为 0。

PS。 g++ 向warning: offsetof within non-standard-layout type ‘HWR&lt;bitfield&gt;’ is conditionally-supported [-Winvalid-offsetof] 抱怨,但我没有尝试找到解决方法。

PPS。没有匿名结构!

【讨论】:

  • R2RT,你是一个合我心意的程序员!我已经在我的代码中实现了这个解决方案,它就像一个魅力。
猜你喜欢
  • 2019-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-21
  • 1970-01-01
  • 2012-07-13
  • 1970-01-01
  • 2015-01-30
相关资源
最近更新 更多