So I tried both can_helpers.hpp and can_helpers.hpp but neither initially compiled under Arduino.
Here it the build output with Wetmelon’s repo’s version:
include/CAN-Helpers\can_helpers.hpp: In instantiation of 'constexpr T can_getSignal(const can_Message_t&, size_t, size_t, bool) [with T = long unsigned int; size_t = unsigned int]':
src\ODriveFlexCAN.h:493:127: required from here
include/CAN-Helpers\can_helpers.hpp:51:5: error: uninitialized variable '<anonymous>' in 'constexpr' function
};
^
include/CAN-Helpers\can_helpers.hpp:47:11: note: 'union can_getSignal(const can_Message_t&, size_t, size_t, bool) [with T = long unsigned int; size_t = unsigned int]::<anonymous>' has no user-provided default
constructor
union {
^
include/CAN-Helpers\can_helpers.hpp:48:18: note: and the implicitly-defined constructor does not initialize 'uint64_t can_getSignal(const can_Message_t&, size_t, size_t, bool) [with T = long unsigned int; size_t = unsigned int]::<anonymous union>::tempVal'
uint64_t tempVal;
^
*** [.pio\build\teensy41\src\main.cpp.o] Error 1
According to cppreference,
It’s undefined behavior to read from the member of the union that wasn’t most recently written. Many compilers implement, as a non-standard language extension, the ability to read inactive members of a union.
So maybe the Arduino compiler hasn’t implemented this behaviour? Dunno, defeated for now.
Here’s the build output from the ODrive firmware version, which doesn’t use union
:
include/ODrive\Firmware\communication\can\can_helpers.hpp: In instantiation of 'constexpr T can_getSignal(can_Message_t, uint8_t, uint8_t, bool) [with T = long unsigned int; uint8_t = unsigned char]':
src\ODriveFlexCAN.h:493:127: required from here
include/ODrive\Firmware\communication\can\can_helpers.hpp:44:7: error: uninitialized variable 'retVal' in 'constexpr' function
T retVal;
^
*** [.pio\build\teensy41\src\main.cpp.o] Error 1
Ok, seems easy enough to fix. This…
T retVal;
std::memcpy(&retVal, &tempVal, sizeof(T));
return retVal;
…to this…
T* retVal = (T*)std::malloc(sizeof(T));
std::memcpy(retVal, &tempVal, sizeof(T));
return *retVal;
And now can_getSignal
works in my class. Next up to test is can_setSignal
.
Tbf, there might be ways to configure the compiler that would allow these patterns. I have not looked in that direction.
I’ve never compiled the ODrive fw myself, but I’d bet money there’s at least a compiler warning associated with that bit of code.