Как сделать указатель на функцию обратного вызова из C ++ в Swift? - PullRequest
0 голосов
/ 07 декабря 2018

Есть много учебных пособий, чтобы сделать это в Интернете, но ни одно из них не является четким и объективным, потому что вот я предпочитаю показать мой случай.

Я разрабатываю приложение для iOS, которое использует C API для подключения квеб-служба и имеет функции обратного вызова, моя задача проста, я должен получать события, сгенерированные в коде C на код Swift.Для этого я попробовал несколько способов, текущий способ, которым я пробую, заключается в следующем.

Сценарий

У меня есть три файла: wrapper.cpp, Bridging-Header.h и ViewController.swift

Вкл. Briding-Header.h Я объявил указатель функции.

Bridging-Header.h

void callback_t(void(*f)(unsigned char*));

Вкл. wrapper.cpp Я написал свой код для подключения к сети и использования функций обратного вызова.Так что это метод обратного вызова для получения состояния соединения, когда он отключен.

wrapper.cpp

void callback_web_disconnected(unsigned char *c) {
    printf("Disconnected %s",c);
    // Here I want invoke a method to Swift code
    callback_t(r); // But this not works
}

Это не компилируется, сообщение об ошибке: Use of undeclared identifier 'callback_t'.

Если я пытаюсь написать: void callback_frame(unsigned char*);, linker command failed with exit code 1 возникает ошибка.

ViewController.swift

func callback_t(_ f: ((UnsafeMutablePointer<UInt8>?) -> Void)!) {
        print("ARRIVED ON VIEWCONTROLLER!")
        // Here I want get the error code (unsigned char*) passed as parameter
    }

Я не могу импортироватьBridging-Header.h файл в wrapper.cpp, потому что вызывает много конфликтов.

1 Ответ

0 голосов
/ 07 декабря 2018

Во-первых, callback_t не является идентификатором.Я нигде не вижу typedef. Во-вторых, вам нужен какой-то способ сказать С ++, что обратный вызов - это ваша быстрая функция.Для этого я передаю его в качестве параметра в функцию C ++, аналогично тому, как мы это делаем в Objective-C и Swift. В противном случае вам нужно где-то сохранить обратный вызов в глобальной переменной и получить к нему доступ C ++.

Используя первый метод передачи обратного вызова в качестве параметра:

Сначала в заголовке C ++ (Foo.h) я сделал (НЕ удаляйте материал ifdef .. компилятор использует связь C при импортев Swift, но при компиляции стороны C ++ он будет искажен, поэтому, чтобы заставить его использовать связь C, мы extern "C" код):

#ifndef Foo_hpp
#define Foo_hpp

#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

    typedef void(*callback_t)(const char *);
    void callback_web_disconnected(callback_t);

#ifdef __cplusplus
} // extern "C"
#endif

#endif /* Foo_hpp */

Затем в файле реализации (Foo.cpp) IСделал:

#include "Foo.h"
#include <thread>
#include <iostream>

#ifdef __cplusplus
extern "C" {
#endif

    void callback_web_disconnected(callback_t callback)
    {
        std::thread t = std::thread([callback] {
            std::this_thread::sleep_for(std::chrono::seconds(2));

            if (callback)
            {
                callback("Hello World");
            }
        });

        t.detach();
    }

#ifdef __cplusplus
} // extern "C"
#endif

Затем в ViewController.swift я сделал:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        callback_web_disconnected({
            if let ptr = $0 {
                let str = String(cString: ptr)
                print(str)
            }
        })
    }
}

Работает нормально.Код Swift вызывается из C ++ по прошествии 2 секунд.


Использование второго метода хранения обратного вызова в глобальной переменной (что я презираю, но не будем вдаваться в это) ..

В Foo.h Я сделал:

#ifndef Foo_hpp
#define Foo_hpp

#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

    typedef void(*callback_t)(const char *);
    callback_t globalCallback; //Declare a global variable..

    void callback_web_disconnected();

#ifdef __cplusplus
} // extern "C"
#endif

#endif /* Foo_hpp */

В Foo.cpp Я сделал:

#include "Foo.h"
#include <thread>
#include <iostream>

#ifdef __cplusplus
extern "C" {
#endif

    void callback_web_disconnected()
    {
        std::thread t = std::thread([] {
            std::this_thread::sleep_for(std::chrono::seconds(2));

            if (globalCallback)
            {
                globalCallback("Hello World");
            }
        });

        t.detach();
    }

#ifdef __cplusplus
} // extern "C"
#endif

В ViewController.swift, я сделал:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        //Set the globalCallback variable to some block or function we want to be called..

        globalCallback = {
            if let ptr = $0 {
                let str = String(cString: ptr)
                print(str)
            }
        }

        //Trigger our test.. I guess your C++ code will be doing this anyway and it'll call the globalCallback.. but for the sake of this example, I've done it here..
        callback_web_disconnected()
    }
}
...