Reputation: 6240
This is standard Arduino library. On line 92 https://github.com/arduino/ArduinoCore-megaavr/blob/master/cores/arduino/WInterrupts.cpp the user supplied function void(*)(void)
is cast into void(*)(void *)
(voidFuncPtrParam is void(*)(void *)
)
How does this work? On line 138, the user supplied function is always called with a void *
argument, regardless if it was void(*)(void)
or void(*)(void *)
. Can you even do this safely?
Upvotes: 1
Views: 123
Reputation: 140930
Is this function casting safe?
The cast itself is always safe. You can always cast function pointer to any function type.
The call at WInterrupts.cpp#L138) is indeed undefined behavior, as it calls void (void)
function via void (void*)
pointer.
How does this work?
The code was written specifically for megaavr to be run on megaavr compiled with compiler for megaavr. Because of how arguments are passed to functions additional arguments are just set up in register on caller side and just ignored on called side.
Can you even do this safely?
Safe code would do a trampoline probably sacrificing performance.
static void trampoline_call_user_func(void *param) {
// let's be super super safe and use memcpy instead of cast to void*
void (*userFunc)(void);
memcpy(&userFunc, ¶m, sizeof(fptr));
userFunc();
}
void attachInterrupt(uint8_t pin, void (*userFunc)(void), PinStatus mode) {
// let's be super super safe and use memcpy instead of cast to void*
void *param;
static_assert(sizeof(void*) >= sizeof(userFunc), "");
memcpy(¶m, &userFunc, sizeof(userFunc));
attachInterruptParam(pin, trampoline_call_user_func, param, NULL);
}
but on any modern architecture just passing function pointer via void*
"will work" (although a function pointer is not required to be able to cast to void*
), just:
static void trampoline_call_user_func(void *param) {
// let's be super super safe and use memcpy instead of cast to void*
void (*userFunc)(void) = param;
userFunc();
}
void attachInterrupt(uint8_t pin, void (*userFunc)(void), PinStatus mode) {
attachInterruptParam(pin, trampoline_call_user_func, userFunc, NULL);
}
Upvotes: 4