|
| 1 | +# Getting started with C++ Interoperability |
| 2 | + |
| 3 | +This document is desgined to get you started with bidirectional API-level interoperability between Swift and C++. |
| 4 | + |
| 5 | +## Table of Contents |
| 6 | + |
| 7 | +- [Creating a Module to contain your C++ source code](#creating-a-module-to-contain-your-c-source-code) |
| 8 | +- [Adding C++ to an Xcode project](#adding-c-to-an-xcode-project) |
| 9 | +- [Creating a Swift Package](#Creating-a-Swift-Package) |
| 10 | +- [Building with CMake](#building-with-cmake) |
| 11 | + |
| 12 | +## Creating a Module to contain your C++ source code |
| 13 | + |
| 14 | +- Create a new C++ implementation and header file |
| 15 | +- For this example we will call the files Cxx, so we should have a Cxx.cpp and Cxx.hpp. |
| 16 | +- Next create an empty file and call it `module.modulemap`, in this file create the module for your source code, and define your C++ header (`requires cplusplus` isn't required but it's convention for C++ modules, especially if they use C++ features). |
| 17 | + |
| 18 | +``` |
| 19 | +//In module.modulemap |
| 20 | +module Cxx { |
| 21 | + //note that your header should be the file that containts your method implementations |
| 22 | + header "Cxx.hpp" |
| 23 | + requires cplusplus |
| 24 | +} |
| 25 | +``` |
| 26 | + |
| 27 | +- Move the newly created files (Cxx.cpp, Cxx.hpp, module.modulemap) into a separate directory (this should remain in your project directory) |
| 28 | + |
| 29 | +<img width="252" alt="Screen Shot 2022-02-26 at 9 14 06 PM" src="https://user-images.githubusercontent.com/62521716/155867937-9d9d6c62-4418-414d-bc4e-5d12c2055022.png"> |
| 30 | + |
| 31 | +## Adding C++ to an Xcode project |
| 32 | +- In your xcode project, follow the steps [Creating a Module to contain your C++ source code](#creating-a-module-to-contain-your-c-source-code) in your project directory |
| 33 | + |
| 34 | +Add the C++ module to the include path and enable C++ interop: |
| 35 | +- Navigate to your project directory |
| 36 | +- In `Project` navigate to `Build Settings` -> `Swift Compiler` |
| 37 | +- Under `Custom Flags` -> `Other Swift Flags` add`-Xfrontend -enable-cxx-interop` |
| 38 | +- Under `Search Paths` -> `Import Paths` add your search path to the C++ module (i.e, `./ProjectName/Cxx`). |
| 39 | + |
| 40 | +``` |
| 41 | +//Add to Other Swift Flags and Import Paths respectively |
| 42 | +-Xfrontend -enable-cxx-interop |
| 43 | +-I./ProjectName/Cxx |
| 44 | +``` |
| 45 | + |
| 46 | +- This should now allow your to import your C++ Module into any `.swift` file. |
| 47 | + |
| 48 | +``` |
| 49 | +//In ViewController.swift |
| 50 | +import UIKit |
| 51 | +import Cxx |
| 52 | +
|
| 53 | +class ViewController: UIViewController { |
| 54 | + override func viewDidLoad() { |
| 55 | + super.viewDidLoad() |
| 56 | + let result = cxxFunction(7) |
| 57 | + print(result) |
| 58 | + } |
| 59 | +} |
| 60 | +``` |
| 61 | + |
| 62 | +``` |
| 63 | +//In Cxx.cpp |
| 64 | +
|
| 65 | +#include "Cxx.hpp" |
| 66 | +int cxxFunction(int n) { |
| 67 | + return n; |
| 68 | +} |
| 69 | +
|
| 70 | +``` |
| 71 | + |
| 72 | +``` |
| 73 | +//In Cxx.hpp |
| 74 | +
|
| 75 | +#ifndef Cxx_hpp |
| 76 | +#define Cxx_hpp |
| 77 | +
|
| 78 | +int cxxFunction(int n); |
| 79 | +
|
| 80 | +#endif |
| 81 | +
|
| 82 | +``` |
| 83 | + |
| 84 | + |
| 85 | +## Creating a Swift Package |
| 86 | +After creating your Swift package project, follow the steps [Creating a Module to contain your C++ source code](#creating-a-module-to-contain-your-c-source-code) in your `Source` directory |
| 87 | + |
| 88 | +- In your Package Manifest, you need to configure the Swift target's dependencies and compiler flags |
| 89 | +- In this example the name of the package is `Cxx_Interop` |
| 90 | +- Swift code will be in `Sources/Cxx_Interop` called `main.swift` |
| 91 | +- C++ source code follows the example shown in [Creating a Module to contain your C++ source code](#creating-a-module-to-contain-your-c-source-code) |
| 92 | +- Under targets, add the name of your C++ module and the directory containing the Swift code as a target. |
| 93 | +- In the target defining your Swift target, add a`dependencies` to the C++ Module, the `path`, `source`, and `swiftSettings` with `unsafeFlags` with the source to the C++ Module, and enable `-enable-cxx-interop` |
| 94 | + |
| 95 | +``` |
| 96 | +//In Package Manifest |
| 97 | +
|
| 98 | +import PackageDescription |
| 99 | +
|
| 100 | +let package = Package( |
| 101 | + name: "Cxx_Interop", |
| 102 | + platforms: [.macOS(.v12)], |
| 103 | + products: [ |
| 104 | + .library( |
| 105 | + name: "Cxx", |
| 106 | + targets: ["Cxx"]), |
| 107 | + .library( |
| 108 | + name: "Cxx_Interop", |
| 109 | + targets: ["Cxx_Interop"]), |
| 110 | + ], |
| 111 | + targets: [ |
| 112 | + .target( |
| 113 | + name: "Cxx", |
| 114 | + dependencies: [] |
| 115 | + ), |
| 116 | + .executableTarget( |
| 117 | + name: "Cxx_Interop", |
| 118 | + dependencies: ["Cxx"], |
| 119 | + path: "./Sources/Cxx_Interop", |
| 120 | + sources: [ "main.swift" ], |
| 121 | + swiftSettings: [.unsafeFlags([ |
| 122 | + "-I", "Sources/Cxx", |
| 123 | + "-Xfrontend", "-enable-cxx-interop", |
| 124 | + ])] |
| 125 | + ), |
| 126 | + ] |
| 127 | +) |
| 128 | +
|
| 129 | +``` |
| 130 | + |
| 131 | +- We are now able to import our C++ Module into our swift code, and import the package into existing projects |
| 132 | + |
| 133 | +``` |
| 134 | +//In main.swift |
| 135 | +
|
| 136 | +import Cxx |
| 137 | +
|
| 138 | +public struct Cxx_Interop { |
| 139 | + |
| 140 | + public func callCxxFunction(n: Int32) -> Int32 { |
| 141 | + return cxxFunction(n: n) |
| 142 | + } |
| 143 | +} |
| 144 | +
|
| 145 | +print(Cxx_Interop().callCxxFunction(n: 7)) |
| 146 | +//outputs: 7 |
| 147 | +
|
| 148 | +``` |
| 149 | + |
| 150 | +## Building with CMake |
| 151 | +After creating your project follow the steps [Creating a Module to contain your C++ source code](#creating-a-module-to-contain-your-c-source-code) |
| 152 | + |
| 153 | +- Create a `CMakeLists.txt` file and configure for your project |
| 154 | +- In`add_library` invoke `cxx-support` with the path to the C++ implementation file |
| 155 | +- Add the `target_include_directories` with `cxx-support` and path to the C++ Module `${CMAKE_SOURCE_DIR}/Sources/Cxx` |
| 156 | +- Add the `add_executable` to the specific files/directory you would like to generate source, with`SHELL:-Xfrontend -enable-cxx-interop`. |
| 157 | +- In the example below we will be following the file structure used in [Creating a Swift Package](#Creating-a-Swift-Package) |
| 158 | + |
| 159 | +``` |
| 160 | +//In CMakeLists.txt |
| 161 | +
|
| 162 | +cmake_minimum_required(VERSION 3.18) |
| 163 | +
|
| 164 | +project(Cxx_Interop LANGUAGES CXX Swift) |
| 165 | +
|
| 166 | +set(CMAKE_CXX_STANDARD 11) |
| 167 | +set(CMAKE_CXX_STANDARD_REQUIRED YES) |
| 168 | +set(CMAKE_CXX_EXTENSIONS OFF) |
| 169 | +
|
| 170 | +add_library(cxx-support ./Sources/Cxx/Cxx.cpp) |
| 171 | +target_compile_options(cxx-support PRIVATE |
| 172 | + -I${SWIFT_CXX_TOOLCHAIN}/usr/include/c++/v1 |
| 173 | + -fno-exceptions |
| 174 | + -fignore-exceptions |
| 175 | + -nostdinc++) |
| 176 | +target_include_directories(cxx-support PUBLIC |
| 177 | + ${CMAKE_SOURCE_DIR}/Sources/Cxx) |
| 178 | +
|
| 179 | +add_executable(Cxx_Interop ./Sources/Cxx_Interop/main.swift) |
| 180 | +target_compile_options(Cxx_Interop PRIVATE |
| 181 | + "SHELL:-Xfrontend -enable-cxx-interop" |
| 182 | +target_link_libraries(Cxx_Interop PRIVATE cxx-support) |
| 183 | +
|
| 184 | +``` |
| 185 | + |
| 186 | +``` |
| 187 | +//In main.swift |
| 188 | +
|
| 189 | +import Cxx |
| 190 | +
|
| 191 | +public struct Cxx_Interop { |
| 192 | + public static func main() { |
| 193 | + let result = cxxFunction(7) |
| 194 | + print(result) |
| 195 | + } |
| 196 | +} |
| 197 | +
|
| 198 | +Cxx_Interop.main() |
| 199 | +
|
| 200 | +``` |
| 201 | + |
| 202 | +- In your projects direcetoy, run `cmake` to generate the systems build files |
| 203 | + |
| 204 | +- To generate an Xcode project run `cmake -GXcode` |
| 205 | +- To generate with Ninja run `cmake -GNinja` |
| 206 | + |
| 207 | +- For more information on `cmake` see the 'GettingStarted' documentation: (https://github.com/apple/swift/blob/main/docs/HowToGuides/GettingStarted.md) |
| 208 | + |
| 209 | + |
0 commit comments