Swift调用C++函数

Franky lol

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如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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如下

1
2
3
4
5
6
7
#include "CppSolver.hpp"
char *solveGame(char* gameString) {
/*
Do Something
*/
return "hahahah";
}

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

CppWrapper.h如下

1
2
3
4
5
6
7
8
9
#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如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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:

1
2
3
4
//
// 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函数

注意做好类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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里都有的类型

  • Post title:Swift调用C++函数
  • Post author:Franky
  • Create time:2023-03-09 21:29:06
  • Post link:https://franky.pro/2023/03/09/Swift调用C-函数/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.