When building a dynamic library intended to be used with a host application written in C/C++, is there any way to generate a C/C++ header file?
A public function can be declared with the @_cdecl("functionName") attribute and have a C calling convention.
The swiftc compiler can be invoked with the -emit-objc-header command line argument, and in this case a header file is created, but it's an Objective-C header file, and I believe it's not compatible with C/C++.
Now that C/C++ interoperability is actively developing, is there a way to emit a C/C++ header file?
I've been wondering this too, a bit. It'd be nice to be able (eventually) to produce good-quality C++ APIs atop Swift code. I like to think it opens some doors for using Swift for core implementations while supporting C++ users, rather than the other way around.
The -emit-clang-header-path Swift frontend flag can be used to emit a generated header when exposing Swift APIs to C++ when building Swift code in a build system that doesn’t provide automatic support for generating a header file with exposed APIs.
The following Swift compiler invocation emits a generated header file for the SwiftModule module that consists of two source files, a.swift and b.swift:
It works. The -emit-clang-header-path does the trick.
But there are concerns, at least for me:
This command line parameter seems to be undocumented. The swiftc --help command does not mention it.
The -cxx-interoperability-mode=default command line parameter is required to emit Objective C/C/C++ compatible header file. Without it only Objective C header is emitted.
Why I'm uncomfortable with the C++ interoperability flag is because modules built with this flag used to be binary incompatible with modules built without it (they seem to be compatible as of Swift 5.10, which is great). And I tend to import precompiled modules (as dynamic libraries) rather than recompile all dependencies every time. Thanks a lot anyway @ole!
If anyone needs it I have a minimal CMake version of this working in a tiny mixed C and Swift example, thanks to this post and an article that referenced it
One warning, you currently have to build it twice for the emitted header to be there for the main build, but I'm working on figuring out artifact dependencies. (Apparently it might be a Ninja thing)
The C++ header is generated, but nowadays it has become so huge that I have doubts that is will compile without problems on C++ side.
Let me explain what I mean:
If my Swift-written dynamic library exports just one function (has just one public func) and does not use Swift specific types as its parameters or a return value (e.g. uses just Int), the emitted header nevertheless contains thousands lines of code related to some esoteric C++ interop and not related to my function at all. Earlier versions of Swift did not do that.
Example:
My library contains 4 lines of code:
@_cdecl("meaningOfLife")
public func meaningOfLife() -> Int {
return 42
}
The emitted header for my library contains 3975 lines of code (Swift 6.1.2, Windows platform). I could not even paste it here, as Discourse has a limit on number of lines in one post.