Has anyone managed to get their GitHub Actions workflows back up and working for C++ interop enabled projects on windows-latest by chance?
I naively assumed "oh, I can just use/install winget to bring MSVC to a version where the STL doesn't break swift" like this, but it's not even possible to install winget on Windows Server, because it uses a .msixbundle which requires the Microsoft Store infrastructure to install, which is not something that Windows Server environments support.
All of my Windows CI for all C++ interop enabled projects has been broken for 2 months now, and I am hopeful to get that working again. If anyone knows of any short-term workarounds, or if there is a particular Swift 6 release on the horizon that might be pulling in the corresponding Clang changes to work with Microsoft's STL again? I think it would be very helpful.
I figured out a workaround, super ugly hack, the only limitation is I believe it does not work for .testTarget(), but this is everything you need to get Swift/C++ interop enabled projects to work on Windows with recent versions of the Microsoft STL:
Working CI of this workaround being utilized can be found here, leveraging Swift/C++ interop on Swift 5.10 and MSVC build tools 14.41.34120, the current version at the time of writing using the windows-latest GitHub Actions runner.
It’s only a snippet; use swift package init to create a complete package manifest, and a proper directory structure, then paste the above inside the targets: [] array.
Also ensure you are using // swift-tools-version: 5.10 at the very top of the file.
Also if it’s any consolation C++ interop certainly works, even on windows, there are some rough patches, but windows successfully builds and runs this example, which are all massive C++ libraries being imported into swift at the top of that file.
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "hello",
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "hello",
targets: ["hello"]),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "hello"),
cxxSettings: [
.define("_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH", .when(platforms: [.windows])),
.define("_ALLOW_KEYWORD_MACROS", to: "1", .when(platforms: [.windows])),
.define("static_assert(_conditional, ...)", to: "", .when(platforms: [.windows])),
],
swiftSettings: [
.interoperabilityMode(.Cxx)
]
.testTarget(
name: "helloTests",
dependencies: ["hello"]
),
]
)
Most likey I pasted your snippet in a wrong place.
And fill in some very basic c++ code, like a single function declaration in the header, and the function definition in the source file, and add that new target to your Package.swift manifest.
And lastly, add this new C++ target dependency to your hello swift target:
You should then be able to import hellocxx in any of your swift files inside the hello directory, and call the C++ code declared in your hellocxx.h file directly from swift.
The workaround above works well for getting around static_assert() at least in the short term until Swift’s embedded clang is fixed for that part of the Microsoft STL.
However, there appears to be one more issue, I actually do believe it to be the only other remaining error regarding Microsoft’s STL, but it doesn’t make much sense to me, when importing C++ targets in Swift on windows with Swift/C++ interop, even when cxxLanguageStandard: .cxx17 is specified, if you attempt to #include <any> it throws the following error:
Which does not make much sense given that the project is set for the C++17 language standard. If anyone knows how to get around that one by chance, I suppose a way that preferably doesn’t involve us having to roll our own <any> header with a custom implementation and “shimming” it in? But sometimes us early adopters have to do what we have to do
As a final windows swift/c++ interop bug, I’ve noticed the following:
A typical pattern that I follow with smart pointers like std::shared_ptr, is that I’ll write an extension around the smart pointer in Swift, abstracting away pointee accessors on the end of the Swift code, for a more familiar syntax to how C++ APIs are called natively, as an example:
// implementation detail, not important.
class MyClass
{
public:
void doSomething();
} SWIFT_IMMORTAL_REFERENCE;
// declare MyClassPtr shared pointer to MyClass.
using MyClassPtr = std::shared_ptr<MyClass>;
// so swift code can call doSomething() directly.
public extension MyClassPtr
{
func doSomething()
{
pointee.doSomething()
}
}
This works on macOS and Linux, but on Windows (Swift 5.10) I get the following error:
error: cannot find 'pointee' in scope
This tells me that the instance member, pointee is being hidden, but I believe it should be within visible scope, if there’s some way to achieve that.
The pattern of smart pointers to foreign reference types is not currently supported in C++ interop. The compiler-generated var pointee normally unwraps the pointer and returns a value, which is not something you could do with a reference type: when annotating a type as SWIFT_*_REFERENCE, you're essentially making a promise to the compiler that this type is only ever passed around by pointer or reference and never by value.
Swift should certainly do a better job at diagnosing this. For now I would recommend refactoring the C++ API to either use raw pointers with types annotated as SWIFT_*_REFERENCE, or smart pointers of value types, if possible.
I see, so more of a bug that it was even working like this on macOS and Linux in the first place
(a combination of extensions of smart pointers whose var pointee was returning the underlying value of types also marked as SWIFT_*_REFERENCE).
I can definitely see a use case for this though, particularly in the realm of reference pointers, a common paradigm across many C++ projects, perhaps something nice to support in the future?