2010-04-04 9 views
18

Quiero agregar soporte de scripting para un proyecto de Objective-C utilizando el tiempo de ejecución objc. Ahora enfrento el problema, que no tengo ni idea, cómo debería llamar un método Objective-C que toma varios argumentos nombrados.usando objc_msgEnviar para llamar a una función de Objective C con argumentos con nombre

Así, por ejemplo, la siguiente llamada Objective-C

[object foo:bar]; 

podría ser llamado de C con:

objc_msgSend(object, sel_getUid("foo:"), bar); 

Pero ¿cómo iba a hacer algo similar para la llamada al método:

[object foo:var bar:var2 err:errVar]; 

??

Mejor Markus

+3

Objecrtive-C no tiene argumentos con nombre. Tiene argumentos intercalados con el nombre del método. (Los argumentos nombrados implican que soltar los nombres es una opción, que no lo es). – bbum

+1

Dado que markus solo quería crear un puente general para el acceso de scripting y no un hipervínculo de nivel c, no podría simplemente usar un uso de NSInvocation como aquí: http://www.cocoawithlove.com/2008/03/construct -nsinvocation-for-any-message.html – trcarden

Respuesta

19
objc_msgSend(object, sel_getUid("foo:bar:err:"), var, var2, errVar); 

Si una de las variables es una float, es necesario utilizar @Ken 's método o engañar por un reinterpretar-reparto:

objc_msgSend(..., *(int*)&var, ...) 

Además, si el selector devuelve un flotador, usted puede necesita usar objc_msgSend_fpret, y si devuelve una estructura debe usar objc_msgSend_stret. Si es una llamada a la superclase, debe usar objc_msgSendSuper2.

+3

Esto está cerca, pero no funcionará como se espera con métodos que toman ciertos tipos. Por ejemplo, supongamos que el método se declara para tomar un flotante para uno de los parámetros. objc_msgSend se declara como una función varargs, y cualquier float que se transfiera se promocionará a double. El impl de objc_msgSend saltará al método que está esperando un flotador y no funcionará. Ver mi respuesta para más detalles. – Ken

+1

Ken es exactamente correcto; El vararg que maneja ABI y ABI de flotación/doble manipulación puede ser extremadamente perverso, dependiendo de la CPU. – bbum

+2

En cuanto a la edición: no todos los retornos de estructura en todas las arquitecturas usan objc_msgSend_stret. Varía según la arquitectura y lo que hay en la estructura. – Ken

3

objc_msgSend (obj, @selector (foo: bar: err :), var, var2, & errVar);

+0

No funcionará por las razones descritas por Ken ... debe crear un puntero de función específicamente tipado. – bbum

38

La respuesta aceptada está cerca, pero no funcionará correctamente para ciertos tipos. Por ejemplo, si se declara que el método toma un flotante como segundo argumento, esto no funcionará.

Para utilizar correctamente objc_msgSend, debe convertirlo al tipo apropiado. Por ejemplo, si el método se declara como

- (void)foo:(id)foo bar:(float)bar err:(NSError **)err 

entonces usted tendría que hacer algo como esto:

void (*objc_msgSendTyped)(id self, SEL _cmd, id foo, float bar, NSError**error) = (void*)objc_msgSend; 
objc_msgSendTyped(self, @selector(foo:bar:err:), foo, bar, error); 

juzgar el caso anterior con sólo objc_msgSend y el registro de los argumentos recibidos. No verá los valores correctos en la función llamada. Esta situación de conversión inusual se debe a que objc_msgSend no debe llamarse como una función C normal. Se implementa (y se debe implementar) en el ensamblaje, y simplemente salta a una función C objetivo después de manipular algunos registros. En particular, no hay forma consistente de referirse a cualquier argumento más allá de los dos primeros dentro de objc_msgSend.

Otro caso en el que llamar a objc_msgSend straight no funcionaría es un método que devuelve un NSRect, por ejemplo, porque objc_msgSend no se usa en ese caso, objc_msgSend_stret es. En la función C subyacente para un método que devuelve un NSRect, el primer argumento es en realidad un puntero a un valor de salida NSRect, y la función en sí devuelve realmente vacía. Usted debe coincidir con esta convención cuando llame porque es lo que asumirá el método llamado. Además, las circunstancias en las que se usa objc_msgSend_stret difieren entre las arquitecturas. También hay un objc_msgSend_fpret, que se debe usar para los métodos que devuelven ciertos tipos de punto flotante en ciertas arquitecturas.

Ahora, como está intentando hacer un puente de scripting, probablemente no pueda mostrar explícitamente cada caso que encuentre, quiere una solución general. En general, esto no es completamente trivial, y desafortunadamente su código tiene que estar especializado para cada arquitectura que desea enfocar (por ejemplo, i386, x86_64, ppc). Su mejor apuesta es probablemente ver cómo PyObjC lo hace. También querrá echar un vistazo al libffi. Probablemente sea una buena idea entender un poco más acerca de cómo se pasan los parámetros en C, que puede leer en el Mac OS X ABI Guide. Por último, Greg Parker, que trabaja en el tiempo de ejecución objc, ha escrito a bunch of very nice posts en objc internals.

+3

fundición entre tipos de punteros de función y 'void *' no está permitido en el estándar C – user102008

+0

Excelente respuesta. –

Cuestiones relacionadas