Swift - pass escaping closure to C API callback - c

I have C API that I use from Swift.
In Swift, I have:
enum GetSnapshotResult {
case success(snapshot: UIImage, String)
case failure()
}
func getSnapshot(completion: #escaping (GetSnapshotResult) -> Void) {
CAPIGetSnapshot(nil) { (_) in
completion(
.success(
snapshot: UIImage(),
"test"
)
)
}
}
And in C API:
void CAPIGetSnapshot(void * ptr, void(*callbackOnFinish)(void *)) {
//do something in background thread
//and on its finish, call callbackOnFinish from thread
callbackOnFinish(ptr);
}
However, with this I get:
A C function pointer cannot be formed from a closure that captures context
How do I solve this?

You need a wrapper class so that a void pointer to the instance can be tunneled through the C function to the callback. The combination of passRetained() and takeRetainedValue() ensures that the wrapper instance is released only after the completion function has been called.
func getSnapshot(completion: #escaping (GetSnapshotResult) -> Void) {
class Wrapper {
let completion: (GetSnapshotResult) -> Void
init(completion: #escaping (GetSnapshotResult) -> Void) {
self.completion = completion
}
}
let wrapper = Wrapper(completion: completion)
let observer = UnsafeMutableRawPointer(Unmanaged.passRetained(wrapper).toOpaque())
CAPIGetSnapshot(observer) { theObserver in
let theWrapper = Unmanaged<Wrapper>.fromOpaque(theObserver!).takeRetainedValue()
theWrapper.completion(
.success( snapshot: UIImage(), "test")
)
}
}
Some remarks:
I am assuming that the C function passes the ptr argument to the callback.
passRetained(wrapper) retains the object. That ensures that the wrapper instance is not released when the getSnapshot() function returns.
takeRetainedValue() in the closure consumes the retain. As a consequence, the wrapper instance is released when the closure returns.
completion is a closure and closures are reference types. wrapper.completion holds a reference to that closure as long as the wrapper instance exists.
Of course you can use the same variable names (“observer”, “wrapper”) inside the closure. I chose different names here (“theObserver”, “theWrapper”) only to emphasize that they are different variables, i.e. that the closure does not capture context anymore.
observer needs to be a mutable raw pointer only because the first argument of the C function is declared as void * ptr. If you can change the function declaration to
void CAPIGetSnapshot(const void * ptr, void(*callbackOnFinish)(const void *))
then let observer = UnsafeRawPointer(...) works as well.
For more information about conversion between object references to void pointers see for example How to cast self to UnsafeMutablePointer<Void> type in swift.
Instead of a custom wrapper class you can also take advantage of the fact that arbitrary Swift values are automatically boxed in a class type when cast to AnyObject (see for example AnyObject not working in Xcode8 beta6?).
func getSnapshot(completion: #escaping (GetSnapshotResult) -> Void) {
let wrapper = completion as AnyObject
let observer = UnsafeRawPointer(Unmanaged.passRetained(wrapper).toOpaque())
CAPIGetSnapshot(observer) { theObserver in
let theCompletion = Unmanaged<AnyObject>.fromOpaque(theObserver!).takeRetainedValue()
as! ((GetSnapshotResult) -> Void)
theCompletion(
.success( snapshot: UIImage(), "test")
)
}
}
The forced unwraps and forced casts are safe here because you know what it passed to the function. A failure to unwrap or cast would indicate a programming error. But I would prefer the first version instead of relying on this “magic”.

Related

SWIFT iterating through structures - issue

