使用已经定义的结构作为联合的匿名成员

时间:2019-06-03 11:58:27

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

假设我有一个32位硬件寄存器Reg,我希望能够以32位值(例如Reg = 0x12345678)或位域(例如{{1})的形式进行访问}。我可以通过声明与匿名struct成员的联合,并声明与Reg.lsw = 0xABCD之间的赋值和转换运算符来实现此目的。在小端环境中,代码可能如下所示:

uint32_t

但是,现在让我们说一堆这些寄存器,每个寄存器都有自己的位域布局,还有一个头文件#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)) ; } ,用于将这些位域布局声明为命名结构。如何在上面的代码中使用这些命名的结构,以便可以访问32位值以及各个位字段?我可以这样:

FieldDefs.h

但是现在我需要输入#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.lsw =...

有什么方法(在C ++ 17中)将已经定义的结构声明为联合的匿名成员?如果有问题,我正在使用g ++ 7.3.0版。

2 个答案:

答案 0 :(得分:2)

union
{
// ...
    struct
    {
    // ...
    };

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

  

(在C ++ 17中)有什么方法可以将已经定义的结构声明为联合的匿名成员?

不。未命名成员不能具有命名类型。

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

答案 1 :(得分:1)

我想没有人喜欢这个答案,没有OP(因为需要g++ 9.1),也没有C ++专家(UB气味?),但是我还是为修补它感到有些自豪。

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

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

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

必须通过继承添加位域名称,该名称必须封装在简单模板中,以确保一致性uint32_t

Voilà,我们有HWR个对象,可以由HWR<bitfield>成员直接分配给uint32_t,并可以访问位域名称。

https://godbolt.org/z/N2xEmz

val

编辑:

事实证明,#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的技巧可以在C ++ 20之前的代码中重复使用。

reinterpret_cast

仍然有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解释时。

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

PS。 g ++抱怨uint32_t,但我没有尝试找到解决方法。

PPS。没有匿名结构!