2010-03-10 18 views
9

Quindi, sto usando l'API FMOD ed è davvero un C api.C callback delle funzioni API nel codice funzione membro C++

Non è male o altro. Solo che non si interfaccia bene con il codice C++.

Ad esempio, utilizzando

FMOD_Channel_SetCallback(channel, callbackFunc) ; 

Vuole una funzione C-stile per callbackFunc, ma voglio passare una funzione membro di una classe.

Ho finito per utilizzare il trucco Win32 per questo, rendendo la funzione membro statica. Funziona quindi come una richiamata in FMOD.

Ora devo suddividere il mio codice per rendere statici alcuni membri, solo per tenere conto della C-nità di FMOD.

Mi chiedo se è possibile in FMOD o se c'è un problema attorno a collegare il callback a una funzione membro di istanza dell'oggetto C++ specifico (non una funzione statica). Sarebbe molto più agevole.

risposta

11

Non è possibile passare direttamente una funzione membro. Una funzione membro ha il parametro implicito this e le funzioni C no.

Avrai bisogno di creare un trampolino (non sicuro della firma del callback, quindi fai qualcosa di casuale qui).

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

Un problema è da dove viene il puntatore a oggetto. Si spera che fmod ti fornisca un valore di contesto generico che ti verrà fornito quando viene effettuata la richiamata (puoi quindi passare il puntatore all'oggetto).

In caso contrario, sarà necessario renderlo globale per accedervi.

+1

+1 Sì, devi fare un trampolino, ma sono così nodosi (se vuoi evitare i globali, e tutto il resto)! :-(Se l'API è stata progettata correttamente in primo luogo .... –

+1

Devi anche preoccuparti di cosa fare se le funzioni del membro C++ vengono generate. E da dove viene questo termine "trampolino"? –

+1

@NeilButterworth - I non ricordo dove ho sentito per la prima volta questo descritto come un "trampolino", ma un riferimento per il mio utilizzo è Wikipedia (http://en.wikipedia.org/wiki/Trampoline_%28computers%29) 'Quando si interfacciano pezzi di codice con Convenzioni di chiamata incompatibili, un trampolino viene utilizzato per convertire la convenzione del chiamante nella convenzione del callee. –

1

L'utilizzo solo di un puntatore a funzione (e nessun puntatore a oggetti separato aggiuntivo) per un callback in C è un design danneggiato, a mio modesto parere.

Se la funzione fosse, invece, FMOD_Channel_SetCallback(channel, callbackFunc, callbackObj), il metodo statico prende solo un'istanza dell'oggetto, quindi chiama callbackObj->func() (che ovviamente può essere non statico).

+1

Suggerisci questo al popolo FMOD! Per favore! – bobobobo

+1

@bobobobo: vedi la risposta di Denis. Penso che l'API lo supporti già (ovviamente ha ricercato FMOD più di quanto ho fatto io :-P). Terrò la mia risposta come ammonimento per altri progettisti di API, ma almeno tu hai una soluzione. –

0

è necessario utilizzare un trampolino e memorizzare il puntatore all'oggetto che si desidera ottenere la funzione di membro invitato in una variabile globale o statico, cioè

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

Credo che dovrebbe funzionare in questo modo:
È possibile assegnare alcuni dati utente al canale chiamando lo FMOD_Channel_SetUserData. Questi dati utente dovrebbero essere un puntatore al tuo oggetto C++ che gestisce gli eventi. Quindi dovresti scrivere callback in stile C che estrae quell'oggetto chiamando lo FMOD_Channel_GetUserData e poi chiama il metodo di istanza C++ su quell'oggetto.

+0

+1 Oh, vinci! Quindi, l'API non è rotta come pensavo. –

2

Esiste una soluzione non portatile e piuttosto hackish che ha il vantaggio di essere almeno thread-safe, che i metodi "trampolino" non lo sono.

È possibile generare al volo il codice della macchina funzione reale. L'idea di base è che si dispone di un modello per la funzione di richiamata che accetta un puntatore a oggetti e un puntatore a funzione membro e fornisce un blocco di memoria heap che è possibile passare alla libreria come una funzione di richiamata C, che quando chiamato, si girerà e chiamerà la funzione membro su quell'oggetto.

È complicato, e dovrai fornire un'implementazione per qualsiasi nuova piattaforma (ogni volta che la convenzione di chiamata cambia), ma funziona, è thread-safe. (Ovviamente dovrai anche fare attenzione a DEP). L'altra soluzione thread-safe consiste nel ricorrere all'archiviazione thread-local (presumendo che si sappia che la richiamata avverrà sullo stesso thread della chiamata effettuata).

Vedere http://www.codeproject.com/KB/cpp/GenericThunks.aspx per un esempio di come si può fare per generare thunk.

Problemi correlati