Swift++ == Swift => C => C++ => STL
A Swift Wrapper, calling a C Wrapper, calling a C++ wrapper, calling STL with Swift Package Manager (Linux)
Swift support for C language is fantastic and it’s super easy to import in your Packages any kind of C Libraries (.so or .dylib) but if you want to import some C++ stuff, and in particular directly the source code and merge all Swift and C++ source code in a single Swift Package the things are a little bit more annoying and you need to create different level of wrappers.
Supposing you need to use and export in the Swift world an existent C++ library you first need to create a C Wrapper interface on it, then you can re-objectify everything creating a Swift class and then you can finally decide to expose a similar interface or something more Swifty.
In this tutorial I‘m focusing in particular on the basic steps for implementing these wrappers and suggesting an easy solution to configure your package and folder structures to implement all of this. Very important I’m not focusing at all on how to better Swiftify an existing C++ interface but instead, just to show a more potentially complex scenario, I will first create a simplified C++ Wrapper, then a C Wrapper and finally the Swift Wrapper trying to recreate an interface similar to the original C++ code.
Let’s start from the beginning. As an example we will create a Swift wrapper for the famous C++ stl::list template class. Of course there’s no need at all to use STL if you need to implement a linked list in Swift and there are several more Swifty options available to implement the final interface but as just said this is not the goal of this tutorial.
So, suppose you have this super simple C++ test code:
The goal is to replicate this with similar testing code in Swift as this:
As you can see I decided to use void* in C++ and Data in Swift as the main data representation format for sharing buffer between these two worlds in order to be more generic as possible.
You can see here the source code for the header file defining the interfaces for the CPPListIterator and CPPListWrapper classes I wrote for this sample simply wrapping respectively the std::iterator and the std::list classes for the standard C++ STL library.
Of course there is no real need to write this wrapper classes and I could have directly created a C Wrapper interface using stl::iterator and stl::list from there but again the goal here is to provide a more complex sample scenario where a full, complex, C++ custom class is needed in project.
In the omitted implementation of these interfaces I basically just call the equivalent method for the embedded stdList or iterator incapsulated private objects.
Now, once you have your C++ class hierarchy you want to export in Swift, the next step to do is to de-objectify all the C++ interfaces and create basically a simple extern “C” interface, in C++, exporting C methods that wrap access to the “this” C++ instance with a void* object always passed as first parameter to all interface methods.
In the abstract below for the implementation C++ code behind this extern “C” interface you see how to get back the “this” C++ pointer from the passed input parameter and how to create an initializer method to create the C++ object instance and return as a casted void* pointer.
Once you have now this de-objectified C interface for all the methods implemented in your original C++ classes you can recreate equivalent Swift classes behaviors.
As said here you can decide to implement in Swift a similar C++ interface or decide to be as Swifty as you want and change the usage scenarios for the basic original funcionalities adding protocols, enums, functional programming and whatever you want. In the context of this specific tutorial I decided to preserve in the following Swift sample code a similitude with the original C++ interface as much as possible.
Here just an abstract for the Swift STL Iterator wrapper:
As you can see the only little complexity here is dealing with data conversion and memory management converting data from the Swift world to the void* generic pointers I used in the C++ stl::list implementation.
Of course using String format could help a lot here in the conversion with C/C++ strings but once again, in order to cover more complex but general scenarios, I decided to use the Swift Data classes and converting back and forward to UnsafeRawPointer in order to pass a void* pointer to the Swift data.
Swift Package and Folder organization
Finally in order to put all the C++, C and Swift source code in a unique project and just build everything with the
swift build tool here are some advise about to easily manage your project subfolders and the main Swift Package file.
Starting from the subfolders organization I basically suggest to have under your Sources folder a specific main subfolder for your C and C++ headers and implementation. I called mine CListWrapper.
Under this folder you will add the C and C++ Wrapper implementation code but very important in order to avoid to configure a module.modulemap and let the Package.swift directly point to your C, and included C++, source code you need to have an “include” subfolder under this C Wrapper folder (CListWrapper for me). You will need to put in this “include” subfolders the .hpp header file where you define the extern “C” interface for your C++ classes that you want to call from the Swift wrapper.
For simplicity I also separated the C++ Wrapper code in a separate (CPPListWrapper) subfolder but this is not strictly needed.
Here is a dump for the specific subfolders organization I used in this sample:
Done. Once you have this subfolder organization your Package.swift file will look like this and you will be able to compile all your Swift, C and C++ code simply just calling the
swift build command.
C++ 11 Features
To enable C++ 11 features eventually used in your C++ code just use the
-std=c++11 flag in the
swift build command.
swift build -Xcxx -std=c++11
# build release:
swift build -c release -Xcxx -std=c++11
swift test -Xcxx -std=c++11
swift run -Xcxx -std=c++11
Full source code
Full source code for this tutorial are provided at this GitHub repo: