#c #c 14 #bitwise-operators #type-punning
#c #c 14 #побитовые операторы #каламбур типа
Вопрос:
Я хочу, чтобы функция constexpr (без UB) вычисляла контрольную сумму сообщения. В соответствии со спецификацией
Контрольная сумма вычисляется путем выполнения исключающего ИЛИ из 16-разрядных слов сообщения из поля заголовка MSG_TYPE (включительно) до последнего поля тела сообщения
Я понял, что я не могу reinterpret_cast
представить тело сообщения в виде массива uint16_t
, поскольку оно выдает UB. Но я не могу использовать std::copy
внутри функции constexpr, поэтому для одного сообщения у меня был рабочий и корректный подход, который использовал битовый сдвиг.
/*!
* brief LRF MESSAGE
*/
struct LrfMes
{
//<! Message ID
constexpr static MessageID message_id = MessageID::lrf;
constexpr static MessageType message_type = MessageType::commandRequiringAResponse;
constexpr static uint32_t message_length = 16;
constexpr uint16_t calc_checksum() const
{
const uint16_t body[]{
uint16_t(uint16_t((uint8_t)lrfFire) << 8 | (uint8_t)setEcho),
uint16_t (uint16_t((uint8_t)setRange) << 8 | (uint8_t)setFreq),
uint16_t(lrfRangeMax >> 16),
uint16_t (lrfRangeMin),
uint16_t (lrfRangeMax >> 16),
uint16_t (lrfRangeMax)
};
uint16_t res = 0;
for (size_t i = 0; i != sizeof(LrfMes) / 2; i)
res ^= body[i];
return res;
}
LrfFire lrfFire;
LrfSetEcho setEcho;
LrfSetRange setRange;
LrfSetFreq setFreq;
uint32_t lrfRangeMin;
uint32_t lrfRangeMax;
};
Помимо того, что она доступна только со стандарта 14 (в идеале она должна быть реализована в 11), я должен написать такой метод для каждой структуры, которая у меня есть.
Но я столкнулся с проблемой, когда у меня есть float в качестве членов структуры.
struct LosSteeringMsg
{
//<! Message ID
constexpr static MessageID message_id = MessageID::losSteering;
constexpr static MessageType message_type = MessageType::commandWithoutResponse;
constexpr static uint32_t message_length = 32;
constexpr uint16_t calc_checksum() const
{
// const uint16_t body[]{
// uint16_t (uint16_t((uint8_t)steeringScr << 8) | (uint8_t)steeringMode),
// uint16_t (uint16_t((uint8_t)resetOffset << 8) | (uint8_t)pauseScan),
// uint16_t (reinterpret_cast<uint32_t>(azmSteering) >> 16) // this does not work
//
// }
uint16_t res = 0;
return res;
}
LosStSCR steeringScr;
LosStMode steeringMode;
LosStResetOffset resetOffset;
LosStPauseScanMes pauseScan;
float azmSteering;
float elevSteering;
float azmAngle;
float elevAngle;
float azmSpeed;
float elevSpeed;
};
Итак, какие у меня варианты?
- Должен ли я просто отказаться от определения этих методов как constexpr? Это действительно упростило бы мою жизнь, дав мне возможность писать только одну функцию, которая использует
std::copy
. - Используется
uint32_t
для хранения значений с плавающей точкой. Хотя это позволит мне вычислить контрольную сумму во время компиляции (например, для тестирования), это не будет иметь большого значения, поскольку мне нужно будет инициализировать целые числа из чисел с плавающей точкой. - Есть ли другой вариант??
Комментарии:
1. Почему вы хотите
calc_checksum
бытьconstexpr
? Есть ли у васconstexpr LosSteeringMsg
где-нибудь переменная со значениями всех элементов данных, известными во время компиляции, для которых вы хотите иметь возможность вычислять контрольную сумму во время компиляции?2. «функция constexpr (без UB)» . Компиляторы должны диагностировать UB в контекстах contant. 🙂
3. @Jarod42 я знаю. Я упомянул об этом, потому что в других ответах есть много предложений использовать объединения или каламбур типа…
4. @IgorTandetnik если вы спрашиваете просто из любопытства, да. У меня есть. У меня есть все возможные тесты во время компиляции, включающие эти структуры, их характеристики и значения.
5. @IgorTandetnik это только ваше мнение. И я не спрашивал об этом, должен ли я делать это во время компиляции или нет. У меня есть сообщения, которые я могу создать для отправки. И я бы предпочел получить недопустимую контрольную сумму во время компиляции.
Ответ №1:
В C 20 вы можете сделать это:
std::bit_cast<std::uint32_t>(azmSteering) >> 16;
До C 20 не было простого решения, которое работало бы в функции constexpr. Это не имеет UB, но независимо от этого не разрешено в функции constexpr:
std::uint32_t u32;
std::memcpy(amp;u32, amp;azmSteering, sizeof azmSteering);
u32 >> 16;
Должен ли я просто отказаться от определения этих методов как constexpr?
До C 20: возможно. Технически может быть возможно извлечь правильные биты, используя операции с плавающей запятой, но это было бы довольно запутанно.
Комментарии:
1. Ну, я знаю о таких прекрасных функциях C 20, но я никак не мог прыгнуть выше 14. Как я уже говорил, копирование в память облегчило бы всю контрольную сумму для каждой структуры сразу. Но я бы использовал ее только в том случае, если нет возможности использовать constexpr. Если вы заявите, что это технически невозможно сделать, я отмечу ваш ответ. Потому что я хотел бы точно знать, возможно ли это.