如何在C++项目中使用Swift静态库(.a)?
How to use Swift static library (.a) in C++ project?
我有一个纯 Swift 包,是我用 Swift 包管理器构建的。我的 Package.Swift
看起来像这样:
// File: Package.swift
// swift-tools-version:5.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "SwiftPackage",
products: [
.library(
name: "SwiftPackage",
type: .static,
targets: ["SwiftPackage"]),
],
dependencies: [
],
targets: [
.target(
name: "SwiftPackage",
dependencies: []),
.testTarget(
name: "SwiftPackageTests",
dependencies: ["SwiftPackage"]),
]
)
我正在构建的这个 Swift 代码包含一个 public 函数,我想从我的 C++ 代码中调用它:
// File: SwiftPackage.swift
public func StartWatcher() {
// code ...
}
我创建了一个头文件 SwiftPackage.hh
,我在其中定义了 StartWatcher
函数,如下所示:
// File: SwiftPackage.hh
void (*StartWatcher)();
现在我有了 main.cc
文件,其中包含 SwiftPackage.hh
并调用 StartWatcher
函数:
// File: main.cc
#include <SwiftPackage.hh>
int main() {
StartWatcher();
return 0;
}
但是,当我 运行 构建的可执行文件时,出现以下错误
./swift_package' terminated by signal SIGSEGV (Address boundary error)
建筑
我的构建过程如下:
- 首先,我通过 运行ning
swift build --package-path SwiftPackage
构建了 Swift 包。这将创建 libSwiftPackage.a
库。
- 其次,我构建了 C++ 项目,其中 link 在上一步中创建的
libSwiftPackage.a
库:
g++ -std=c++11 -L./SwiftPackage/.build/debug/ main.cc -lSwiftPackage -o swift_package
我做错了什么?我怀疑 Swift 库没有正确 linked.
编辑
根据@Acorn 的回答我做了两件事:
- 在
extern "C"
块中添加了我的 StartWatcher
声明
- 向我的
StartWatcher
Swift 函数添加了一个属性 @_cdecl("StartWatcher")
,它应该确保库中的名称没有被破坏。
现在我得到了不同的输出,它是一堆这样的消息:
Undefined symbols for architecture x86_64:
"static Foundation.Notification._unconditionallyBridgeFromObjectiveC(__C.NSNotification?) -> Foundation.Notification", referenced from:
@objc SwiftPackage.AppDelegate.applicationDidFinishLaunching(Foundation.Notification) -> () in libSwiftPackage.a(AppDelegate.swift.o)
@objc SwiftPackage.AppDelegate.applicationWillTerminate(Foundation.Notification) -> () in libSwiftPackage.a(AppDelegate.swift.o)
在我看来,访问 Swift 包中使用的其他库时出现某种问题?
总结:这可以工作(可能),但如果打算在生产代码中使用,那么唯一正确的方法是查阅 Swift 文档和,如果仍然没有官方支持,请咨询Swift团队如何解决问题。
您应该遵循 Swift 在 C ABI 约定中导出函数的任何文档。盲目做是否有效是值得怀疑的,如果确实有效,那很可能是偶然的,将来可能会停止工作。
可悲的是,似乎没有官方支持这样的事情,至少根据这样的问题:
- What is the best way to call into Swift from C?
要使任何类型的 FFI 非正式运作,需要考虑以下几点:
弄清楚 Swift 遵循哪种调用约定,包括它是否使用隐藏参数或类似的东西。最好的情况是 Swift 使用您系统中常用的那个。
检查Swift导出的实际符号的名称是什么。也许他们被破坏了。
研究是否有任何语义支持其他语言运行时自动执行。例如,如果需要调用某些代码 before/after,或者可能需要初始化某些代码等
在 C++ 方面,您应该在 extern "C"
块中编写声明,这样符号就不会被 C++ 损坏:
extern "C" {
void StartWatcher();
}
应该也不需要将其声明为函数指针。
我有一个纯 Swift 包,是我用 Swift 包管理器构建的。我的 Package.Swift
看起来像这样:
// File: Package.swift
// swift-tools-version:5.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "SwiftPackage",
products: [
.library(
name: "SwiftPackage",
type: .static,
targets: ["SwiftPackage"]),
],
dependencies: [
],
targets: [
.target(
name: "SwiftPackage",
dependencies: []),
.testTarget(
name: "SwiftPackageTests",
dependencies: ["SwiftPackage"]),
]
)
我正在构建的这个 Swift 代码包含一个 public 函数,我想从我的 C++ 代码中调用它:
// File: SwiftPackage.swift
public func StartWatcher() {
// code ...
}
我创建了一个头文件 SwiftPackage.hh
,我在其中定义了 StartWatcher
函数,如下所示:
// File: SwiftPackage.hh
void (*StartWatcher)();
现在我有了 main.cc
文件,其中包含 SwiftPackage.hh
并调用 StartWatcher
函数:
// File: main.cc
#include <SwiftPackage.hh>
int main() {
StartWatcher();
return 0;
}
但是,当我 运行 构建的可执行文件时,出现以下错误
./swift_package' terminated by signal SIGSEGV (Address boundary error)
建筑
我的构建过程如下:
- 首先,我通过 运行ning
swift build --package-path SwiftPackage
构建了 Swift 包。这将创建libSwiftPackage.a
库。 - 其次,我构建了 C++ 项目,其中 link 在上一步中创建的
libSwiftPackage.a
库:
g++ -std=c++11 -L./SwiftPackage/.build/debug/ main.cc -lSwiftPackage -o swift_package
我做错了什么?我怀疑 Swift 库没有正确 linked.
编辑
根据@Acorn 的回答我做了两件事:
- 在
extern "C"
块中添加了我的StartWatcher
声明 - 向我的
StartWatcher
Swift 函数添加了一个属性@_cdecl("StartWatcher")
,它应该确保库中的名称没有被破坏。
现在我得到了不同的输出,它是一堆这样的消息:
Undefined symbols for architecture x86_64:
"static Foundation.Notification._unconditionallyBridgeFromObjectiveC(__C.NSNotification?) -> Foundation.Notification", referenced from:
@objc SwiftPackage.AppDelegate.applicationDidFinishLaunching(Foundation.Notification) -> () in libSwiftPackage.a(AppDelegate.swift.o)
@objc SwiftPackage.AppDelegate.applicationWillTerminate(Foundation.Notification) -> () in libSwiftPackage.a(AppDelegate.swift.o)
在我看来,访问 Swift 包中使用的其他库时出现某种问题?
总结:这可以工作(可能),但如果打算在生产代码中使用,那么唯一正确的方法是查阅 Swift 文档和,如果仍然没有官方支持,请咨询Swift团队如何解决问题。
您应该遵循 Swift 在 C ABI 约定中导出函数的任何文档。盲目做是否有效是值得怀疑的,如果确实有效,那很可能是偶然的,将来可能会停止工作。
可悲的是,似乎没有官方支持这样的事情,至少根据这样的问题:
- What is the best way to call into Swift from C?
要使任何类型的 FFI 非正式运作,需要考虑以下几点:
弄清楚 Swift 遵循哪种调用约定,包括它是否使用隐藏参数或类似的东西。最好的情况是 Swift 使用您系统中常用的那个。
检查Swift导出的实际符号的名称是什么。也许他们被破坏了。
研究是否有任何语义支持其他语言运行时自动执行。例如,如果需要调用某些代码 before/after,或者可能需要初始化某些代码等
在 C++ 方面,您应该在 extern "C"
块中编写声明,这样符号就不会被 C++ 损坏:
extern "C" {
void StartWatcher();
}
应该也不需要将其声明为函数指针。