Как использовать библиотеку Swift stati c (.a) в проекте C ++? - PullRequest
1 голос
/ 04 августа 2020

У меня есть чистый пакет Swift, который я создал с помощью Swift Package Manager. Мой 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, который я создаю, содержит функцию publi c, которую я хочу вызвать из моего кода 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)

Building

Мой процесс сборки следующий:

  • Сначала я собираю пакет Swift, запустив swift build --package-path SwiftPackage. Это создает библиотеку libSwiftPackage.a.
  • Во-вторых, я создаю проект C ++, в котором я связываю библиотеку libSwiftPackage.a, созданную на предыдущем шаге:
g++ -std=c++11 -L./SwiftPackage/.build/debug/ main.cc -lSwiftPackage -o swift_package

What я делаю не так? Я подозреваю, что библиотека Swift не связана должным образом.

Edit

На основе ответа @ Acorn я сделал две вещи:

  1. Добавлено мое StartWatcher объявление в блоке extern "C"
  2. Добавлен атрибут @_cdecl("StartWatcher") в мою StartWatcher Swift-функцию, которая должна гарантировать, что имя не искажено в библиотеке.

Теперь я получаю другой вывод, который представляет собой кучу таких сообщений:

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?

1 Ответ

1 голос
/ 04 августа 2020

Резюме: это можно заставить работать (возможно), но если это предназначено для использования в производственном коде, тогда единственный правильный подход - обратиться к документации Swift, и, если это все еще так, там нет официальной поддержки, спросите команду Swift, как решить проблему.

Вы должны следовать любой документации Swift для экспорта функций в соответствии с соглашением C ABI. Сомнительно, что слепая работа сработает, а если и работает, то, вероятно, случайно и может перестать работать в любой момент в будущем.

К сожалению, похоже, что нет официальной поддержки для такой вещи, на по крайней мере, согласно таким вопросам, как:

Чтобы любой вид FFI работал неофициально, необходимо принять во внимание несколько вещей:

  • Выясните, какое соглашение о вызовах Swift следует, в том числе, если он использует скрытые параметры или тому подобное. В лучшем случае Swift использует обычный в вашей системе.

  • Проверьте, как называются имена фактических символов, экспортируемых Swift. Возможно, они искажены.

  • Изучите, есть ли какая-либо семантика, которую среда выполнения на другом языке выполняет автоматически. Например, если какой-то код нужно вызвать до / после или, возможно, что-то нужно инициализировать, et c.

На стороне C ++ вы должны написать свое объявление в блок extern "C", чтобы не предполагалось, что символ будет C ++ - искаженным:

extern "C" {
    void StartWatcher();
}

Не должно быть необходимости объявлять его как указатель функции.

...