Swift/C++ interop third party library integration feedback

At Ordo One, we recently tried using Swift/C++ interoperability for integrating a third-party library on Linux. I’d like to share some feedback on improvements we’ve noticed, as well as a few unexpected behaviors.

First of all, thank you for the huge effort behind this feature. It’s really impressive that Swift can now work directly with C++—this removes a lot of the pain of building an additional C layer.

Combined with other improvements, this is an uber-super feature.

We were able to successfully integrate a third-party library using this interop.

There are a few key things we’ve run into and worked around, but addressing them would make development much smoother:

  1. Pimpl idiom with precompiled libraries
    Many precompiled libraries use the pimpl idiom. These classes are not automatically accessible in Swift. I raised this previously in slack and on the swift forum with examples, but didn’t receive feedback. Generally, It feels strange that class types aren’t available while structs are, since from a C++ perspective there isn’t much difference between them.

  2. Virtual functions requiring overrides
    Classes with virtual functions that need to be overridden in client code are a major pain point. Right now I have to wrap all of them and expose a different interface.
    Ideally, it would be nice if they are available as non-final classes with `open func` to be able to override them. However I understand that it might be nearly impossible as swift and c++ classes are significantly different.

  3. Protocols in C++
    As a workaround for (2), I tried exposing Swift protocols to C++. Unfortunately, protocols are not yet supported in C++.
    It would be very useful if they could be represented as pure virtual classes.

  4. std::function and Swift closures
    Since I couldn’t override C++ classes directly or use Swift protocols in C++, I ended up relying on callbacks with std::function. Initially I hoped this would “just work,” and in a sense it did—but only as pure C functions. This led to the need for additional wrappers and some problems passing references. In the end I used this wrapper with modifications for variadic template arguments.

I also noticed real progress between Swift 6.1 and 6.2—some issues were fixed and new features were added.

  1. Swift headers (e.g. MyLib-Swift.h) can now be imported into C++, which previously gave a “file not found” error.

  2. Static libraries in bundles are now supported on Linux. In 6.1 this only worked on macOS with XCFs.

  3. Some linking errors disappeared between 6.1.0 and 6.1.1. For example, I previously hit errors like

    Summary
    foo.swift.o:foo.swift.o:function std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_add_ref_copy():(.text._ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE15_M_add_ref_copyEv+0x1a): error: undefined reference to '__gnu_cxx::__atomic_add_dispatch(int*, int)'
    

Miscellaneous Issues and Nice-to-Haves

  1. It would be great to have a more direct way to construct std::optional from Swift. I couldn’t find something like .init(value), but perhaps I missed it.

  2. Frequent warnings like: `- warning: 'import_owned' Swift attribute ignored on type 'vector': type is not copyable or destructible [#ClangDeclarationImport]`

  3. Direct template support in Swift without requiring aliases.

  4. In a project structured as SwiftTarget -> CppTarget -> SwiftExecutable, I wasn’t able to use `#include "SwiftTarget-Swift.h"` inside a C++ header. Including it leads to a compilation error when building the Swift executable:

$ swift build --verbose…/…/Sources/CppTarget/Headers/LibCpp/Test.h:4:10: error: ‘SwiftTarget-Swift.h’ file not found
3 |
4 | #include "SwiftTarget-Swift.h"
5 |          `- error: 'SwiftTarget-Swift.h' file not found

Once again, a big thank you to everyone working on this. The progress is impressive, and even with the rough edges, Swift/C++ interoperability already makes a big difference for real-world use cases. I hope this feedback helps guide further improvements.

Happy to elaborate further if something is unclear and/or file issues if it would be helpful for tracking something specific.

There are several links for the related issues that I have seen/created before:

  1. https://forums.swift.org/t/swift-noncopyable-macro-questions/80142
  2. https://github.com/swiftlang/swift/issues/83805
  3. https://github.com/swiftlang/swift/issues/83536 - seems kinda regression in 6.2
  4. https://github.com/swiftlang/swift/issues/80269 / https://github.com/swiftlang/swift/issues/82783 - also when trying to pass swift closure to std function
13 Likes

I also see that ‘import-owned’ warning everywhere. I really am curious why it happens since this is part of the C++ std library and it’s not like it’s my custom code that is giving warnings…

1 Like

I also had problems with std::function and Swift. It turns out Clang blocks are interoperable with both std::function and Swift closures, so I ended up adding a thin C++ layer that accepts Clang blocks and just forwards those.

2 Likes

Thank you for your suggestion!
That sounds really good and probably deserves a separate case for swift compiler to make it work automatically.

I guess that blocks should work much better - I will give a try this approach in our project as well.

1 Like