Correct. String does not have C equivalent. It contains a lot more than array of char. Instead, use UnsafePointer<CChar> which is what pointer to char is ported to. You can convert between them using init(utf8String:), and withCString, or their siblings if the string you pass is non-ascii.
Hmm, if glfw.Error is ported from C, it should be able to do a round-trip. I don't know much about interop either. Guess we'll need to wait for someone more knowledgeable.
There are a lot of mismatch between Swift and C. Whatever you end up doing, it'll likely reflect that.
Swift function is a pointer to function + capture list.
C function is just a pointer (no capturing).
I think there's C lambda that let you capture, but if the in-memory layout doesn't match that of Swift, you'll still need to do conversion anyhow.
Swift String is a unicode-correct string that support both utf8 and utf16.
C string is just an array of ascii characters.
There's also C wide char which I don't know what it does.
Most (all?) of Swift types engages in reference-counting. C doesn't have one. You'll need to drop-down into unsafe territory since there's nothing telling compiler if what goes into C is still in used.
AFAICT these mismatches is not easily synthesised since there's no one right way to do it.
Should there be some concrete improvements to be had, you can pitch some idea in this forum! (perhaps as a separate thread)
Hi @elect86, what a coincidence! I have spent some time on this problem last week : )
Have a look of this article: Swift callbacks, I solved my problem through this way(You need to make some minor changes to the code in the article, because of the API change)
class glfw {
func setErrorCallback(cbFun: ErrorFun) -> GLFWerrorfun? {
let observer = UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())
// ^^^^^^^^ observer is defined here
return glfwSetErrorCallback { err, desc in
// ^^^^^^^^^ these are the *only* arguments that this C function takes, and observer is neither of them
let mySelf = Unmanaged<glfw>.fromOpaque(observer).takeUnretainedValue()
// ^^^^^^^^ observer is used here
}
}
}
As you can see, inside the C function you're using something that wasn't passed in as a parameter. C functions cannot do that. It works in swift closures, because as @Lantua said, "swift function is a pointer to a function + capture list", so you can pass observer via the capture list, unlike C function that is "just a pointer (no capturing)".
In the stack overflow example, observer is passed as a parameter
Roughly speaking, in that SO example a pointer to self is “tunneled” to the callback function via the observer parameter.
Your glfwSetErrorCallback() function does not have such a “user data parameter” which is passed on to the callback, so there is no (clean) way to get a pointer to self inside the callback. The callback is probably meant to be a global function.
@elect86, as the error message said, you can not use any variable outside the closure. But you used observer in your closure.
In this piece of code, the observer is used to pass the class pointer. But you need to pass it to the C function first and then pass it back, then you can use it in the closure.
I'll show you a demo which I used in my project. The only difference is that the callback type is () -> Void
The swift code:
func getClassPtr<T: AnyObject>(_ obj: T) -> UnsafeMutableRawPointer {
return UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque())
}
class Timer {
var callback: (() -> Void)?
func addCallback(_ closure: @escaping ()->Void) {
self.callback = closure
lowLevelAddCallback(getClassPtr(self)) { (classPtr)->Void in
let mySelf = Unmanaged<Timer>.fromOpaque(classPtr!).takeUnretainedValue()
//Then you can use mySelf to access class member
mySelf.callback!()
}
}
}