Function Pointers in Objective C Function Pointers in Objective C objective-c objective-c

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.