Im trying to iterate thorough and array of structures using generics and I keep getting this error. Value of type [T] has no member 'printMessage'
My 2nd questions is - What message would print? The statement in the Foo protocol extension or the statement in the struct instance?
Not sure what the issue is.. and its driving me insane!
protocol Foo {
func printMessage()
}
extension Foo {
func printMessage() {
print("Hello")
}
}
struct test: Foo {
func printMessage() {
print("Goodbye")
}
}
func MessagePrinter<T: Foo>(for message: [T]) {
for message in [message] {
message.printMessage()
}
For more clarity name the array in plural form and and the element in singular form.
And the square brackets in the for loop are wrong, the parameter is already an array
func messagePrinter<T: Foo>(for messages: [T]) {
for message in messages {
message.printMessage()
}
}
And please name functions always with starting lowercase letter.
The method in the protocol extension is called unless the method is implemented.
But consider that T is a single concrete type at runtime, you cannot call the method on a heterogenous Foo array like this
let messages : [Foo] = [Test()]
messagePrinter(for: messages)
You will get the error
Protocol 'Foo' as a type cannot conform to the protocol itself
To be able to call the method on an heterogenous array whose elements conform to Foo you have to declare
func messagePrinter(for messages: [Foo]) { ...
You are wrapping an array in another array here:
func MessagePrinter<T: Foo>(for messages: [T]) {
for message in [messages] {
message.printMessage()
}
}
(I've renamed your function argument from message to messages to make it clearer.)
When you write [messages] you end up with an array containing another array containing T, so the type is [[T]]. The single element message thus has type [T], and an array has no method printMessage.
What you want is this:
func MessagePrinter<T: Foo>(for messages: [T]) {
for message in messages {
message.printMessage()
}
}
As for what's printed when you execute it: that depends on what elements you feed it. If the elements implement printMessage those methods are called (e.g. "Goodbye"). Otherwise the default implementation you have provided for the protocol is called ("Hello").

Swift conversion of Array of Strings to C array of C Strings not working

The C function I wish to call is documented as:
GDALVectorTranslateOptions* GDALVectorTranslateOptionsNew(char** papszArgv, GDALVectorTranslateOptionsForBinary* psOptionsForBinary)
...and is imported to swift as:
public func GDALVectorTranslateOptionsNew(_ papszArgv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!, _ psOptionsForBinary: OpaquePointer!) -> OpaquePointer!
I have been unable to convert my swift array of strings into a C array of C strings in such a way that does not produce an error.
I have tried various methods found during research, and they all end up with similar errors being produced. My current method is to use the following function to convert the Swift array of strings to be used within a closure:
public func withArrayOfCStrings<R>(_ args: [String], _ body: ([UnsafeMutablePointer<CChar>?]) -> R) -> R {
var cStrings = args.map { strdup($0) }
cStrings.append(nil)
defer {
cStrings.forEach { free($0) }
}
return body(cStrings)
}
I'm attempting to use it all like this:
var opts = ["blah1", "blah2"]
withArrayOfCStrings(opts) { (cOpts) in
let translateOpts = GDALVectorTranslateOptionsNew(cOpts, nil)
}
But I get the error:
Cannot convert value of type '[UnsafeMutablePointer?]' (aka 'Array<Optional<UnsafeMutablePointer>>') to expected argument type 'UnsafeMutablePointer<UnsafeMutablePointer?>?'
How should I be doing this?
That withArrayOfCStrings function works with C functions taking a char * const * argument, i.e. a pointer to constant pointers to characters.
If you change the C declaration to
GDALVectorTranslateOptions* GDALVectorTranslateOptionsNew(char* const* papszArgv,
GDALVectorTranslateOptionsForBinary* psOptionsForBinary)
then it compiles and runs as expected.
If you do not have the option to modify the C code then you can change the helper method to
public func withArrayOfCStrings<R>(_ args: [String],
_ body: (UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>) -> R) -> R {
var cStrings = args.map { strdup($0) }
cStrings.append(nil)
defer {
cStrings.forEach { free($0) }
}
return body(&cStrings)
}
The body is now called with a UnsafeMutablePointer<UnsafeMutablePointer<Int8>?> argument, which corresponds to a C char ** argument.

String.withCString when the String is nil

The problem that'll be described relates to my previous question:
string.withCString and UnsafeMutablePointer(mutating: cstring) wrapped into a function which was my first approach to handle nil Strings (by putting withCString into a function)
and to a question which Mecki asked:
Why can't I pass an optional Swift String to C function that allows NULL pointers?
Imagine there is a c-function like:
unsigned long randomSign(char *pin, char *tag_signature, char *tag_data, char *xyz);
I know that the function works correctly if I wrap 4 string.withCString closures around the corresponding swift function:
// pin, tag_signature, tag_data and xyz are optional Strings so they may be nil which is a problem for my result.
// corresponding swift function:
// randomSign(pin: UnsafeMutablePointer<Int8>!, tag_signature: UnsafeMutablePointer<Int8>!, tag_data: UnsafeMutablePointer<Int8>!, xyz: UnsafeMutablePointer<Int8>!)
let result = pin.withCString { s1 in return
tag_signature.withCString {s2 in return
tag_data.withCString {s3 in return
xyz.withCString { s4 in return
randomSign(UnsafeMutablePointer(mutating: s1), UnsafeMutablePointer(mutating: s2), UnsafeMutablePointer(mutating: s3), UnsafeMutablePointer(mutating: s4))
}}}}
And so Martin R replied to an easier example, that it is not needed to wrap the closures around randomSign(arguments) and UnsafeMutablePointer(mutating: ...) because it can also take the strings and converts it.
But when I drop the closures and use it as Martin R described, it worked at the first launch on the simulator directly after starting the mac, but on consecutive calls of the randomSign-Function the return would tell me that for example the tag_signature or the pin would be invalid (but it actually is valid and I don't know why?!).
This leads me to the problem that I need the withCString closures (at the moment) but I have to handle nil-Strings, which would result the app to crash when it shall return the result because it couldn't evaluate the randomSign-Function.
So I tried to fit the approach below (also suggested by #Martin R) to Swift 3, but I did not workout to adapt it.
//Swift-2 written by Martin R
protocol CStringConvertible {
func withCString<Result>(#noescape f: UnsafePointer<Int8> throws -> Result) rethrows -> Result
}
extension String: CStringConvertible { }
extension Optional where Wrapped: CStringConvertible {
func withOptionalCString<Result>(#noescape f: UnsafePointer<Int8> -> Result) -> Result {
if let string = self {
return string.withCString(f)
} else {
return f(nil)
}
}
}
//Swift 3: ???
If anyone can tell me, why my function only works out when I use withCString but not when I dismiss it, I would be very grateful
and also if anyone knows how to solve the issue, i.e. correctly translating the swift-2 code to working swift-3 code.
The problem with
let result = randomSign(UnsafeMutablePointer(mutating: pin),
UnsafeMutablePointer(mutating: tag_signature),
UnsafeMutablePointer(mutating: tag_data),
UnsafeMutablePointer(mutating: xyz))
is that the temporary UTF-8 representation created from the Swift
strings is valid only during each call of UnsafeMutablePointer(),
but not necessarily still valid during the call of randomSign().
(So my final suggestion in https://stackoverflow.com/a/44027397/1187415
was actually not correct, I have updated that part).
A possible Swift 3 version of the wrapper in https://stackoverflow.com/a/39363769/1187415 is
extension Optional where Wrapped == String {
func withOptionalCString<Result>(_ f: (UnsafeMutablePointer<Int8>?) -> Result) -> Result {
if let string = self {
return string.withCString { f(UnsafeMutablePointer(mutating: $0)) }
} else {
return f(nil)
}
}
}
This handles both the optionality and casts the C string pointer
to a mutable pointer (as required by randomSign()). This can be
called as
let result = pin.withOptionalCString { s1 in
tag_signature.withOptionalCString { s2 in
tag_data.withOptionalCString { s3 in
xyz.withOptionalCString { s4 in
randomSign(s1, s2, s3, s4)
}
}
}
}
Remark: In theory, the problem can be avoided if the signature of randomSign() is changed to take const char * parameters:
unsigned long randomSign(const char *pin, const char *tag_signature, const char *tag_data, const char *xyz);
which one could then simply call as
let result = randomSign(pin, tag_signature, tag_data, xyz)
with optional or non-optional Swift strings.
However, this does currently not work, as reported in
SR-2814 Swift does not correctly pass in multiple optional strings to C function.

GLFW callback signature in Swift 3

I'm trying to setup a simple GLFW application in Swift 3. Currently I'm stuck trying to set a callback function.
func setupGLFW() {
...
glfwSetErrorCallback(errorCallback)
...
}
func errorCallback(error: Int32, description: UnsafePointer<Int8>) {
...
}
This worked in Swift 2 but now I get this error:
cannot convert value of type '(Int32, UnsafePointer) -> ()' to expected argument type 'GLFWerrorfun!'
After consulting the section Interacting with C APIs in the documentation, I also tried this signature:
func errorCallback(error: CInt, description: UnsafePointer<CChar>) {
...
}
This leads to the same error.
The C signature for GLFWerrorfun is:
typedef void(* GLFWerrorfun) (int, const char *)
Try with:
func errorCallback(error: Int32, description: UnsafePointer<Int8>?) {
(Please do not miss the ? after UnsafePointer<Int8>.)
In Swift 3, nullable pointer are imported as Optional pointer, so you need ?.
You could have the callback inline where you call glfwSetErrorCallback:
glfwSetErrorCallback { (error, description) in
//
}
Or, use a closure stored in a variable:
let callback: GLFWerrorfun = { (error, description) in
//
}
glfwSetErrorCallback(callback)
You can see how this typedef is bridged to Swift by Option + Left Clicking on the symbol:
In this case I didn't have the actual GLFW headers, so I just dropped the declaration in a Bridging Header. Your generated interface may show more information.

Pass Go function to C as callback

Suppose I have a C library with code like this:
typedef int (callback_t)(int);
void register_callback(callback_t cb);
I want to write go bindings for this function and pass arbitrary go callbacks.
I found an excellent answer here. However, it is a trick that makes use of the fact that the callback accepts a void *, through which Go passes the function pointer to C and receives it back. However, this can't be applied in my example since there isn't a user void *.
The best I could do is:
/*
extern int gobridge(int data);
static int cbridge(void)
{
register_callback(gobridge);
}
*/
import "C"
type Callback func (int) int
var g_cb Callback
//export gobridge
func gobridge(data C.int) C.int {
return C.int(g_cb(int(data)))
}
func RegisterCallback(cb Callback) {
g_cb = cb //save callback in a global
C.cbridge()
}
func Count(left, right int) {
C.count(C.int(left), C.int(right))
}
This only works for a single register_callback (because of the global variable), but the C library can register many callbacks and invoke them all.
I hoped to achieve something like this, but this doesn't compile (put aside the clojure, it can't convert the go function to c function even though the signature is the same):
import "C"
func RegisterCallback(cb Callback) {
f := func (d C.int) C.int {
return C.int(cb(int(d)))
}
C.register_callback(f)
}
func Count(left, right int) {
C.count(C.int(left), C.int(right))
}
Is there a way to properly bind the C library?
You could create an array of callbacks and have the bridge iterate the array of callbacks if the there is no return value (void function).

Resources