I have a pure Swift package that I built with the Swift Package Manager. My Package.Swift
looks like this:
// 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"]),
]
)
This Swift code I'm building contains a public function that I want to call from my C++ code:
// File: SwiftPackage.swift
public func StartWatcher() {
// code ...
}
I created a header file SwiftPackage.hh
where I define the StartWatcher
function like so:
// File: SwiftPackage.hh
void (*StartWatcher)();
Now I have my main.cc
file where I include the SwiftPackage.hh
and call the StartWatcher
function:
// File: main.cc
#include <SwiftPackage.hh>
int main() {
StartWatcher();
return 0;
}
However, when I run the built executable I'm getting the following error
./swift_package' terminated by signal SIGSEGV (Address boundary error)
My build process is following:
swift build --package-path SwiftPackage
. This creates the libSwiftPackage.a
library.libSwiftPackage.a
library that was created in the previous step:g++ -std=c++11 -L./SwiftPackage/.build/debug/ main.cc -lSwiftPackage -o swift_package
What am I doing wrong? I suspect that the Swift library isn't properly linked.
Edit
Based on the @Acorn's answer I did two things:
StartWatcher
declarition in an extern "C"
block@_cdecl("StartWatcher")
to my StartWatcher
Swift function which should make sure that the name isn't mangled in the library.Now I get a different output which is a bunch of messages like this:
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)
It seems to me that the there is some kind of problem accessing other libraries that are used in the Swift package?
Summary: this can be made to work (probably), but if this is intended to be used in production code, then the only correct approach is to consult the Swift documentation and, if it is still the case there is no official support, ask the Swift team how to approach the problem.
You should follow whatever documentation Swift has for exporting functions in the C ABI convention. It is doubtful doing it blindly works, and if it does, it is probably by chance and may stop working at any point in the future.
Sadly, there does not seem to be official support for such thing, at least according to questions like:
To make any kind of FFI to work unofficially, there are several things to take into account:
Figure out which calling convention Swift follows, including if it uses hidden parameters or things like that. The best case scenario is that Swift uses the usual one in your system.
Check what are the names of the actual symbols exported by Swift. Perhaps they are mangled.
Research if there are any semantics to hold that the other language runtime does automatically. For instance, if some code needs to be called before/after, or perhaps something need to be initialized, etc.
On the C++ side, you should write your declaration in an extern "C"
block so that the symbol is not expected to be C++-mangled:
extern "C" {
void StartWatcher();
}
There should be no need to declare it as a function pointer either.