这个函数转换安全吗?

Is this function casting safe?

这是标准的 Arduino 库。在第 92 行 https://github.com/arduino/ArduinoCore-megaavr/blob/master/cores/arduino/WInterrupts.cpp 中,用户提供的函数 void(*)(void) 被转换为 void(*)(void *)(voidFuncPtrParam 是 void(*)(void *)

这是如何工作的?在第 138 行,用户提供的函数总是使用 void * 参数调用,无论它是 void(*)(void) 还是 void(*)(void *)。你能安全地做到这一点吗?

Is this function casting safe?

演员本身总是安全的。您始终可以将函数指针转换为任何函数类型。

WInterrupts.cpp#L138) 处的调用确实是未定义的行为,因为它通过 void (void*) 指针调用 void (void) 函数。

How does this work?

代码是专门为 megaavr 编写的 运行 在使用 megaavr 编译器编译的 megaavr 上。由于 arguments are passed to functions 其他参数只是在调用方的寄存器中设置,而在被调用方则被忽略。

Can you even do this safely?

安全代码会做蹦床,可能会牺牲性能。

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, &param, 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(&param, &userFunc, sizeof(userFunc));

   attachInterruptParam(pin, trampoline_call_user_func, param, NULL);
}

但是在任何现代架构上,只要通过 void* 传递函数指针“就可以”(尽管函数指针不需要能够转换为 void*),只是:

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);
}