Skip to content

Instantly share code, notes, and snippets.

@rfdickerson
Last active February 11, 2016 16:39
Show Gist options
  • Select an option

  • Save rfdickerson/ea6a9d82ca9cc5968a89 to your computer and use it in GitHub Desktop.

Select an option

Save rfdickerson/ea6a9d82ca9cc5968a89 to your computer and use it in GitHub Desktop.
Swift and C integration

Swift C-bindings Tutorial

##Creating a static library:

Suppose you have a project with the following structure:

.
+-- CAwesome
|   +-- awesome.h
|   +-- awesome.c
|   +-- module.modulemap
+-- hello.swift
+-- Makefile

I strongly recommend using a Makefile in your project root to launch the build. The Swift Package Manager does something very similiar to the following, but for now we are going to create our own custom Makefile to have more control over the build process.

AWESOME_DIR=CAwesome
BUILD_DIR=build
all: hello

${BUILD_DIR}/awesome.a: ${AWESOME_DIR}/awesome.c
  clang -c ${AWESOME_DIR}/awesome.c -o ${BUILD_DIR}/awesome.o
  ar -r ${BUILD_DIR}/libawesome.a ${BUILD_DIR}/awesome.o

hello: hello.swift ${BUILD_DIR}/libawesome.a
  swiftc -I${AWESOME_DIR} -L${BUILD_DIR} -lawesome hello.swift

module.modulemap contains:

module CAwesome [system] {
       header "awesome.h"
       link "awesome"
       export *
}

Pointers

In your awesome.h file, suppose you have:

typedef struct Point_t {
  float x;
  float y;
  float z;
} Point;

After you import CAwesome, you can use the struct directly in Swift:

var p1 = Point()
p1.x = 3
p1.x = 4
p1.x = 5
typealias PointRef = UnsafeMutablePointer<Point_t>

let p2 = PointRef.alloc(1)
// ensure the memory is freed when leaving scope.
defer { p2.dealloc(1) }

p2.memory.x = 3
p2.memory.y = 4
p2.memory.z = 5

Calling Swift closures in C

typedef void (*callback_t)(void);

void run_callback( callback_t callback);
#include "awesome.h"

void run_callback( callback_t callback) {
  callback();
}

Now, in your Swift code you can construct the function or closure and pass the function pointer to C:

func sayHello() {
  print("Hello World")
}

run_callback( sayHello )

// or create the closure directly upon invocation:
run_callback() { 
  print("Hello World") 
}

If you want to pass arguments to the closure, you can use the following

typedef int (*binary_function_t)(int a, int b);

int execute_binary_function( int a, int b, binary_function_t function) {
  return function(a, b);
}

In Swift, you will invoke this callback using:

let result = execute_binary_function( 6, 7) {
  a,b in
  return a * b
}

Or more simply:

let result = execute_binary_function( 6, 7) { $0 * $1 }

Using Swift code in other projects:

You can compile the Swift code to object code:

$ swiftc -emit-object -parse-as-library simple.swift -o simple.o
nm simple.o

Results in:

0000000000000000 T __TF6simple8sayHelloFT_T_
                 U __TFSSCfT21_builtinStringLiteralBp8byteSizeBw7isASCIIBi1__SS
                 U __TFs5printFTGSaP__9separatorSS10terminatorSS_T_
                 U __TIFs5printFTGSaP__9separatorSS10terminatorSS_T_A0_
                 U __TIFs5printFTGSaP__9separatorSS10terminatorSS_T_A1_
                 U __TMSS
                 U __TTSg5P____TFs27_allocateUninitializedArrayurFBwTGSax_Bp_

When Swift compiles the object code, it will use a series of rules to create mangled symbols that the linker will use later. More information about the rules can be found here. You can use the Swift demangler utility to get the original method name:

swift-demangle __TF6simple8sayHelloFT_T_

Results in:

_TF6simple8sayHelloFT_T_ ---> simple.sayHello () -> ()
extern _TF6simple8sayHelloFT_T_();

int main(int argc, char ** argv) {

  _TF6simple8sayHelloFT_T_();
  
}
$ clang -c main.c -o main.o

You can link your main and simple objects together building an executable named example:

$ clang -L/path/to/swiftCore.dylib main.o simple.o -o example
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment