假设我有一个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版。
答案 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 的地址。
因此,我们可以创建具有Proxy
和operator=(uint32_t)
的{{1}}类,并将operator uint32_t
视为this
。代理对象没有地址,不会增加使用它的结构的大小。
必须通过继承添加位域名称,该名称必须封装在简单模板中,以确保一致性uint32_t
。
Voilà,我们有HWR
个对象,可以由HWR<bitfield>
成员直接分配给uint32_t
,并可以访问位域名称。
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。没有匿名结构!