arduino回调函数是什么_为什么只有静态成员函数才能作为回调函数?

非静态成员函数由于隐含的`this`指针不能直接作为Win32回调函数,因为回调函数通常需要特定的签名。通过传递`this`指针作为上下文参数可以解决这个问题。文章讨论了如何利用`stdcall`调用约定匹配回调函数的栈布局,并建议不要直接转换函数指针,以避免潜在问题。移植性是关键,即使在调用约定巧合的情况下,代码应保持可工作性。
摘要由CSDN通过智能技术生成

隐藏的this指针

对于非静态成员函数来说,它携带了一个隐藏的”this”指针,这导致它不能满足Win32回调函数签名的要求,这样的结果就是:一个非静态成员函数,不能作为一个合法的Win32回调函数。
幸运的是,几乎所有的回调函数都提供了一些方法来感知调用上下文。你可以将这个”this”指针作为一个上下文环境来重构代码,下面是一个例子:

e8e9c7af1cde89780acff668acf56283.png

有一些回调函数签名将它的第一个参数作为指示上下文的参数(也叫做”引用数据”)。凑巧的是,这个隐藏的”this”指针也正好是第一个参数。回顾我们之前关于调用约定的文章,可以知道对于成员函数的__stdcall调用约定也刚好匹配我们预期的栈布局。下面我们看一个WAITFORTIMERCALLBACK的例子:

6b7d0e7ee89fa2f6674f181f0105dc3a.png

请注意:”thiscall”调用约定不能匹配上面的场景,但是两个”__stdcall”却可以。幸运的是,编译器足够聪明,它能识别这个this指针,并对上面的静态成员函数s_ThreadProc进行优化。

3ca04de4a4763fa342b164d74faa48f0.png

如果你有机会观察一下编译器为s_ThreadProc生成的汇编代码,则你会发现:函数的实现实际上就是一个跳转指令,因为编译器已经意识到了两种不同的调用约定,所以这里不需要做任何的翻译工作,只是跳转即可。

fec2522a785fbe88fdb793e4d7b3461d.png

有些人可能想着更进一步:直接对CreateThread的第二个参数强制转换为LPTHREAD_START_ROUTINE,这样就可以完全不需要定义s_ThreadProc这个函数了。我强烈建议不这么做:因为我看到很多人都曾在转换函数指针这个上吃过亏(关于这个议题,我后面会专门讲一讲)。

总结

在上面的代码中,虽然我们考虑到了两个__stdcall调用约定,但是我们并没有依赖它们。如果调用约定的巧合不能像预期那样的工作,则代码依然会正常工作。这个对于将代码移植到其他架构的时候很重要,特别是当这个架构没有上面所述的调用约定的巧合的时候。

最后

Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《Why do member functions need to be “static” to be used as a callback?》

3c07be683c4d070bd2c46aabfb186756.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值