Swift Interoperation With C/C++ Using Clang Modules

Swift Interoperation With C/C++ Using Clang Modules

Swift has evolved to become a very versatile and powerful modern programming language. However, at times it may be necessary or beneficial to dig down into the lower layers and interoperate with C or C++ for a variety of reasons. These could include:

  • Needing or wanting to use an existing library written in C/C++
  • Cross platform development, with common code written in C/C++
  • Integrating C/C++ legacy code into a new app
  • Performance critical code that needs to leverage the flexibility and control afforded by a low-level language

While some may never need to deal with these requirements, it is something I have faced on numerous occasions throughout my career as an iOS developer. Furthermore, Apple is currently moving forward with their experimental C++ interoperability with Swift stats (currently Swift can only talk to C, meaning that any communication with C++ code must be done through a pure C interface). This will greatly facilitate bringing in C++ specific code into a Swift project by eliminating the boilerplate code currently necessary to create a C interface around C++. It's evident that Apple views this as a valuable step forward for Swift, and that the need is there.

Importing C/C++ using Clang Modules

The most common case involving interoperation is for Swift to call C/C++ code, and there are two ways to do this. The first should be familiar to anyone who has had to interoperate with Objective-C: bridging headers. For projects that just need to import a few files of C or C++, this solution is likely to be sufficient. With larger projects that utilize a more complex set of C/C++ libraries and code, however, Clang Modules will result in a much better organization of the imported code and avoid piling many header files into the bridging header of the Xcode project. For example, the submodules feature of Clang Modules allows you to organize and import specific features from a library:

         
          
import SomeLibrary.SomeFeature
import SomeLibrary.AnotherFeature.SmallFeature
          
      

Another important consideration is that unlike with bridging headers that import code into the global scope, Clang Modules are imported at the file scope via the import statement just as you would do with import UIKit and others. Clang Modules that contain the C/C++ code are thus only visible to the files they are imported in, which also makes it far clearer where this code is used within a Swift project.

The key piece in creating a module from C/C++ code is a module map file, which maps one or more headers onto the structure of the module it defines. In the following test project, we can see that I've defined two modules: CLib and CppLib (to illustrate interoperation with both C and C++). Each one contains its own module map file, which Clang expects to be named "module.modulemap".

Swift Image

The module map files themselves are very straightforward.

         
          
// CLib/module.modulemap:
module CLib {
	header "CLib.h"
	export *
}
 
// CppLib/module.modulemap:
module CppLib {
	header "CppLib-C.h"
	export *
}
          
      

Both module map files define the name of their respective module, the header file containing the interface, and declare that the module should export everything as part of its API.

I’ve written one simple function in each of the header files to illustrate calling them from Swift.

         
          
// CLib.h
#include <stdio.h>
 
int CLibTest(void);
 
 
// CppLib.h
#include <iostream>
 
int CppLibTest();

          
      

The C header can be used as-is by the module map file as we can see above, but the C++ header needs to be wrapped in a pure C interface:

         
          
// CppLib-C.h
#ifdef __cplusplus
extern "C" {
#endif
 
int CppLib_CppLibTest();
 
#ifdef __cplusplus
}
#endif
 
 
// CppLib-C.cpp
#include "CppLib-C.h"
#include "CppLib.h"
 
int CppLib_CppLibTest() {
	return CppLibTest();
}
         
      

One last thing is to tell Xcode where to find the modules for importing. In the project's Build Settings, directories containing module map files must be listed under Swift Compiler - Search Paths, Import Paths.

Swift Image

The modules are now ready to be imported into any Swift file that needs to call the C/C++ code.

         
          
import UIKit
import CLib
import CppLib
 
class ViewController: UIViewController {
 
	override func viewDidLoad() {
    	super.viewDidLoad()
       
    	let cValue = CLibTest()
    	let cppValue = CppLib_CppLibTest()
	}
}
 
           
      

For more information on Clang Modules, including the Module Map Language and all the various features it provides, see the documentation here.

Calling Swift from C/C++

