I want a constexpr function (without UB) to calculate a checksum of a message. According to the specification
The Checksum is computed by running an exclusive OR of the 16-bit words of the message from header field MSG_TYPE (inclusive) up to the last field of the message body
I've figured out, that I can't reinterpret_cast
the message body as an array of uint16_t
, as it yields UB. But I cannot use std::copy
inside a constexpr function, so for one message I had a working and valid approach that used bitshifting.
/*!
* \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;
};
Apart from being available only since 14 standard (ideally it must be implemented in 11), I have to write such method for each and every structure I have.
But I have faced a problem when I have floats as struct members.
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;
};
So, what are my options?
- Should I just give up on defining these methods as constexpr? This would actually simplify my life, giving me an ability to write only one function that uses
std::copy
. - Use
uint32_t
to store float values. Though it will allow me to calculate a checksum at compile time (for testing, for example), it wouldn't make much difference since I'll need to initialize integers from floats. - Any other option??
In C++20, you can do this:
Prior to C++20 there is no simple solution that works in a constexpr function. This has no UB, but is regardless not allowed in constexpr function:
Prior to C++20: Maybe. Technically it may be possible to extract the correct bits using floating point operations but it would be quite convoluted.