Вопрос, апятьже, к "икспердам"

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

есть структура из битовых полей, без методов, размером в 1 байт

	struct TAHTStatus {
	public:
		uint8_t Reserved	: 3;
		bool	Calibrated	: 1;
		bool	Reserved2	: 1;
		enMode	WorkMode	: 2;
		bool	Busy		: 1;
	};

как можно байт ответа устройства привести к типу этой структуры без таких костылей?  

	TAHTStatus GetStatus(void) {
		Wire.requestFrom(FDevAddress,1U); // запрашиваем байт статуса
		uint8_t status = Wire.read();     //  читаем его
		TAHTStatus result;                // создаем временный обьект, чтоб его отдать
		uint8_t *tmp = (uint8_t *)(&result);      //  применяем костыли
		*tmp = status;                            // копируем в них
		return result;                            // и возвращаем 
	}

memcpy() наерна можно применить, но это тоже костыли.  А static_cast<TAHTStatus>(status) не работает, пишет, нет такова конструктора.  Чёртичо, короче, и сбоку бантег.  В Delphi хоть absolute есть. 

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

канеш, так   memcpy(&result, &status, 1);  тоже работает, но ить некрасива же. 

 
rkit
Offline
Зарегистрирован: 23.11.2016

reinterpret_cast. Для static_cast нужно правила объявлять.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

так он же для пойнтеров только, а у меня оба типа-значения одного размера 

Allows any pointer to be converted into any other pointer type

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

наерна, проще будет в структуру конструктор запилить 

	struct TAHTStatus {
	public:
		uint8_t Reserved	: 3;
		bool	Calibrated	: 1;
		bool	Reserved2	: 1;
		enMode	WorkMode	: 2;
		bool	Busy		: 1;

		TAHTStatus(uint8_t value) { memcpy(this, &value, 1); };
	};

Наскока это безопасно?

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

DetSimen пишет:

наерна, проще будет в структуру конструктор запилить 

	struct TAHTStatus {
	public:
		uint8_t Reserved	: 3;
		bool	Calibrated	: 1;
		bool	Reserved2	: 1;
		enMode	WorkMode	: 2;
		bool	Busy		: 1;

		TAHTStatus(uint8_t value) { memcpy(this, &value, 1); };
	};

Наскока это безопасно?

С точки зрения COVID-19? Ну руки воткой протри, штоле!

Дет,  ты ж нормальный программист, а иногда тебя заносит, аж жуть! ;))) По трезвяку верно?

------------------------

Если под "безопасно" ты понимаешь страховку от неработоспособности в новой версии GCC, то Женя тебе объяснит, что такой страховки не бывает! ;))) В комитете - такие затейники! Даже я краснею, бывает.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

wdrakula пишет:

Дет,  ты ж нормальный программист, а иногда тебя заносит, аж жуть! ;))) 

Чойта?  Я реально иногда нихрена в этом лжывом С++ не понимаю, не родной он мне, не привыкну всё, что  никогда нельзя сказать точно, что сегодня с утра значит оператор присваивания, к примеру.  Да и программист из меня - как из авна пуля, особенно в последние годы. :) 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Деда, первое, что нужно сделать - это принудительно указать компилеру на выравнивание структуры по границе байта:

#pragma pack(push,1)
struct TAHTStatus {
public:
	uint8_t Reserved	: 3;
	bool	Calibrated	: 1;
	bool	Reserved2	: 1;
	enMode	WorkMode	: 2;
	bool	Busy		: 1;
};
#pragma pack(pop)

В случае, когда у тебя там будет битовых полей и прочей чепухи больше, чем на один байт - ты будешь уверен, что всё идёт последовательно, без выравниваний, которые применяет компилятор так, как ему хочется.

Второе - можно применить побайтовое копирование, отдав это дело на откуп компилятору, и без memcpy:

#pragma pack(push,1)
struct TAHTStatus {
public:
  uint8_t Reserved  : 3;
  bool  Calibrated  : 1;
  bool  Reserved2 : 1;
  uint8_t  WorkMode  : 2;
  bool  Busy    : 1;
};
#pragma pack(pop)

void setup() {

    uint8_t someByte = 0xD0;
    TAHTStatus status = *reinterpret_cast<TAHTStatus*>(&someByte);
        

}

void loop() {

}

Я только enMode заменил на uint8_t, т.к. не знаю, чего это за тип у тебя.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Дед, ну, конечно, можно и прямо преобразовывать типы, никто не мешает, только аккуратно. Например, можно написать вот так:

enum enMode { Nefig, Nafig, Pofig };

struct TAHTStatus {
public:
	uint8_t Reserved	: 3;
	bool	Calibrated	: 1;
	bool	Reserved2	: 1;
	enMode	WorkMode	: 2;
	bool	Busy		: 1;
};

void setup(void) {
	TAHTStatus kaka = * (TAHTStatus *) & PORTB;
	PORTC = * (uint8_t *) & kaka;
}

void loop(void) {}

Но я бы так писать не стал.  И memcpy тоже не стал бы использовать. Вместо этого я бы сделал из структуры union и использовал бы простое присваивание. Примерно вот так:

enum enMode { Nefig, Nafig, Pofig };

