Swift调用C++函数

背景(可略过)

字节跳动青训营要求写一个华容道游戏,实现 一键求解 的功能。刚开始用Swift写了个暴力的BFS,但是实在是太慢了,我觉得可能是Swift效率太低造成的,所以就想用C++写这个BFS。就这样,我的折磨旅途开始了。

概述

总体来说,Swift调用C++函数的步骤如下:

  • 写好C++函数
  • 使用Objective-C把C++函数封装一层
  • 将Objective-C封装好的函数桥接到Swift
  • 在Swift中调用前,做好类型转化

具体操作

这里就以我的华容道为例子了。

项目结构如下

其中主要关注**CppSolver.hpp CppSolver.cpp CppWrapper.h CppWrapper.mm Klotski-Bridging-Header.h **即可,这是在Swift调用C++函数中起到关键作用的文件。


1.写好C++函数

CppSolver.hpp如下

#ifndef CppSolver_hpp
#define CppSolver_hpp

#include <stdio.h>
#include <string.h>
#ifdef __cplusplus
extern "C" { 
#endif
    int getNum(int x);
    char *solveGame(char *gameString);
#ifdef __cplusplus
}
#endif

#endif /* CppSolver_hpp */

注意这里的extern "C"

之前没有加这个,一直报错,上网搜了之后,发现要加上这个。

CppSolver.cpp如下


#include "CppSolver.hpp"
char *solveGame(char* gameString) {
    /*
        Do Something 
    */
    return "hahahah";
}

2.使用Objective-C封装C++函数

CppWrapper.h如下

#ifndef CppWrapper_h
#define CppWrapper_h

#import <Foundation/Foundation.h>
struct CppStruct;
@interface CppWrapper: NSObject
- (char *) solveGame_Wrapped: (NSString *) gameString;
@end
#endif /* CppWrapper_h */

CppWrapper.mm如下

#import <Foundation/Foundation.h>
#import "CppWrapper.h"
#import "CppSolver.hpp"

@implementation CppWrapper

- (int) getNum_Wrapped: (int)x {
//    struct CppStruct *cs = CppStruct(1, 2);
    return getNum(x);
}

- (char *) solveGame_Wrapped: (NSString *) gameString {
    return solveGame([gameString UTF8String]);
}

@end

3.把Objective-C封装好的C++函数桥接到Swift

在创建Objective-C文件时,Xcode会提醒是否要建立桥接文件,这里我们要选择是,也就是创建了Klotski-Bridging-Header.h:

//
//  Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "CppWrapper.h"

4.在Swift中调用Objective-C函数

注意做好类型转换

func solveByPersonControllers(_ personControllers: [PersonController]) {
        let game = GameStatusForCpp(personControllers: personControllers)
        guard let gameData = try? JSONEncoder().encode(game) else {
            fatalError("unexpected game status \(game)")
        }
        
        guard let gameString = String(data: gameData, encoding: .utf8) else {
            fatalError("unexpected gameData \(gameData)")
        }
        let cppWrapper = CppWrapper()
        guard let solveChars = cppWrapper.solveGame_Wrapped(gameString) else { fatalError() }
        guard let solveStr = String(cString: solveChars, encoding: .utf8) else { fatalError() }
        print(solveStr)
    }

建议

建议需要用到复杂的数据类型(类、结构体)的时候,可以传递json字符串(我上面的代码就是这样做的),因为我暂时还没找到更好的方法可以让Swift和C++使用共同的结构。至于C++的json处理,有一个比较好用的库,是腾讯的rapidjson
C++的返回值类型应该需要是Objective-C和Swift里都有的类型

   swift
  最后修订:2022-8-19