使用已定义的结构作为联合的匿名成员
Using an already-defined struct as an anonymous member of a union
假设我有一个 32 位硬件寄存器 Reg
,我希望能够以 32 位值(例如 Reg = 0x12345678
)或位域(例如 Reg.lsw = 0xABCD
).我可以通过声明一个具有匿名结构成员的联合,并声明赋值和转换运算符 to/from 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)) ;
}
但是现在假设我有一大堆这样的寄存器,每个都有自己的位域布局,并且我有一个头文件 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 版。
union
{
// ...
struct
{
// ...
};
这是一个匿名结构。匿名结构在 C++ 中是 ill-formed。只有工会可以是匿名的。这与允许匿名结构的 C 不同(自 C11 起)。
Is there any way (in C++17) to declare an already defined struct as an anonymous member of a union?
没有。未命名的成员不能有命名类型。
您需要在未命名成员和 pre-declared class 之间做出选择。鉴于匿名结构首先是 non-standard,我建议使用命名成员和 pre-defined class。也许给它一个简短的名称以尽量减少冗长。
我想 none 会喜欢这个答案,无论是 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
class,其中包含 operator=(uint32_t)
和 operator uint32_t
,将 this
视为 uint32_t
。代理对象没有地址,不会增加使用它的结构的大小。
位域名称必须通过继承来添加,它被包裹在简单的模板中,以保持命名的一致性 HWR
。
瞧,我们有 HWR<bitfield>
对象,它可以由 val
成员直接分配给 uint32_t
,并提供对位域名称的访问。
#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
的技巧可以在 pre-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<bitfield>’ is conditionally-supported [-Winvalid-offsetof]
,但我没有尝试找到解决方法。
PPS。没有匿名结构!
假设我有一个 32 位硬件寄存器 Reg
,我希望能够以 32 位值(例如 Reg = 0x12345678
)或位域(例如 Reg.lsw = 0xABCD
).我可以通过声明一个具有匿名结构成员的联合,并声明赋值和转换运算符 to/from 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)) ;
}
但是现在假设我有一大堆这样的寄存器,每个都有自己的位域布局,并且我有一个头文件 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 版。
union
{
// ...
struct
{
// ...
};
这是一个匿名结构。匿名结构在 C++ 中是 ill-formed。只有工会可以是匿名的。这与允许匿名结构的 C 不同(自 C11 起)。
Is there any way (in C++17) to declare an already defined struct as an anonymous member of a union?
没有。未命名的成员不能有命名类型。
您需要在未命名成员和 pre-declared class 之间做出选择。鉴于匿名结构首先是 non-standard,我建议使用命名成员和 pre-defined class。也许给它一个简短的名称以尽量减少冗长。
我想 none 会喜欢这个答案,无论是 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
class,其中包含 operator=(uint32_t)
和 operator uint32_t
,将 this
视为 uint32_t
。代理对象没有地址,不会增加使用它的结构的大小。
位域名称必须通过继承来添加,它被包裹在简单的模板中,以保持命名的一致性 HWR
。
瞧,我们有 HWR<bitfield>
对象,它可以由 val
成员直接分配给 uint32_t
,并提供对位域名称的访问。
#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
的技巧可以在 pre-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<bitfield>’ is conditionally-supported [-Winvalid-offsetof]
,但我没有尝试找到解决方法。
PPS。没有匿名结构!