union TAHTStatus {
	inline TAHTStatus(const uint8_t v = 0) { _cover = v; }

	struct {
		uint8_t Reserved	: 3;
		bool	Calibrated	: 1;
		bool	Reserved2	: 1;
		enMode	WorkMode	: 2;
		bool	Busy		: 1;
	};
private:
	uint8_t _cover; 
};

void setup(void) {
	TAHTStatus kaka = PORTB;
}

void loop(void) {}

Обрати внимание, что структура, которая сидит внутри union не имеет никакого имени. Это важно! Именно это позволяет обращаться к её членам напрямую (например, kaka.Calibrated). Если бы у неё было имя, то пришлось бы тащить его всё время.

Но это половинчатое решение. А как быть с обратной задачей, не впихнуть в твою структуру какое-то значение, а наоборот её значение запихать куда-то? Можно добавить переопределение операторов присваивания и приведения типа, но тут засада - у юнионов операторы не переопределяются. Выход, например, такой - запихать union в ещё одну структуру и уже у неё всё, что надо переопределить (при этом помним про то, что без крайней нужды имена никуда не пихаем!). Получается что-то типа:

enum enMode { Nefig, Nafig, Pofig };

struct TAHTStatus {
	union {
		struct {
			uint8_t Reserved	: 3;
			bool	Calibrated	: 1;
			bool	Reserved2	: 1;
			enMode	WorkMode	: 2;
			bool	Busy		: 1;
		};
		uint8_t _cover; 
	};
	inline operator uint8_t & () { return _cover; }
	inline uint8_t operator = (const uint8_t v) { return _cover = v; }
	inline TAHTStatus(const uint8_t v = 0) { _cover = v; }
};

void setup(void) {
	TAHTStatus kaka = PORTB;
	PORTC = kaka;
}

void loop(void) {}

На самом деле, можно ещё кучу способов предложить, но такой, вроде, всё, что нужно делает и к полям можно прямо обращаться, типа kaka.Calibrated

Все рассмотренные решения (включая и твоё с memcpy) абсолютно идентичны по ресурсам (память там и быстродействие) - код получается просто одинаковый.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Спасибо, парни, остановился на этом, если подводных камней нет, то меня полностью устра. 

enum enMode { NOR = 0, CYK = 1, CMD = 2 };

#pragma pack(push,1)
	struct TAHTStatus {
	public:
		uint8_t Reserved	: 3;
		bool	Calibrated	: 1;
		bool	Reserved2	: 1;
		enMode	WorkMode	: 2;
		bool	Busy		: 1;

		TAHTStatus(uint8_t value) { memcpy(this, &value, 1); };
	};
#pragma pack(pop)
.
.
.
.
	TAHTStatus GetStatus(void) {
		Wire.requestFrom(FDevAddress,1U);
		return (Wire.read() & 0xFF);
	}

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

DIYMan пишет:

Деда, первое, что нужно сделать - это принудительно указать компилеру на выравнивание структуры по границе байта:

#pragma pack(push,1)
#pragma pack(pop)

Спасибо, я это всегда делаю теперь на уровне спинного мозга, где надо, и где не надо. :) 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

DetSimen пишет:

Спасибо, парни, остановился на этом, если подводных камней нет, то меня полностью устра. 

В приведённом примере - подводных камней нет, если меня зрение не подводит. Однако, чем тебя не устраивает reinterpret_cast? В твоём случае - ты жёстко привязываешься к байту. В случае с reinterpret_cast - компилятор сам скопирует в структуру всё, что находится по указателю. Расширил объявление структуры - оно автоматически скопируется из источника, приведённого к типу указателя на структуру. Конечно, и этим способом можно себе ногу прострелить, залазя в память не тудыть.

Короче, делай как нравится, вариантов, как видишь, всегда есть ;)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Спасибо. :-)

-NMi-
Offline
Зарегистрирован: 20.08.2018

На срецтво, дет, на срецтво. Виш кака эпидерсия вакрук, срецтво + перец + агурец = вот нашефсё!!!

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

DIYMan пишет:

В приведённом примере - подводных камней нет, если меня зрение не подводит. Однако, чем тебя не устраивает reinterpret_cast?

Ненавижу звёздочки, скопки и их комбинацыи.  :)  Пока разберешься, чо к чему относица - уже, глядишь, и плюшевый вхлам. 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ЕвгенийП пишет:

Обрати внимание, что структура, которая сидит внутри union не имеет никакого имени. Это важно! Именно это позволяет обращаться к её членам напрямую (например, kaka.Calibrated). Если бы у неё было имя, то пришлось бы тащить его всё время.

Вот не знал, категорически блаадарю. :)  Иногда так ломает в union struct пхать, именно из-за обилия точек в такой переменной, аж в глазах рябит, теперь знать буду. :)  Век живи - а дураком памрёшь. 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

-NMi- пишет:
На срецтво, дет, на срецтво. Виш кака эпидерсия вакрук, срецтво + перец + агурец = вот нашефсё!!!

Понятное дело, мы с моим стратегическим запасом мяса и шерсти теперь столько валерянки накупим... 

Скотокот одобряэ. 

Green
Offline
Зарегистрирован: 01.10.2015

Хорошо когда белый - все блохи как на ладони. Не то что у моего чёрного.(