如何在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)

建筑

我的构建过程如下:

g++ -std=c++11 -L./SwiftPackage/.build/debug/ main.cc -lSwiftPackage -o swift_package

我做错了什么?我怀疑 Swift 库没有正确 linked.

编辑

根据@Acorn 的回答我做了两件事:

  1. extern "C" 块中添加了我的 StartWatcher 声明
  2. 向我的 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();
}

应该也不需要将其声明为函数指针。