2010-03-10 103 views
9

所以,我使用FMOD API,它確實是一個C API。C API函數回調到C++成員函數代碼

這不是那麼糟糕或什麼。它只是它不能很好地與C++代碼進行交互。

例如,使用

FMOD_Channel_SetCallback(channel, callbackFunc) ; 

它希望callbackFunc C風格的功能,但我想它傳遞一個類的成員函數。

我結束了使用Win32技巧,使成員函數靜態。然後它作爲FMOD的回調。

現在我必須破解我的代碼,使一些成員靜態,只是爲了解釋FMOD的C-ness。

我不知道它是否可能在FMOD中,或者是否有解決方法將回調鏈接到特定的C++對象的實例成員函數(而不是靜態函數)。它會更光滑。

回答

11

您不能直接傳遞成員函數。成員函數具有隱含參數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

1

對於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

0

你需要使用一個蹦牀和存儲的指針,你想在一個全局或靜態變量呼籲成員函數的對象,即

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

我想這應該是這樣工作的:
您可以撥打FMOD_Channel_SetUserData將一些用戶數據分配到頻道。這個用戶數據應該是一個指向處理事件的C++對象的指針。 然後,您應該編寫C風格的回調函數,通過調用FMOD_Channel_GetUserData來提取該對象,然後在該對象上調用您的C++實例方法。

+0

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

2

有一個不可移植的,很漂亮的hackish解決方案,其優點是至少是線程安全的,而「蹦牀」方法則不是。

您可以即時生成實際功能的機器代碼。基本思想是你有一個你的回調函數的模板,它接受一個對象指針和一個成員函數指針,並給你一堆堆內存,你可以把它作爲C回調函數傳遞給庫,將在調用時轉向並調用該對象上的成員函數。

這很麻煩,你必須爲任何新的平臺提供一個實現(任何時候調用約定改變),但它的工作原理是線程安全的。 (當然你也必須小心DEP)。另一個線程安全的解決方案是求助於線程本地存儲(假設您知道回調會在您所做的調用的同一線程上發生)。

請參閱http://www.codeproject.com/KB/cpp/GenericThunks.aspx瞭解如何生成thunk的示例。