设置混合语言 Swift 和 C++ 项目
Swift 支持 与 C++ 的双向互操作性。本页介绍如何使用受支持的 IDE 或构建系统之一来设置混合语言 Swift 和 C++ 项目
它还介绍了如何通过描述直接调用 Swift 编译器时如何使用 C++ 互操作性,来使其他构建系统能够实现 C++ 互操作性。
使用 Swift 包管理器混合 Swift 和 C++
Swift 包管理器 允许 Swift 代码在 Swift 中使用 C++ API。
Swift 包管理器尚不支持在 C++ 中使用 Swift API。
在包目标中启用 C++ 互操作性
Swift 包中的特定目标必须启用 C++ 互操作性,才能在 Swift 中导入和使用 C++ API。interoperabilityMode
Swift 构建设置用于为目标启用 C++ 互操作性。例如,以下包清单展示了如何为库目标启用 C++ 互操作性
let package = Package(
name: "LibraryThatUsesCxx",
products: [
.library(
name: "libraryUsesCxx",
targets: ["libraryUsesCxx"])
],
targets: [
.target(
name: "libraryUsesCxx",
swiftSettings: [.interoperabilityMode(.Cxx)])
]
)
从 C++ 包目标导入头文件
Swift 使用 Clang 模块 导入 C++ 头文件。Swift 包管理器可以为一个包含总括头文件的 C++ 目标自动生成一个 模块映射文件。生成的模块映射文件允许依赖于此类 C++ 目标的 Swift 目标从此类目标导入 C++ 头文件。
总括头文件必须包含 #include
指令列表,其中包含目标的其它公共 C++ 头文件。一旦将由生成的模块映射表示的 Clang 模块导入到 Swift 中,就可以在 Swift 中使用总括头文件中列出的头文件中声明的受支持的 C++ 类型和函数。
C++ 目标使用的总括头文件必须
- 使用 C++ 目标的名称(带有附加扩展名)作为其文件名
- 位于目标的公共头文件目录中
include
子目录是目标的默认公共头文件目录。publicHeadersPath
属性可用于为目标的公共头文件指定备用路径。
例如,以下 Swift 包构建了一个使用 C++ 库的 Swift 命令行工具
let package = Package(
name: "CommandLineSwiftToolUsesCxx",
products: [
.library(
name: "cxxLibrary",
targets: ["cxxLibrary"]),
.executable(
name: "swiftCLITool",
targets: ["swiftCLITool"])
],
targets: [
.target(
name: "cxxLibrary"),
.executableTarget(
name: "swiftCLITool",
dependencies: ["cxxLibrary"],
swiftSettings: [.interoperabilityMode(.Cxx)])
]
)
Swift 包管理器将自动为此包中的 C++ 库生成模块映射,因为它可以在源文件中找到总括头文件
Sources
├── swiftCLITool
└── cxxLibrary
├── include
│ ├── cxxLibrary.h [This is the umbrella header]
│ └── classImpl.h
├── cxxLibrary.cpp
└── classImpl.cpp
总括头文件 cxxLibrary.h
包含一些声明,并且还包含 C++ 目标中的其他头文件
// Header file `cxxLibrary.h`
#pragma once
#include <classImpl.h>
swiftCLITool
中的 Swift 代码可以直接导入 cxxLibrary
import cxxLibrary
然后,classImpl.h
头文件中声明的所有受支持的 C++ API 都将在 Swift 中可用。
销售启用 C++ 互操作性的包
为一个 Swift 包管理器目标启用 C++ 互操作性将需要依赖于该目标的其他目标也启用 C++ 互操作性。
启用 C++ 互操作性对于现有包来说是一项破坏性更改,因此必须仅在新的主要 semver 版本中完成。当您启用 C++ 互操作性时,请务必提高包的主要版本!
如果您想销售一个包含启用 C++ 互操作性的目标的包,我们建议您
- 明确告知客户端,当他们依赖于来自此类包的目标时,他们必须启用 C++ 互操作性。
- 明确告知客户端,您的包依赖于仍在开发中的 Swift 的未发布版本。
使用 Xcode 混合 Swift 和 C++
Xcode 15 支持混合语言 Swift 和 C++ 项目。本节介绍如何在 Xcode 中从 Swift 使用 C++ API 以及从 C++ 使用 Swift API。
请查看 “混合使用 Swift 和 C++” WWDC 会议,以获取有关如何在 Xcode 中使用 C++ 互操作性的更多详细信息。以下两个示例 Xcode 项目也可供下载
在 Xcode 中启用 C++ 互操作性
“C++ 和 Objective-C 互操作性”Xcode 构建设置可以设置为“C++ / Objective-C++”,以为特定构建目标启用 C++ 互操作性。启用 C++ 互操作性允许您
- 在同一目标中混合 Swift 和 C++。
- 从导入的框架目标中使用 C++ 和 Objective-C++ API。
- 从另一个目标中的 C++ 或 Objective-C++ 代码中使用框架的公共 Swift API。
在同一 Xcode 目标中混合 Swift 和 C++
Xcode 允许您在同一框架或 App 目标中混合 Swift 和 C++ 或 Objective-C++。
框架目标的 public 头文件中声明的受支持 C++ 类型和函数可以从同一目标中的 Swift 代码中使用。App 目标必须使用桥接头文件,以使目标的 C++ 类型和函数可用于同一 App 目标中的 Swift 代码。一旦将包含声明这些 API 的 App 目标的 C++ 头文件的 #include
指令添加到桥接头文件中,App 目标中的 Swift 代码就可以使用此类 C++ API。
框架或 App 目标的已公开 public Swift API 也可以从同一目标中的 C++ 或 Objective-C++ 实现文件中使用,方法是包含 Xcode 自动生成的生成头文件。例如,C++ 源文件可以通过包含以下生成的头文件来访问来自名为 Fibonacci 的同一框架或 App 目标的 Swift API
// FibonacciCxx.cpp [Fibonacci target]
#include <Fibonacci/Fibonacci-Swift.h>
// You can now use the exposed Swift APIs from Fibonacci.
使用导入的框架目标的 C++ API
Xcode 框架目标的 public C++ 头文件中声明的受支持 C++ 函数和类型可以从其他目标中的 Swift 代码中使用。想要从另一个框架目标使用 C++ API 的目标必须将该框架添加到其依赖项列表中。然后,此类目标中的 Swift 源文件可以导入该框架并在 Swift 中使用 C++ API。
例如,ForestLib 框架的公共 C++ API 可以从 ForestViewer App 目标中的 Swift 源文件使用,一旦 Swift 文件导入 ForestLib 框架
// TreeView.swift [ForestViewer App target]
import ForestLib
// You can now use the supported C++ APIs from ForestLib.
使用导入的框架目标的 Swift API
Xcode 框架目标的受支持和公开的 public Swift API 可以从其他目标中的 C++ 和 Objective-C++ 代码中使用。想要从另一个框架目标使用 Swift API 的目标必须将该框架添加到其依赖项列表中。然后,此类目标中的 C++ 或 Objective-C++ 源文件可以包含该框架的生成头文件,并在 C++ 或 Objective-C++ 中使用 Swift API。
例如,StorageProvider 框架的公共公开 Swift API 可以从 SafeStorage App 目标中的 Objective-C++ 源文件使用,一旦 Objective-C++ 源文件包含以下生成的头文件
// StorageAccess.mm [SafeStorage App target]
#include <StorageProvider/StorageProvider-Swift.h>
// You can now use the exposed Swift APIs from StorageProvider.
使用其他构建系统混合 Swift 和 C++
本节介绍如何在直接调用 Swift 编译器时启用和使用 C++ 互操作性。这允许其他构建系统配置混合语言 Swift 和 C++ 项目。
在 Swift 编译器中启用 C++ 互操作性
-cxx-interoperability-mode=
构建标志用于在 Swift 编译器中启用 C++ 互操作性。它接收互操作性兼容性版本作为其值。目前唯一支持的值是 default
。default
值表示 Swift 使用的互操作性兼容性版本与 Swift 语言版本匹配。
在直接调用编译器时导入 C++ Clang 模块
以下构建标志允许 Swift 查找 C++ 头文件
-I <path>
:此标志告诉 Swift,它应该在给定路径指定的目录中查找导入。当您想要将 C++ Clang 模块导入 Swift 时,此路径应包含一个module.modulemap
文件。
-Xcc
标志用于将额外的 C++ 构建设置传递给 Swift 编译器中嵌入的 C++ Clang 编译器。例如,您可以使用 Clang 的 -std=
标志将需要 C++20 的 C++ 头文件导入 Swift
swiftc ... -Xcc -std=c++20 ...
综上所述,以下 Swift 编译器调用允许您编译一个 Swift 文件,该文件导入一个 Clang 模块,该模块的模块映射文件位于 include
目录中
swiftc main.swift -cxx-interoperability-mode=default -I include -o main
生成带有暴露的 Swift API 的 C++ 头文件
-emit-clang-header-path
Swift 前端标志可用于在构建系统中构建 Swift 代码时,当向 C++ 公开 Swift API 时,发出生成的头文件,该构建系统不提供对生成带有暴露的 API 的头文件的自动支持。
以下 Swift 编译器调用为由两个源文件 a.swift
和 b.swift
组成的 SwiftModule
模块发出一个生成的头文件。
swiftc -frontend -typecheck \
/sources/a.swift /sources/b.swift -module-name SwiftModule \
-cxx-interoperability-mode=default \
-emit-clang-header-path SwiftModule-Swift.h