Said Sikira
Said Sikira

Reputation: 4533

Compile C code and expose it to Swift under Linux

Is there a way to compile native C or C++ code and expose it to Swift on Linux? I can see that several Apple libraries like libdispatch are written in pure C and that you can access them in Swift just by importing them.

To set the example let's say that I have two files Car.c and Car.h that define structure named Car. Is there a way that I can compile them and use them in Swift by writing import statement?

import Car

I've tried writing module.modulemap file inside directory where .c, .h and Package.swift files are located:

module Car {
   header "Car.h"
   export *
}

and running swift build. This yield error:

<unknown>:0: error: unexpected 'commands' value (expected map)
<unknown>:0: error: unable to load build file

I'm using Swift version 3.0-dev (March 24 2016)

[Update 1]

I've contacted Max(mxcl) - one of the creators of Swift Package Manager and he told me to get rid of the modulemap and put the .c and .h files directly in Sources folder. After I did that package compiled but it's not available as module. Also I can't call any of the defined functions in the .h file.

Upvotes: 12

Views: 4523

Answers (2)

Philip
Philip

Reputation: 1606

Using C functions in swift requires a bridging header that includes all the C functionality you need. For example, myBridgingHeader.h which contains #include "Car.h"and whatever other C stuff you want. I believe C++ is currently not supported.

Once you have the bridging header you need to make swift aware of it. Xcode users get this for free when they add it to the project. In Linux, use '-import-objc-header /path/to/header' flag when compiling.

Edit: I've added a complete example below consisting of 6 files for any others who may have this question. It's basically the same as the one above but I didn't see that til I had already put it together haha. Also, it maybe useful for someone who needs to link against static libraries.

Copy the file contents below to appropriately named files, make, then ./hello and that should work. For the record, I've only run this on swift version 2.2-dev (use swift --version to check yours)

  • hello.swift:

    let n: Int32 = 5
    print("Hello, Swift World!")
    print("mult2(\(n,N)) = \(mult2(n,N))")
    print("CONST1=\(CONST1), CONST2=\(CONST2), CONST3=\(CONST3)")
    
  • bridge.h:

    #include "defs.h"
    #include "mult.h"
    
  • defs.h:

    #define CONST1 1
    #define CONST2 2
    #define CONST3 3
    
  • mult.h:

    #define N 7
    int mult2(int,int);
    
  • mult.c:

    #include "defs.h"
    #include "mult.h"
    int mult2(int a, int b)
    {
         return a*b;
    }
    
  • Makefile:

    all: hello
    
    hello: libmult.a
         swiftc hello.swift -import-objc-header ./bridge.h -L. -lmult -o hello
    
    libmult.a: mult.o
         ar -rc libmult.a mult.o
         ranlib libmult.a
    
    mult.o: mult.c mult.h defs.h
         gcc -c mult.c -o mult.o
    
    .PHONY: clean   
    clean:
         rm -f *.o *.a hello
    

Upvotes: 5

Anatoli P
Anatoli P

Reputation: 4891

If you build a library out of your C code, you can create a system module for it, which can then be imported into Swift, see this answer: Use a C library in Swift on Linux.

Another way to approach this task is to create a bridging header, as suggested by @Philip. Here is an oversimplified example. Let's consider the following C code:

/* In car.h */
int getInt();

/* In car.c */
int getInt() { return 123; }

We will use car.h as the bridging header. The swift source is (in file junk.swift):

print("Hi from swift!")
var i = getInt()
print("And here is an int from C: \(i)!")

First, create an object file, car.o, from car.c:

gcc -c car.c

Now build an executable, junk, as follows:

swiftc -import-objc-header car.h junk.swift car.o -o junk

Running the executable gives:

$ ./junk
Hi from swift!
And here is an int from C: 123!

The -import-objc-header option is hidden. To see it and a bunch of other hidden options, run:

swiftc -help-hidden 

I did this using Swift 3.0 development snapshot for Ubuntu 14.04 from April 12, available here: https://swift.org/builds/development/ubuntu1404/swift-DEVELOPMENT-SNAPSHOT-2016-04-12-a/swift-DEVELOPMENT-SNAPSHOT-2016-04-12-a-ubuntu14.04.tar.gz

Now, if you want to use C++, you will need to create a wrapper, written in a C++ source file and compiled with a C++ compiler, but with functions callable from C by using extern "C". Those functions can then be called from Swift as any C function. See, for example, this answer: Can I mix Swift with C++? Like the Objective - C .mm files

Upvotes: 15

Related Questions