In some cases, you may need to call Swift from C/C++ code. Usually this involves something UI or OS specific, such as logic in the C/C++ code needing to trigger something UI related (e.g., displaying an alert, file selector, etc.) or perform some OS related function (e.g., getting user/system paths, controlling the idle sleep timer on iOS, etc.). This is accomplished through function pointers in the C/C++ code that define what it needs to delegate to the platform.

Let's take an example of triggering an alert from within C/C++. First, we define a function pointer in C for showing an alert with a single message.

         
          
// CLib.h
 
typedef void(*show_alert_f)(const char *message);
show_alert_f showAlertFunc;
 
bool DoSomeWork(void);
The following C function is called from Swift and will trigger an alert.
// CLib.c
 
show_alert_f showAlertFunc = NULL;
 
bool DoSomeWork() {
	char *alertMessage = "This is an alert message from C!";
	if (showAlertFunc) {
    	showAlertFunc(alertMessage);
    	return true;
	}

	return false;
}
             
      

In Swift, we define a function called showAlert that will do the work of displaying an alert using the iOS SDK. This function is assigned to the showAlertFunc function pointer declared in CLib from within viewDidLoad. For this to work, the Swift function must be able to be converted to the C calling convention. In other words, it must be either a non-generic global function, a local function that doesn't capture any variables, or a closure that doesn't capture any local variables.

Furthermore, the Swift function's signature must match that of the function pointer in C, and this can sometimes be a bit tricky depending on the types involved. The show alert function pointer in our example returns nothing and takes a single parameter of type const char *. The way pointers are handled when mapping from C to Swift is by UnsafeMutablePointer <Type> for regular pointers or UnsafePointer <Type> for const-declared pointers. In our case, const char* maps to UnsafePointer <Int8>? Additionally, void pointers map to UnsafeMutableRawPointer, while incomplete or unknown types map to OpaquePointer. Primitive types are straightforward, with int mapping to Int32, float maps to Float, etc. For a more complete listing of type mappings, see Apple's documentation here.

For this example, the alert will be triggered by a button tap, so we define a function that will respond to the event in our View Controller, which then calls the C function that ends up triggering the alert.

          
          
import SomeLibrary.SomeFeature
import SomeLibrary.AnotherFeature.SmallFeature
            
      
         
          
import UIKit
import CLib
 
class ViewController: UIViewController {
 
	override func viewDidLoad() {
    	super.viewDidLoad()
       
// Assign the C function pointer, showAlertFunc, to local Swift function showAlert
    	showAlertFunc = showAlert
	}
 
	@IBAction
	func buttonTapped(_ sender: UIButton?) {
    	alertViewController = self
 
// Call the C function DoSomeWork, which will trigger the alert to be shown via the function pointer showAlertFunc
    	guard DoSomeWork() else {
        	debugPrint("No function assigned to show alert")
        	return
    	}
	}
}
 
fileprivate var alertViewController: UIViewController?
 
fileprivate func showAlert(_ message: UnsafePointer<Int8>?) {
	guard let viewController = alertViewController else {
    	debugPrint("No view controller assigned for alert")
    	return
	}
   
	var alertMessage: String?
	if let message = message {
    	alertMessage = String(cString: message)
	}
	let alert = UIAlertController(title: "Message", message: alertMessage, preferredStyle: .alert)
	alert.addAction(UIAlertAction(title: "Ok", style: .default))
	viewController.present(alert, animated: true)
}
          
      

Now, when we run the app and tap the button, the code in CLib triggers the alert we show from Swift:

Swift Image

Interoperation with C/C++ from Swift can open up powerful and flexible possibilities. C and C++ have been around for decades and have a myriad of valuable libraries available for use. They also provide the control and low-level access required for performance critical code and can be an alternative to higher level frameworks for certain cross-platform development needs.

Need help with custom app development in Vancouver? Then contact Atimi today.

Find out how we can work together to bring your ideas to life.

Find out how we can work together to bring your ideas to life.

Thank you for contacting us