views:

1492

answers:

4

Hey Everyone! Quick question. Does anyone know how to get the function pointer of an objective c method? I can declare a C++ method as a function pointer, but this is a callback method so that C++ method would need to be part of the class SO THAT IT CAN ACCESS THE INSTANCE FIELDS. I don't know how to make a C++ method part of an objective c class. Any suggestions?

+1  A: 

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.

Pablo Santa Cruz
+6  A: 

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 here
push dword 0 ; Object will go here
push dword 0 ; IMP will go here
pop eax      ; eax = imp
call eax     ; call imp
add esp, 8   ; cleanup stack
ret          ; 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. :-)

asveikau
The second code snippet does provide a C-style function pointer, as requested. The details can be a little tricky, though.
Mark Bessey
Its a nice option, but I want to just pass a pointer to the function. NOT a pointer to the object as well. otherwise, I would have just used selectors.
Sam
Typically, neither the IMP nor the Selector is going to help you much as you also need a reference to the instance you need to call back into....
bbum
Or you could use libffi to create a closure in a portable fashion.... but you'll run into some nasty problems related to security (the code has to be on a page marked executable, which not all OSes support).
bbum
@bbum Sounds like the mprotect() call in my example would help with what you mention.. Also on Windows I think VirtualAlloc() lets you specify executable. So between those two calls I think you're pretty well covered for platforms commonly in use today.
asveikau
Not quite; on the iPhone, you can't mess with memory permissions. Another issue is that once you mark a page as readonly/executable, you can't safely ever mark it as readwrite/nonexecutable to add additional closures. You can quickly find yourself eating memory!
bbum
A very kind attempt. I really appreciate it. However, I don't think I need something so complex. I'm really not working that close to the metal.
Sam
Also, for everyone's benefit, this question was mainly an issue of my own ignorance. I had some idea that I could just make a function pointer to an Object's method in Obj-C. Well, of course that's wrong. There isn't an absolute address for Obj-C methods. Obj-c method addresses are determined at runtime.
Sam
A: 

You can do this with instanceMethodForSelector: See the Apple NSObject documentation for more info.

Mark Bessey
+4  A: 

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.

bbum
Hmmm...good tip. I guess this is sorta the same thing as using target/action. Basically, you have to tell that silly call back function just who to talk to. If I was working with an API for which I didn't have the source it would be useful. Actually, I'm just implementing the whole setup with my own code. Still, thanks a ton.
Sam
It is akin to target/action, but not quite the same. If writing pure C, the context would be the malloc'd chunk of memory containing whatever state you needed in the callback. This is almost exactly what is happening here, but you are just forwarding the callback into Objective-C...
bbum
When developing with Cocoa, NSInvocation can be the glue class: http://developer.apple.com/mac/library/documentation/cocoa/reference/foundation/Classes/NSInvocation_Class/Reference/Reference.html
outis
You could use NSInvocation, but it can quickly get messy and almost always defeats most of the compiler's ability to do type checking. A glue class is trivial to write, can be easily expanded upon later, and maximizes the compilers ability to double-check your code.
bbum