Полагаю, никому не понравится этот ответ, ни OP (поскольку требует g++ 9.1
), ни гуру C ++ (пахнет UB?), Но я все еще немного горжусь тем, что возиться с ним.
Есть *Атрибут 1004 *[[no_unique_address]]
входит в C ++ 20, а g ++ 9.1 уже поддерживает его (даже без флага -std=c++2a
).
Как его можно использовать здесь? При тестировании и испытаниях кажется, что если мы создадим прокси-член val
, помеченный им, он получит адрес объекта 1 .
Таким образом, мы можем создать Proxy
класс с operator=(uint32_t)
и operator uint32_t
, который обрабатывает this
как uint32_t
.Прокси-объект не имеет адреса, не увеличивает размер структуры, которая его использует.
Имена битовых полей должны быть добавлены наследованием, заключенным в простой шаблон, для согласованности с именем HWR
.
Вуаля, у нас есть HWR<bitfield>
объект, который может быть назначен uint32_t
напрямую, val
членом и дает доступ к именам битовых полей.
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 Я не уверен, когда смещение 0 гарантируется P0840R2 .
PS.g ++ жалуется с warning: offsetof within non-standard-layout type ‘HWR<bitfield>’ is conditionally-supported [-Winvalid-offsetof]
, но я не пытался найти обходной путь для этого.
PPS.Нет анонимных структур!