This post presents the progress that the Swift and C++ interoperability workgroup has made during the first eight months of this year by highlighting the major documentation and compiler changes that landed in that time frame. The span of this time frame stretches from the initial workgroup announcement until the middle of September of this year when Swift-5.7 was released. Note that the majority of compiler changes in this post haven’t made it into the Swift-5.7 release, but they will make it into the next planned release of Swift.
Workgroup
The Swift and C++ interoperability workgroup now has a page on the Swift website that talks about what the workgroup does and describes how to join it.
Documentation
The drafts of the two vision documents providing the framing for future Swift evolution pitches of using C++ APIs from Swift and vice-versa have been posted on Github.
Preliminary user guide for how to use Swift APIs from C++ has been added to Swift’s Github repo.
Preliminary user manual that describes how C++ APIs get imported into Swift has been added to Swift’s Github repo.
Using C++ APIs From Swift
Designed and implemented a safe model for importing C++ types and their members that prevents accidental misuse of inner pointers and references in Swift. The vision document provides more details about this model.
Extended previously prototyped support for bridging C++ types with reference semantics to Swift class types by adding support for bridging custom C++ reference counting operations to Swift.
Added a C++ standard library overlay to allow easier conversion between Swift and C++ standard library types.
For example, you can now convert a C++ std::string to a Swift String using the String(cxxString:)initializer.
Designed and implemented a safe and ergonomic iterator bridging model from C++ to Swift.
This allows the use of C++ collection types like std::vector in for-in loops in Swift, and also provides access to Swift’s collection APIs like map and filter for such types.
Improved support for bridging C++ operators to Swift.
Improved Swift’s compatibility with the Foundation framework in Objective-C++ mode (i.e. when C++ interoperability is enabled).
Using Swift APIs from C++
Designed and implemented support for bridging Swift structs, enums, and classes to C++.
Added support for bridging functions, methods, properties and initializers to C++.
Designed and implemented initial support for bridging generic functions and types to C++.
This allows C++ code to call Swift generic APIs that don’t have generic requirements.
Added initial support for using Array and Optional Swift standard library types from C++.
Added an overlay for the Array type that allows it to be used in C++ ranged-based for loops.
Added initial support for bridging functions that throw to C++.
Looking ahead
In the time frame of the next Swift release, the workgroup is going to focus on taking some of the currently prototyped interoperability features through Swift’s evolution process. The workgroup intends to finalize and publish the vision documents and is planning to submit several evolution pitches. The workgroup is going to prioritize bug fixes and stabilization of these compiler features as well. Additionally, the workgroup is planning to work on related ecosystem features like improved debugging and editor tooling support for projects that use interoperability.
I would like to thank the members of the C++ interoperability workgroup and the Swift community for their contributions to this project. The workgroup has made great strides towards the vision of greater bi-directional interoperability between C++ and Swift and we’re excited for what will come next.
Is there a reason why this is not String(cppString:)? “cpp” is generally understood to refer to C++: it’s the standard file extension for C++ implementation files, and GitHub-Flavored Markdown recognizes it as the language name for C++ when introducing a code block with syntax highlighting. I don’t think that “cxx” has the same strong association with C++.
FWIW, in macOS and Linux CLI cpp command will launch a C preprocessor, not a C++ compiler.
There's already a precedent for this in public SwiftPM API: CXXSetting type and cxxSettings property of Target, introduced in Swift 5.0. Our users (at least those interested in C++) have already been exposed to this naming convention for more than 3 and a half years.
Either way, I personally would like this thread to stay focused on overall progress with interop. Naming convention discussions, especially those dedicated to a specific API, deserve their own separate thread.
Thanks for the interesting update. As someone who is writing (network protocol) C++ code for microcontrollers which I really would love to reuse from Swift, I wonder about a general rough timeframe. I see that you're working on this since quite a while and though I enjoy the progress updates, I'm not enough "into" the problem to realize how far you really are.
So… for calling a C++ library that is using bits and pieces from STL, how much of an effort is left over until we can just import that like we do with C libraries? Are we talking months, a year, or multiple years?
I cannot provide a specific answer. However, over the next couple of Swift releases we're aiming to be in a position where this becomes possible for a subset of an API surface of any existing C++ library, including parts of STL itself as well.
Right now it's possible to try this feature in an experimental manner, so you could probably import large chunks of the library you're interested in into Swift already. Are you interested in potentially becoming an early adopter so that you could try to see if our current experimental support could work for your use case? That could help us refine the initially supported version of interop as well.
I managed to do interop from Swift to C++ but I can't seem to do the opposite.
Where can I find a minimal example to call a Swift function from C++ with instruction on project setup using Swift 5.7.2 (or other toolchain) and Xcode 14.2 ?
I guess there must be a flag like -emit-cxx-header or something else to pass to the Swift compiler to get the required c++ header ?
I used this flag with success to experiment when calling C++ from Swift.
However problem is calling Swift function from C++.
I experimented with a very simple project consisting of a minimal Swift library and C++ code as documented in the user guide and using -enable-experimental-cxx-interop but with no success. The generated header fille don't include the c++ function signature.
Anybody having success calling a simple Swift function from C++ ?
Update: It's better using -Xfrontend -enable-experimental-cxx-interop with @_expose(Cxx) and dropping -clang-header-expose-decls=all-public.
I can use Swift from C++ in Xcode with the latest development snapshot toolchain. I use -Xfrontend -clang-header-expose-decls=all-public in the "Build Settings - Other Swift Flags" setting. As the name suggests, make sure the access levels of your Swift functions and types are public; otherwise they won't show up in the generated header.
Thanks a lot Tongjie, that works ! The public function now appear in the header without the need to use the @_expose attribute.
My very basic experiment calling Swift from C++ now work ok on macOS.
Ultimately I expect the interop to work also on Windows and Linux.
I tried on Windows and I can't see any header generated.
The build ran ok but I there is no header generated at all.
// Ultra simple Swift library
// file.swift
import Foundation
// Expose a Swift function to C++
// The function must be public
// To expose _all_ the public functions, use the flag -Xfrontend -clang-header-expose-decls=all-public
// The @_expose attribute can be used to expose the function using a different name, useful when dealing with function overloads
// ref: https://github.com/apple/swift/blob/main/docs/CppInteroperability/UserGuide-CallingSwiftFromC%2B%2B.md
// Note: in the UserGuide this attribute looks like @expose(C++, foo) however,
// at least in the 5.8 and latest toolchain snapshot we must instead write @_expose(Cxx, "foo")
//@_expose(Cxx, "my_exposed_swift_add")
public func my_swift_add(a: Int64, b: Int64) -> Int64 {
return a + b
}