C API function callbacks into C++ member function code
Asked Answered
O

5

10

So, I'm using the FMOD api and it really is a C api.

Not that that's bad or anything. Its just it doesn't interface well with C++ code.

For example, using

FMOD_Channel_SetCallback( channel, callbackFunc ) ;

It wants a C-style function for callbackFunc, but I want to pass it a member function of a class.

I ended up using the Win32 trick for this, making the member function static. It then works as a callback into FMOD.

Now I have to hack apart my code to make some of the members static, just to account for FMOD's C-ness.

I wonder if its possible in FMOD or if there's a work around to link up the callback to a specific C++ object's instance member function (not a static function). It would be much smoother.

Otisotitis answered 10/3, 2010 at 20:30 Comment(0)
P
13

You cannot directly pass a member function. A member function has the implicit parameter this and C functions don't.

You'll need to create a trampoline (not sure the signature of the callback, so just doing something random here).

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

One issue is where does that object pointer come from. Hopefully, fmod gives you a generic context value that will be provided to you when your callback is made (you can then pass in the object pointer).

If not, you'll just need to make it a global to access it.

Persecution answered 10/3, 2010 at 20:36 Comment(5)
+1 Yes, you do have to make a trampoline, but they're so gnarly (if you want to avoid globals, and all that)! :-( If the API were designed correctly in the first place....Intimacy
You also need to worry about what to do if the C++ member functions throws. And where did this "trampoline" term come from?Despinadespise
@NeilButterworth - I don't recall where I first heard this described as a "trampoline", but one reference for my usage is Wikipedia (en.wikipedia.org/wiki/Trampoline_%28computers%29) When interfacing pieces of code with incompatible calling conventions, a trampoline is used to convert the caller's convention into the callee's convention.Persecution
@R Samuel Klatchko Thanks for the link. So basically another word for a "thunk" I think.Despinadespise
@NeilButterworth - definitely - I consider a "trampoline" and a "thunk" synonymous.Persecution
L
5

I guess it supposed to work like this:
You can assign some user data to channel by calling FMOD_Channel_SetUserData. This user data should be a pointer to your C++ object that handles events. Then you should write C-style callback that extracts that object by calling FMOD_Channel_GetUserData and then calls your C++ instance method on that object.

Lingual answered 10/3, 2010 at 21:4 Comment(0)
N
2

There is a non-portable, and pretty hackish solution that has the advantage of at least being thread-safe, which the "trampoline" methods are not.

You can generate the actual function machine code on the fly. The basic idea is that you have a template for your call-back function that takes an object pointer and a member-function pointer and gives you a block of heap memory that you can pass to the library as a C call-back function, that will, when called, turn around and call the member function on that object.

It's messy, and you'll have to provide an implementation for any new platform (any time the calling convention changes), but it works, is thread-safe. (Of course you'll also have to watch out for DEP). The other thread-safe solution is to resort to thread-local storage (assuming that you know the call-back will happen on the same thread as the call you made).

See http://www.codeproject.com/KB/cpp/GenericThunks.aspx for an example of how you could go about generating thunks.

Nancienancy answered 10/3, 2010 at 21:9 Comment(0)
I
1

Using only a function pointer (and no additional separate object pointer) for a C callback is a broken design, in my humble opinion.

If the function were, instead, FMOD_Channel_SetCallback(channel, callbackFunc, callbackObj), then your static method just takes an instance of the object, then calls callbackObj->func() (which obviously can be non-static).

Intimacy answered 10/3, 2010 at 20:36 Comment(2)
Suggest this to the FMOD people! Please!Otisotitis
@bobobobo: See Denis's answer. I think the API already supports this (obviously he's researched FMOD more than I have :-P). I'll keep my answer up as a cautionary tale for other API designers, but, at least you do have a solution.Intimacy
N
0

you need to use a trampoline and store the pointer to the object you want to get the member function called on in a global or static variable, i.e.

Object *x;
void callback_trampoline() { x->foobar(); }
...
FMOD_Channel_SetCallback(CHANNEL, callback_trampoline);
Niggard answered 10/3, 2010 at 20:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.