C API函数回调到C++成员函数代码

问题描述:

所以,我使用FMOD API,它确实是一个C API。C API函数回调到C++成员函数代码

这不是那么糟糕或什么。它只是它不能很好地与C++代码进行交互。

例如,使用

FMOD_Channel_SetCallback(channel, callbackFunc) ; 

它希望callbackFunc C风格的功能,但我想它传递一个类的成员函数。

我结束了使用Win32技巧,使成员函数静态。然后它作为FMOD的回调。

现在我必须破解我的代码,使一些成员静态,只是为了解释FMOD的C-ness。

我不知道它是否可能在FMOD中,或者是否有解决方法将回调链接到特定的C++对象的实例成员函数(而不是静态函数)。它会更光滑。

您不能直接传递成员函数。成员函数具有隐含参数this,而C函数不具有。

您将需要创建一个蹦床(不确定回调的签名,所以只需在这里做一些事情)。

extern "C" int fmod_callback(... args ...) 
{ 
    return object->member(); 
} 

一个问题是该对象指针来自哪里。希望fmod为你提供一个通用的上下文值,当你的回调被创建时,你会提供给你(你可以传入对象指针)。

如果不是,你只需要使它成为一个全局访问它。

+1

+1是的,你必须做一个蹦床,但它们非常粗糙(如果你想避免全局和所有这些)! :-(如果API的设计正确的话...... – 2010-03-10 20:38:02

+1

如果C++成员函数抛出,你还需要担心该怎么做。这个“蹦床”术语从何而来? – 2010-03-10 20:43:54

+1

@NeilButterworth - I不要回想起我第一次听到这种描述为“蹦床”的地方,但我的用法的一个参考是*(http://en.wikipedia.org/wiki/Trampoline_%28computers%29)'当将部分代码与不兼容的调用约定,蹦床用于将调用者的约定转换为被调用者的惯例。' – 2010-03-10 21:52:19

对于C回调,仅使用一个函数指针(并且没有额外的单独对象指针)是一个破碎的设计,根据我的愚见。

如果该函数是FMOD_Channel_SetCallback(channel, callbackFunc, callbackObj),那么您的静态方法只需要该对象的一个​​实例,然后调用callbackObj->func()(显然可以是非静态的)。

+1

建议将此发送给FMOD人员!请! – bobobobo 2010-03-10 20:45:26

+1

@bobobobo:查看Denis的回答。我认为API已经支持这个(显然他研究的FMOD比我更多:-P)。我会把我的答案作为其他API设计人员的警示故事,但至少您确实有解决方案。 – 2010-03-10 21:07:47

你需要使用一个蹦床和存储的指针,你想在一个全局或静态变量呼吁成员函数的对象,即

Object *x; 
void callback_trampoline() { x->foobar(); } 
... 
FMOD_Channel_SetCallback(CHANNEL, callback_trampoline); 

我想这应该是这样工作的:
您可以拨打FMOD_Channel_SetUserData将一些用户数据分配到频道。这个用户数据应该是一个指向处理事件的C++对象的指针。 然后,您应该编写C风格的回调函数,通过调用FMOD_Channel_GetUserData来提取该对象,然后在该对象上调用您的C++实例方法。

+0

+1哦,胜利!所以,API没有我想象的那么糟糕。 – 2010-03-10 21:07:10

有一个不可移植的,很漂亮的hackish解决方案,其优点是至少是线程安全的,而“蹦床”方法则不是。

您可以即时生成实际功能的机器代码。基本思想是你有一个你的回调函数的模板,它接受一个对象指针和一个成员函数指针,并给你一堆堆内存,你可以把它作为C回调函数传递给库,将在调用时转向并调用该对象上的成员函数。

这很麻烦,你必须为任何新的平台提供一个实现(任何时候调用约定改变),但它的工作原理是线程安全的。 (当然你也必须小心DEP)。另一个线程安全的解决方案是求助于线程本地存储(假设您知道回调会在您所做的调用的同一线程上发生)。

请参阅http://www.codeproject.com/KB/cpp/GenericThunks.aspx了解如何生成thunk的示例。