Skip to content

Latest commit

 

History

History
179 lines (125 loc) · 4.46 KB

README.zh_CN.md

File metadata and controls

179 lines (125 loc) · 4.46 KB

Swift 调用 C 语言自建函数库 及 Swift 脚本和动态链接库的使用方法

Rockford Wei,2017-01-17

最近更新:2018-01-23

本程序示范了如何用Swift调用自定义C语言模块的方法。您可以直接下载本程序,或者按照以下教程逐步完成。 此外,示范程序还展示了如何使用 Swift 作为脚本运行,并且在脚本中调用C语言的动态链接库,使得服务器端Swift 开发实现热补丁。

简介

示范程序中有一个C语言的源程序CSwift.c和一个头文件CSwift.h,我们的目标是构造一个CSwift的函数库,能够让swift源程序执行CSwift程序中的函数。

快速上手

本程序需要Swift 4.0.3 以上版本。

下载、编译和测试

$ git clone https://github.com/RockfordWei/CSwift.git
$ cd CSwift
$ swift build
$ swift test

源程序采用C语言写成,测试程序则是Swift语言编写。因此如果通过测试,则恭喜您,已经成功实现了Swift语言调用C语言的整个过程。

详细步骤

您可以完全不依赖所有上述内容,而一步一步从零开始制作C函数库和调用C库的Swift代码:

构造空白的函数库

仍然假定函数库名称为CSwift。首先找一个空白目录,然后执行:

mkdir CSwift && cd CSwift && swift package init
mkdir Sources/CSwift/include && rm Sources/CSwift/CSwift.swift

上述命令能够新建一个空白的函数库模板

C 函数头文件

现在编辑头文件 Sources/CSwift/include/Swift.h:

extern int c_add(int, int);
#define C_TEN 10

C 函数体文件

然后通过修改文件 Sources/CSwift/CSwift.c完成函数实现:

#include "include/CSwift.h"
int c_add(int a, int b) { return a + b ; }

模块映射

下一步为Swift 语言构造一个映射模块文件: Sources/CSwift/include/module.modulemap

module CSwift [system] {
  header "CSwift.h"
  export *
}

Swift 程序调用

请修改Tests/CSwiftTests/CSwiftTests.swift文件,内容如下:

import XCTest
@testable import CSwift

class CSwiftTests: XCTestCase {
  func testExample() {
    // 测试调用 C 函数
    let three = c_add(1, 2)
    XCTAssertEqual(three, 3)
    // 测试调用 C 语言的符号
    XCTAssertEqual(C_TEN, 10)
  }

  static var allTests : [(String, (CSwiftTests) -> () throws -> Void)] {
    return [ ("testExample", testExample)]
  }
}

测试

最后一步最简单,直接执行:

$ swift build
$ swift test

如果没有问题,那就一切OK了!

Swift 脚本解释器的使用以及动态链接库的调用

除了以上正文之外,Swift 还提供了直接以解释器执行 Swift脚本的方法,相当于在linux或mac命令行直接使用playground。 本文顺便利用这个脚本,同时说明一下动态链接库的使用方法。

动态链接库

默认的Swift 4为静态编译,因此如果希望使用动态链接,则需要做一个调整。 请打开Package.swift文件增加下一行:

.library(
    name: "CSwift",
    type: .`dynamic`,  // <------------ 在这里增加新的一行
    targets: ["CSwift"]),

本项目附带了一个特殊脚本dll.swift.script,内容为一个普通Swift 源程序:

// 首先dllpath 一定是编译完成的C语言动态链接库,在mac上是.dylib文件,在linux上是.so文件
// 请注意dllpath 一定是绝对路径
guard let lib = dlopen(dllpath,  RTLD_LAZY) else {
  exit(0)
}

// 然后声明需要引用的函数类型
typealias AddFunc = @convention(c) (CInt, CInt) -> CInt

// 用dlsym方法真正获取目标函数的地址
guard let c_add = dlsym(lib, "c_add") else {
  dlclose(lib)
  exit(0)
}

// 将动态函数库函数地址绑定到刚才声明的函数类型上
let add = unsafeBitCast(c_add, to: AddFunc.self)

// 调用动态链接库
let x = add(1, 2)
print(x)

// 释放资源
dlclose(lib)

用Swift脚本调用动态链接库

附件中还包括了另外一个bash脚本dll.sh,供运行上述脚本并调用动态链接库

# 首先编译一下C函数库
swift build

# 然后判断操作系统类型,如果是苹果则使用.dylib,如果是Linux就用.so
if swift --version|grep apple
then
  SUFFIX=dylib
else
  SUFFIX=so
fi

# 合成动态函数库名称
DLL=$PWD/.build/debug/libCSwift.$SUFFIX

# 用swift 解释器调用脚本,并执行动态链接库调用
swift dll.swift.script $DLL

其他

如果您在使用Xcode,则需要使用swift package generate-xcodeproj