Подтвердить что ты не робот

Обратный вызов с С++ на объективный c

У меня есть ViewController в objective-c, и большая часть моего кода - С++ (.mm). Я хотел бы установить некоторые обратные вызовы для функций-членов из obj-c (в С++) и вызвать их из С++. Что-то вроде этого (это очень упрощено):

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

@implementation MyClass

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

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

и

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

Это, конечно, неправильно. Любой совет, как я могу это сделать, пожалуйста?

4b9b3361

Ответ 1

У вас есть несколько вариантов.

Использование блоков

Вы можете использовать блоки для передачи вашей работы обратного вызова. Это, пожалуй, самое простое решение, поскольку оно позволяет вам называть ваш код без необходимости передавать какой-либо параметр функции "обратный вызов". Блоки работают на C и все его надмножества с Clang, а Clang++ даже допускает неявные отбрасывания между блоками и lambdas.

#include <dispatch/dispatch.h>

void setup_callback(dispatch_block_t block)
{
    // required to copy the block to the heap, otherwise it 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];
    });
}

Это может потребовать некоторой переделки на конец С++ для принятия функторов (или просто блоков, если это проще) вместо указателей на функции.

Поскольку блоки создают замыкания, они очень удобны для такого рода работ.

Блоки - это расширение Apple для C, С++ и Objective-C. Подробнее о них здесь.

Используйте среду выполнения Objective-C, чтобы получить указатель на метод, который вы хотите вызвать

Используйте среду выполнения Objective-C для доступа к указателю функции вашего селектора. Это более утомительно и требует от вас отслеживать три переменные (объект для вызова метода, селектор для использования и реализация метода), но он действительно работает даже в том случае, если вы не можете использовать Objective-C синтаксис.

Objective-C реализация методов - это указатели на функции с этой сигнатурой:

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

Где self - это то, что вы ожидаете, _cmd - это селектор, вызвавший этот вызов метода (переменная _cmd действительно доступна во всех методах Objective-C, попробуйте), а остальная часть VARIADIC. Вы нуждаетесь, чтобы отличать переменные IMP в правильной сигнатуре функции, поскольку соглашение о вызове для вариативных функций C не всегда совпадает с соглашением о вызове для вызовов метода Objective-C (вызов метода Objective-C стандартное назначение вызова функции для вашего компилятора, возможно, либо cdecl, либо соглашение о вызове amd64, а соглашение о вариационном вызове не всегда одно и то же). A reinterpret_cast сможет это сделать.

Вот код, который я собрал для подобных целей. Он использует С++ 11 вариационных шаблонов, чтобы помочь получить правильную подпись функции.

#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));
}

Также, прежде чем использовать вызов функции, убедитесь, что ваш экземпляр не является nil, потому что проверка nil выполняется с помощью среды выполнения Objective-C. В этом случае мы обходим его.

Отслеживать объект и SEL

Используйте -[NSObject performSelector:] для выполнения обратного вызова. В основном более простая версия решения 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));
}

Обтекание вызова внутри функции С++

Я думаю, что этот пример действительно не нужен. Создайте функцию, которая примет ваш тип объекта в качестве первого параметра и вызовет метод, который вы хотите на нем. Аналогично решению SEL, вам необходимо отдельно отслеживать функцию для вызова и объект, на который ее можно вызвать.

Ответ 2

Поскольку С++ и Obj-C являются суперсетями C, вы можете иметь обратный вызов кода С++ для функции C. Чтобы передать обратный вызов обратно на объект Obj-C, просто убедитесь, что аргументы обратного вызова C содержат указатель на исходный объект Obj-C.

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

Ответ 3

Я не думаю, что это возможно: вызов обратного вызова Objective-C в коде С++. Возможно и наоборот.

Что я сделал, когда у меня возникла эта проблема, так это написать EVENT_LOOP навсегда в Objective-C и получить события из экземпляра С++ std::vector. Таким образом, на С++, если вы хотите "вызвать" обратный вызов в Objective-C, вы просто добавляете событие в соответствующий std::vector.

Ответ 4

Вы можете свободно смешивать Obj-C и С++ в ваших .mm файлах - он называется "Objective-C ++".

Чтобы сохранить изоляцию кода на С++ и кода Objective-C, вы можете создать легкий фасад между ними, который бы вызывал вызовы метода Objective-C (обычным способом) из С++ обратных вызовов.