如何使从C ++到Swift的回调函数指针?

时间:2018-12-06 22:18:48

标签: c++ ios swift objective-c-swift-bridge

网上有很多教程可以使之实现,但是没有一个清晰而客观的教程,因为那是我更愿意展示的案例。

我正在开发一个使用C API连接到服务网站并且具有回调功能的iOS应用,我的任务很简单,我必须在Swift代码上获取C代码中生成的事件。为此,我尝试了一些方法,目前正在尝试的方法如下。

场景

我有三个文件:wrapper.cppBridging-Header.hViewController.swift

Briding-Header.h上,我声明了一个函数的指针。

Bridging-Header.h

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

wrapper.cpp上,我编写了代码以连接到Web并使用回调函数。因此,这是在断开连接时获得状态连接的回调方法。

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 :(得分:1)

首先,callback_t不是标识符。我typedef在任何地方都看不到。 其次,您需要某种方式告诉C ++回调是您的快速函数。为此,我将其作为参数传递给C ++函数,这与在Objective-C和Swift中的用法类似。否则,您需要将回调存储在全局变量中的某个位置,并让C ++访问它。

使用第一种将回调传递为参数的方法:

首先,我做了C ++头文件(Foo.h。(不要删除ifdef东西。.编译器在导入到Swift时使用C链接,但是在编译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)中,我做到了:

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

工作正常。经过2秒钟后,从C ++调用了Swift代码。


使用将回调函数存储在全局变量中的第二种方法(我不屑一顾,但我们不要赘述)。

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()
    }
}