Function Pointers in Objective C
Typically, you need two pieces of information to call back into Objective-C; the method to be invoked and the object to invoke it upon. Neither just a selector or just the IMP -- the instanceMethodForSelector:
result -- will be enough information.
Most callback APIs provide a context pointer that is treated as an opaque value that is passed through to the callback. This is the key to your conundrum.
I.e. if you have a callback function that is declared as:
typedef void (*CallBackFuncType)(int something, char *else, void *context);
And some API that consumes a pointer of said callback function type:
void APIThatWillCallBack(int f1, int f2, CallBackFuncType callback, void *context);
Then you would implement your callback something like this:
void MyCallbackDude(int a, char *b, void *context) { [((MyCallbackObjectClass*)context) myMethodThatTakesSomething: a else: b];}
And then you would call the API something akin to this:
MyCallbackObjectClass *callbackContext = [MyCallbackObjectClass new];APIThatWillCallBack(17, 42, MyCallbackDude, (void*)callbackContext);
If you need to switch between different selectors, I would recommend creating a little glue class that sits between the callback and the Objective-C API. The instance of the glue class could contain the configuration necessary or logic necessary to switch between selectors based on the incoming callback data.
You can do it with @selector
and the SEL
type:
SEL sel = @selector(myMethod:);/* Equivalent to [someObject myMethod:Paramater] */[someObject performSelector:sel withObject:Parameter]
There is also the IMP
type...
IMP imp = [someObject methodForSelector:sel];/* don't remember if this syntax is correct; it's been a while... * the idea is that it's like a function pointer. */imp(someObject, sel, Parameter);
Update based on your comments
If you don't want to have to specify the object, in this case you are asking for something awfully non-portable. Essentially you want lambda expressions which is not a feature of C, though it's coming in C++0x.
So my solution is going to be "out there", sketchy, and non portable...
BUT... maybe you can do it with runtime code generation...
You can start by writing a stub function in assembly (assuming you want x86)
push dword 0 ; SEL will go herepush dword 0 ; Object will go herepush dword 0 ; IMP will go herepop eax ; eax = impcall eax ; call impadd esp, 8 ; cleanup stackret ; return
This assembles to:
0068 0000 6800 0000 0000 0068 0000 5800 d0ff c481 0004 0000 00c3
Note the instruction push dword 0
is the bytes 68 00 00 00 00
. We will fill in the zeros to a pointer at runtime.
So, we can copy that into a malloc()
'd buffer, patch it up, and call mprotect()
to make it executable.
Below code is for illustrative purposes and I have no idea if it works. :-)
/* x86 code... */char code[] = { 0x68, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x58, 0xff, 0xd0, 0x81, 0xc4, 0x04, 0x00, 0x00, 0x00, 0xc3 };char *buf = malloc(sizeof(code));SEL Selector = @selector(Method);IMP Imp = [object methodForSelector:Selector];/* Copy template */memcpy(buf, code, sizeof(code));/* Patch the "push dword 0" parts with your arguments * This assumes everything is 32-bit, including SEL, IMP, etc. */memcpy(buf + 1, &Selector, sizeof(Selector));memcpy(buf + 6, &object, sizeof(object));memcpy(buf + 11, &Imp, sizeof(Imp));/* Now here comes the sketchy part... * Make it executable and turn it into a function pointer. */mprotect(buf, sizeof(code), PROT_EXEC);void (*Function)() = (void(*)())buf;/* Now, crazy as it sounds, you should be able to do: */Function();
You might want to do an [object retain]
for as long as this function exists, and [object release]
when and if you should choose to free it. (Probably best to wrap this sketcyness inside an object anyway, then use normal objc refcounting to control the buffer and a reference to object
.) Maybe you'll also want to use mmap()
to allocate instead of malloc()
...
If that sounds needlessly complex that's because it is. :-)
You can create a C++ class as a wrapper. And call the objective C message from that class. Just remember to make your file yoursource.mm instead of yoursource.cpp.