У меня есть структура, чтобы представить 29-битный идентификатор CAN с битовыми полями, как показано ниже.
struct canId
{
u8 priority :3;
u8 reserved :1;
u8 dataPage :1;
u8 pduFormat :8;
u8 pduSpecific :8;
u8 sourceAddress :8;
} iD;
в моем коде я хочу скопировать эту структуру в целочисленную переменную. Что-то вроде:
int newId = iD;
Однако я не уверен, правильно ли это. Кто-нибудь может это прокомментировать?
Изменить: я могу сделать это с помощью оператора сдвига в каждом поле, а затем с помощью побитового ИЛИ, чтобы они были в нужном месте. Но это делает использование структуры с битовыми полями бесполезным в первую очередь.
Для действительно портативного решения вам не следует использовать структуру битовых полей совсем, поскольку расположение поля не определено. Вместо этого используйте целочисленную и явную побитовую арифметику.
Но для более удобного решения вполне достаточно объединения int со структурой битовых полей.
Чтобы объединить безопасность и удобство, вы также можете рассмотреть возможность интеграции тестовой функции, которая переключает поля по одному и проверяет совпадение с желаемыми целыми числами.
union
{
uint32_t packed;
struct canId
{
u8 priority :3;
u8 reserved :1;
u8 dataPage :1;
u8 pduFormat :8;
u8 pduSpecific :8;
u8 sourceAddress :8;
} split;
} iD;
iD.packed= 0; iD.split.priority= 7; if (iD.packed != 0x7) ...
Я надеюсь, что я правильно сделал немного магии … Вы могли бы сделать что-то вроде:
newID = (int)iD.priority
| (int)(iD.reserved) << 3
| (int)(iD.dataPage) << (3 + 1)
| (int)(iD.pduFormat) << (3 + 1 + 1)
| (int)(iD.pduSpecific) << (3 + 1 + 1 + 8)
| (int)(iD.sourceAddress) << (3 + 1 + 1 + 8 + 8)
Но для этого вам понадобится система, в которой int
имеет не менее 32 бит
Оставляя в стороне вопрос о том, что порядок битовых полей определяется реализацией, вы всегда можете добавить битовое поле, чтобы оно стало 32-битным, а затем memcpy
это в ваш int
:
struct canId
{
u8 priority :3;
u8 reserved :1;
u8 dataPage :1;
u8 pduFormat :8;
u8 pduSpecific :8;
u8 sourceAddress :8;
u8 _padding :3;
} iD;
int newId = 0;
static_assert(sizeof(iD) <= sizeof(newId), "!");
memcpy(&newId, &iD, sizeof(iD));
Это совершенно четко определенное поведение. Наберите через union
не является.
безопасный Я могу представить, как я делаю это вручную:
int canIdToInt(canId id) {
int temp = 0;
int offset = 0;
temp |= id.sourceAddress << offset; offset += 8;
temp |= id.pduSpecific << offset; offset += 8;
temp |= id.pduFormat << offset; offset += 8;
temp |= id.dataPage << offset; offset += 1;
temp |= id.reserved << offset; offset += 1;
temp |= id.priority << offset; // offset += 3; redundant
return temp;
}
Конечно, вы можете скрыть всю смещение за макросом, чтобы сделать его немного чище.
#define START_WRITE int temp=0,offset=0
#define RESULT temp
#define ADD_VALUE(X) temp |= X << offset; offset += sizeof(X)
На самом деле, sizeof
не будет вести себя так, как ожидалось, но есть другой макрос что будет.
struct Id
{
std::uint32_t bytes;
// only considers least three significant bytes of x
void priority(std::uint8_t x)
{
bytes |= x & 0x03;
}
// only considers the least signifficant byte of x
// sets byte into 4th bit of target value
void reserved(std::uint8_t x)
{
bytes |= ((x & 0x01) << 4);
}
// ...
};
Код клиента:
Id id;
id.priority(0x1);
id.reserved(0x0); // reset the "reserved" bit to zero
В частности, это опасно делать:
u8 priority :3;
u8 reserved :1;
Представление этих битов, как вы указываете, зависит от компилятора (и не является частью стандарта).
Если выбранная реализация этого формата в OP еще не установлена, возможно, это может помочь.
Вы можете создать класс-оболочку, который управляет словом соответствующего размера и возвращает / устанавливает соответствующие части для вызывающей стороны, как это предлагается user3528438.
Или вы можете сделать то, что я делаю (хотя не для CAN) и создать множественный Обертки: для каждого «подполя» сделайте struct
держа uint_or_whatever_t
плюс функции-члены /operator
s, чтобы извлечь соответствующие биты. затем union
несколько таких struct
вместе.
Подождите! Убери свои вилами! Последний шаблон исключение, специально разрешенное Стандартом для чередующихся чтений членов в общая начальная последовательность (посмотрите) стандартного макета struct
в union
— гарантировать, что они определенно обращаются к одной и той же памяти / не оптимизированы.
Определив, что мы находимся в Defined Behavior Land, реальное преимущество состоит в том, как это аккуратно избегает определенной в реализации компоновки «собственных» битовых полей: вы, не ваш компилятор, определите, как соответствующие биты получить / установить. Конечно, вы можете сделать это вручную в C, используя встроенные функции bitmanip или вспомогательные функции — я это делал раньше, — но с помощью класса все это скрыто в реализации класса, как и должно быть.
например
class CanPriority {
// the raw memory
uint32_t m_raw;
public:
// getter/setter methods or operators
uint8_t get() const { /* extract, shift... */ }
// etc.
};
class CanReserved {
uint32_t m_raw;
public:
uint8_t get() const { /* ... */ }
// ...
};
union CanId {
CanPriority priority;
CanReserved reserved;
// etc.
};
Кстати, я сделал довольно дикие вещи с очень сложными версиями этого паттерна для других случаев — представьте, что вы можете сделать, когда template
s добавляются, например — и только когда-либо имели прекрасные результаты, даже при -O3 -flto
,
Я предполагаю, что вы хотели просто взять эти 29 бит из памяти и упаковать их в int за одну операцию приведения.
Боюсь, вы не можете быть уверены, что ваша структура упакована в 29 бит, потому что ссылка говорит:
Несколько смежных битовых полей обычно упаковываются вместе (хотя это поведение определяется реализацией)
Поэтому вам нужно использовать решение fritzone и написать аналогичный код для его преобразования.
Другое возможное решение — изначально обернуть struct в целое и просто обернуть геттеры и сеттеры, чтобы выбрать правильные биты.
Если это только C ++, то вы можете использовать reinterpret_cast
:
struct canId {
u8 priority :3;
u8 reserved :1;
u8 dataPage :1;
u8 pduFormat :8;
u8 pduSpecific :8;
u8 sourceAddress :8;
int &raw() {
return *reinterpret_cast<int *>(this);
}
} iD;
Затем:
int data = iD.raw();