This gist is a simple example on how to call a function written in swift from C/C++, without taking the detour via Objective-C/C++.
In this example we're going to invoke a function called say_hello
, which,
as the name already suggests, prints "Hello, World!" to the terminal.
say_hello is defined as following:
// func.swift
@_cdecl("say_hello")
public func say_hello(){
print("Hello, World!")
}
This is just the canonical "Hello, World!"-example wrapped in a function
called say\_hello
. The only thing, that stands out, is the attribute @_cdecl("say\_hello")
.
@_cdecl
is an undocumented attribute, which according to vague informations found on the web,
enforces C-name mangling. Note: @_cdecl
solely enforces the name mangling; not the calling convention!
In order to link our code with a C/C++ application, will need a library. In order to compile our code as a linked library,
specify -emit-library
when compiling the .swift-file.
We want to link our swift code with our C/C++ applications, which means, that we'll need this code to be compiled as a library. In order to compiler our code as a linked library, just specify '-emit-library' when compiling the .swift-file.
swiftc func.swift -emit-library
This will result in a linked library called "libfunc.dylib" (or "libfunc.so" on Linux, I suppose),
which exports our function say_hello
as _say_hello
.
Now to our C++:
// main.cpp
// only "extern" when targeting C.
extern "C" void say_hello();
int main(){
say_hello(); // Prints "Hello, World!"
return 0;
}
Here we declare say_hello
as an external reference, which will be provided by the library, we compiled before.
What's left at this point is just to compile and link our code.
gcc -std=c++11 -c main.cpp -o main.o
gcc libfunc.dylib main.o -o main
When we run the application, we're getting greeted with following:
./main
> Hello, World!
This was my first "technical article". Feedback is welcome!
None the less, I hope I could help you with this "article".
J. Kirsch
- SO Answer by Max Desiatov
- Trial'n'Error with the command line tool
nm
- Other links, that I cannot remember... :-(
I tinkered around with it recently and found, that the
@_cdecl
directive indeed also forces the cdecl-calling convention, which trivially enables us to pass arguments. See following example:main.cpp:
lib.swift:
build.ninja:
For those who do not use the ninja build system (and have no idea what is going on above), you can compile this example using following terminal commands
P.S.: I hacked this example out of my memory; there could be some trivial mistakes.