2012-08-30 13 views
12

Tengo ViewController en objetivo-c y la mayor parte de mi código es C++ (.mm). Me gustaría configurar algunas devoluciones de llamadas a funciones miembro desde obj-c (en C++) y llamarlas desde C++. Algo como esto (es muy simplifyed):devolución de llamada desde C++ al objetivo c

@interface MyClass 
{ } 
-(void)my_callback; 
@end 

@implementation MyClass 

-(void)my_callback 
{ 
    printf("called!\n"); 
} 

-(void)viewDidLoad 
{ 
    // setup_callback("to my_callback ?"); 
} 
@end 

y:

void setup_callback(void(*func)()) { func(); } 

esto no es correcto, por supuesto. Algún consejo, ¿cómo puedo hacerlo, por favor?

Respuesta

15

Tiene algunas opciones.

utilizando bloques

Es posible utilizar bloques para transmitir su trabajo de devolución de llamada. Esta es probablemente la solución más simple, ya que le permite llamar a su código sin tener que pasar ningún parámetro a la "función" de devolución de llamada. Los bloques funcionan en C y todos sus superconjuntos con Clang, y Clang ++ incluso permite conversiones implícitas entre bloques y lambdas.

#include <dispatch/dispatch.h> 

void setup_callback(dispatch_block_t block) 
{ 
    // required to copy the block to the heap, otherwise it's on the stack 
    dispatch_block_t copy = [block copy]; 

    // setup stuff here 
    // when you want to call the callback, do as if it was a function pointer: 
    // block(); 
} 

int main() 
{ 
    MyClass* instance = [[MyClass alloc] init]; 

    setup_callback(^{ 
     [instance callback_method]; 
    }); 
} 

que podrían requerir alguna reelaboración en el extremo C++ para aceptar funtores (o sólo bloques si es más sencillo) en lugar de punteros de función.

Dado que los bloques crean cierres, son muy convenientes para ese tipo de trabajos.

Los bloques son una extensión de Apple para C, C++ y Objective-C. Ver más sobre ellos here.

Utilice el tiempo de ejecución de Objective-C para adquirir el puntero de función para el método al que desea llamar

Usar el tiempo de ejecución de Objective-C para acceder a la función de puntero de su selector. Esto es más tedioso y requiere que realice un seguimiento de tres variables (el objeto para invocar el método, el selector a usar y la implementación del método), pero en realidad funciona incluso en el caso de que no pueda usar el Objective-C sintaxis.

implementaciones de métodos de Objective-C son los punteros de función con esta firma:

typedef void (*IMP)(id self, SEL _cmd, ...); 

Dónde self es lo que se espera, _cmd es el selector que causó esta llamada al método (la variable _cmd está realmente disponible en todo Métodos Objective-C, pruébelo), y el resto se considera variadic. Usted necesita para convertir las variables IMP en la firma de función adecuada porque la convención de llamadas para funciones C variables no siempre coincide con la convención de llamadas de método Objective-C (la llamada de método Objective-C es la convención de llamada de función estándar para su compilador, probablemente cdecl o la convención de llamadas amd64, y la convención de llamadas variadas es no siempre lo mismo). Un reinterpret_cast podrá hacerlo.

Aquí hay un código que armé para propósitos similares. Utiliza plantillas variadas de C++ 11 para ayudar a obtener la firma de función adecuada.

#include <objc/runtime.h> 

template<typename TReturnType, typename... TArguments> 
auto GetInstanceMethodPointer(Class class, SEL selector) -> TReturnType (*)(id, SEL, TArguments...) 
{ 
    Method m = class_getInstanceMethod(class, selector); 
    IMP imp = method_getImplementation(m); 
    return reinterpret_cast<TReturnType (*)(id, SEL, TArguments...)>(imp); 
} 

int main() 
{ 
    MyClass* instance = [[MyClass alloc] init]; 
    auto foo = GetInstanceMethodPointer<void>(
     [MyClass class], 
     @selector(my_callback)); 
    // foo is a void (*)(id, SEL) function pointer 
    foo(instance, @selector(my_callback)); 
} 

también se ocupan de que su caso no es nil antes de utilizar la llamada a la función, porque nil comprobación es manejado por el tiempo de ejecución de Objective-C. En este caso, lo estamos eludiendo.

Realizar un seguimiento de un objeto y un SEL

Uso -[NSObject performSelector:] para realizar su devolución de llamada. Básicamente es una versión más simple de la solución de tiempo de ejecución Objective-C.

void setup_callback(id object, SEL selector) 
{ 
    // do stuff 
    // to execute the callback: 
    // [object performSelector:selector]; 
} 

int main() 
{ 
    MyClass* instance = [[MyClass alloc] init]; 

    setup_callback(instance, @selector(my_callback)); 
} 

Envolviendo su llamada dentro de una función de C++

Creo que éste en realidad no necesita ningún ejemplo. Cree una función que acepte su tipo de objeto como primer parámetro y llame al método que desee. De forma similar a la solución SEL, debe realizar un seguimiento por separado de la función a llamar y el objeto al que se debe llamar.

+2

ni siquiera necesita la implementación. Siempre puede usar 'objc_msgSend' en lugar de la implementación – user102008

+0

True. En ese caso, llevaría el selector en lugar del puntero del método. – zneak

+0

No. Con su IMP, tendría que llevar el selector de todos modos. Entonces es lo mismo, excepto que ahora no necesita obtener un IMP. – user102008

1

No creo que sea posible: llamar a una devolución de llamada de Objective-C dentro del código de C++. Al revés, es posible.

lo que hice cuando tuve este problema es escribir un EVENT_LOOP corriendo siempre en Objective-C, y obtener los eventos desde una instancia de C++ std::vector. De esta forma, en C++, si desea "llamar" una devolución de llamada en Objective-C, simplemente agregue el evento al correspondiente std::vector.

1

Puede mezclar libremente Obj-C y C++ en sus archivos .mm; se lo conoce como "Objective-C++".

Para mantener el aislamiento de su código C++ y código Objective-C, podría crear una fachada ligera entre ellos que haría llamadas al método Objective-C (de la manera habitual) desde callbacks C++.

2

Dado que C++ y Obj-C son superconjuntos de C, puede hacer que su código C se devuelva a una función C. Para comunicar la devolución de llamada a su objeto Obj-C, simplemente asegúrese de que los argumentos de devolución de llamada C incluyan un puntero al objeto Obj-C original.

// C++ calls this C function 
void exampleCallback(void* classInstance) 
{ 
    // callback arguments include a pointer to the Obj-C object 
    [((MYObjCClassType*)classInstance) exampleCallback]; 
} 
+0

Para hacer ese reparto que muestra, debe hacerlo con un puente gratuito: '[((__bridge MYObjCClassType *) classInstance) exampleCallback];' – dadude999

Cuestiones relacionadas