Swift allows you to call existing C library calls and system calls normally. This is correct from the perspective of making good use of past assets, but since Swift is more type-strict than C, it is not uncommon to have trouble calling C functions.
This time, I rewrote the third argument start_routine
of pthread_create
from the C function to the closure of Swift. I hope it helps you to see what happens to the code cast in C in Swift.
(Because I am a beginner in Swift, please let me know if there are any mistakes)
pthread_create
creates a new thread and passes the function pointer of the function to be executed in the new thread with the third argument start_routine
. Function arguments and return values are void *
, so if you want to use different types of arguments and return values, you need to typecast both the caller and the callee.
It's a C-like idea to make anything void *
, but of course it doesn't go well with Swift.
This time, I wanted to use a function that takes ʻintand
char ** as arguments and returns ʻint
(in short, it's a main
function) from pthread_create
, so I used a wrapper function for that. I wrote it in C.
void *pthread_main_routine(void *_arg)
{
void **arg = (void **)_arg;
int argc = (int)arg[0];
char **argv = (char**)arg[1];
void *retval = (void *)(long)orig_main(argc, argv);
return retval;
}
This was called on the Swift side as follows.
var thread_arg = [
UnsafeMutablePointer<Void>(bitPattern: args.pointers.count-1),
UnsafeMutablePointer<Void>(args.pointers)
]
pthread_create(&thread, nil, pthread_main_routine, &thread_arg)
It worked in the above code as well, but I tried replacing the C function with a Swift closure for various reasons.
typealias VoidPtr = UnsafeMutablePointer<Void>
typealias VoidPtrArray = [UnsafeMutablePointer<Void>]
typealias CharPtrArray = [UnsafeMutablePointer<CChar>]
let thread_arg = [
unsafeBitCast(args.pointers.count-1, VoidPtr.self),
unsafeBitCast(args.pointers, VoidPtr.self),
]
pthread_create(&thread, nil, {_arg in
let arg = unsafeBitCast(_arg, VoidPtrArray.self)
let argc = Int32(unsafeBitCast(arg[0], Int.self))
let argv = unsafeBitCast(arg[1], CharPtrArray.self)
let retval = unsafeBitCast(Int(orig_main(argc, argv)), VoidPtr.self)
return retval
}, unsafeBitCast(thread_arg, VoidPtr.self))
I managed to make something that works with a lot of ʻunsafeBitCast`, but the code is quite verbose and hard to read. Code that is easier to write in C should be written in C.
By the way, please note that the contents passed are slightly different depending on whether the Swift array is converted to void *
or char **
to void *
in the two codes.
The handling of C functions in Swift2 is not good, and only the global function name itself or closure literal can be placed in the third argument of this pthread_create
. If you assign a function to a variable and give that variable as an argument, you will get a compile error as follows.
A C function pointer can only be formed from a reference to a 'func' or a literal closure
Also, don't write code that depends on the environment, even if it's a closure literal. For example, if you refer to an instance variable from within a closure, you will get a compile error as follows.
A C function pointer cannot be formed from a closure that captures context
I feel that the latter can't be helped, but what about the former? I feel like that. I hope Swift3 will be easier to handle around here.
Recommended Posts