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.
Related
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").
I have a library parsing FIT file in swift using an externally provided c library. The parsing function takes as argument a void * data.
To call the function, I was converting the data using data.withUnsafeBytes( { (ptr: UnsafePointer<UInt8>) in ...} to build the argument to the c function and it was working fine.
After the upgrade of Xcode to swift 5, I now get a deprecated warning
'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R instead
I couldn't work out how to fix the code to remove the deprecated warning. The code has been working fine and without warning in swift 4
I tried to change the argument in the closure to take UnsafeRawBufferPointer instead of the UnsafePointer but this resulted in an error in calling the function: Cannot convert 'UnsafeRawBufferPointer' to expected argument type 'UnsafeRawPointer?'
This is a small swift file to show the problem:
import Foundation
// Create sample data (Typically would be read from a file
let data = Data(repeating: 1, count: 10)
data.withUnsafeBytes( { (ptr : UnsafePointer<UInt8>) in
// call the c function with the void* argument
let value = readFITfile(ptr)
print( value )
})
And an example c function
unsigned readFITfile(const void * data){
//Silly example to show it works, just returning the value of pointer as int
//Typically would parse the data and return a structure
return (unsigned)data;
}
I saved a small repo with the above code here https://github.com/roznet/swift2c and the full scale project with the parsing of the file is here https://github.com/roznet/fit-sdk-swift
You have to change the closure argument to UnsafeRawBufferPointer and then take its baseAdress (which is a UnsafeRawPointer?, the Swift equivalent of void * in C):
data.withUnsafeBytes( { (ptr : UnsafeRawBufferPointer) in
let value = readFITfile(ptr.baseAddress)
// ...
})
The Swift compiler can also infer the closure argument type automatically:
data.withUnsafeBytes( { ptr in
let value = readFITfile(ptr.baseAddress)
// ...
})
For more information about this problem, see withUnsafeBytes Data API confusion in the Swift forum.
To get UnsafePointer now you should do something like that
data.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) in
if let ptrAddress = ptr.baseAddress, ptr.count > 0 {
let pointer = ptrAddress.assumingMemoryBound(to: UInt8.self) // here you got UnsafePointer<UInt8>
let value = readFITfile(ptr)
print( value )
} else {
// Here you should provide some error handling if you want ofc
}
}
I'm building a wrapper/interface for a C library with Swift 3. One of the functions that I need to call require a function pointer for callback as an argument.
In detail: after said function has successfully done the file operation it has to, it then calls the function the given argument pointer refers to - essentially letting me do other operations with said data
The function looks like this:
HSYNC MXDEF (Syncer)(DWORD h, DWORD t, QWORD p, SYNCPROC *proc, void *user);
The callback function type SYNCPROC is defined as follows:
typedef void (CALLBACK SYNCPROC)(HSYNC h, DWORD c, DWORD d, void *user);
I have only been able to use the Syncer function with setting the callback argument to nil so far.
I tried creating a function outside the Class as someone suggested:
func callbackCalled(handle: HSYNC, channel: DWORD, data: DWORD, user: UnsafeMutableRawPointer) -> Void {
print("callback called")
}
And I even tried this method within the Class:
var callbackTest : #convention(c) (_ handle: HSYNC, _ channel: DWORD, _ data: DWORD, _ user: UnsafeMutableRawPointer) -> Void = {
print("\($0) \($1) \($2) \($3)")
}
But however I tried / read upon this topic I always end up with this error message:
Cannot convert value of type 'Void' (aka '()') to expected argument type '(#convention(c) (HSYNC, DWORD, DWORD, UnsafeMutableRawPointer?) -> Void)!'
My question is: How am I supposed to satisfy the criteria regarding this type of a callback function?
I haven't been able to find any info regarding these types of callback functions, presumably due to my lack of knowledge and thorough understanding of the problem. Thank you in advance!
Passing a global function as callback should work, but the last parameter
must be an optional UnsafeMutableRawPointer?:
func callbackCalled(handle: HSYNC, channel: DWORD, data: DWORD, user: UnsafeMutableRawPointer?) -> Void {
print("callback called")
}
Syncer(h, c, d, callbackCalled, u)
Alternatively, pass a closure expression (and let compiler infer
the types of the parameters):
Syncer(h, d, c, {
(handle, channel, data, user) in
// ...
}, u)
You can not pass an instance method as a callback, compare
How to use instance method as callback for function which takes only func or literal closure.
If NULL is not a valid value for the user parameter then you can
add
"Nullability annotations" to the C declaration, e.g.
typedef void (CALLBACK SYNCPROC)(HSYNC h, DWORD c, DWORD d, void * _Nonnull user);
and then it will be represented as a non-optional UnsafeMutableRawPointer in Swift.
I have seen:
Rust array of functions
Iterate over vector of functions
and searched online. I do not want closures. I am trying to implement a classic dynamic(-ish) function lookup table.
mod impl_foo;
mod impl_bar;
use utils;
// a CmdResult is a Result with a tuple of an int and a string
static FUNCTIONS: &'static [fn(&[String]) -> utils::CmdResult] = &[
("bar".to_string(), impl_bar::call_bar),
("foo".to_string(), impl_foo::call),
];
fn find_action(name: &String) -> (fn(&[String]) -> utils::CmdResult) {
match FUNCTIONS.binary_search_by(|item| item[0].cmp(name)) {
Ok(action) => action,
Err(_) => (|&[String]| Err((1, format!("Unknown '{}'", name))))
}
}
// later on in another function ....
action = find_action("foo");
let result = action(args);
// process results
But this does not compile:
src/main.rs:44:5: 44:50 error: mismatched types:
expected `fn(&[collections::string::String]) -> core::result::Result<i32, (i32, collections::string::String)>`,
found `(collections::string::String, fn(&[collections::string::String]) -> core::result::Result<i32, (i32, collections::string::String)> {impl_foo::call})`
and again for impl_bar::call_bar.
What am I missing? It appears to have something to do with the use of different modules since it clearly works for other people.
I also tried to define a type:
type Action = fn(&[String]) -> utils::CmdResult;
and use that to cut down on typing but no luck there either.
BTW, you need #![feature(slice_patterns)] because of the &[String].
Edit the next morning.....
As Francis points out below my transcription here had a flaw. It did not exactly match the real problem I had but it helped me see with fresh eyes.
The slice pattern requirement is because I was trying to handle unknown functions with a closure. Once I removed that the complaint went away. I was trying to be a little too dynamic language style I think :-)
Below is the completed code that actually works so that people finding this question can see working code.
type Action = fn(&[String]) -> utils::CmdResult;
static FUNCTIONS: &'static [(&'static str, Action)] = &[
("bar", impl_bar::call),
("foo", impl_foo::call_foo),
];
fn find_action(prog: &String) -> Option<Action> {
match FUNCTIONS.binary_search_by(|&(name,_)| name.cmp(prog)) {
Ok(idx) => Some(FUNCTIONS[idx].1),
Err(_) => None,
}
}
fn invoke(prog: &String, args: &[String]) -> i32 {
let result = match find_action(prog) {
Some(action) => action(args),
None => Err((1, format!("Unknown: {}", prog))),
};
result.unwrap_or_else(|(n, msg)| {
writeln!(io::stderr(), "{}", msg).ok();
n
})
}
Read the error message carefully:
src/main.rs:44:5: 44:50 error: mismatched types:
expected `fn(&[collections::string::String]) -> core::result::Result<i32, (i32, collections::string::String)>`,
found `(collections::string::String, fn(&[collections::string::String]) -> core::result::Result<i32, (i32, collections::string::String)> {impl_foo::call})`
Let's simplify it:
src/main.rs:44:5: 44:50 error: mismatched types:
expected `fn(&[String]) -> Result<i32, (i32, String)>`,
found `(String, fn(&[String]) -> Result<i32, (i32, String)> {impl_foo::call})`
What this message is telling you is that you're trying to put a tuple of String and a function type into an array that expects only the function type.
You probably meant to define your array like this:
static FUNCTIONS: &'static [(&'static str, fn(&[String]) -> utils::CmdResult]) = &[
("bar", impl_bar::call_bar),
("foo", impl_foo::call),
];
I'm trying and failing to create an instance of AudioServicesSystemSoundCompletionProc for an argument in AudioServicesAddSystemSoundCompletion using Swift in Xcode.
Here's what I've got so far
func completionCallback(ssID:SystemSoundID,clientData:UnsafeMutablePointer<Void>) -> Void {
}
var foo:(ssID:SystemSoundID,clientData:UnsafeMutablePointer<Void>) -> Void = completionCallback;
AudioServicesAddSystemSoundCompletion(soundID, nil, nil, foo, nil);
I wrote this with the help of some guides explaining how to write equivalent C Function Pointers in Swift, but this throws this error:
'(ssID: SystemSoundID, clientData: UnsafeMutablePointer<Void>) -> Void' is not convertible to 'AudioServicesSystemSoundCompletionProc'
The documentation shows the Objective-C declaration:
typedef void (*AudioServicesSystemSoundCompletionProc) ( SystemSoundID ssID, void *clientData );
This is declaration shown when using Xcode:
typealias AudioServicesSystemSoundCompletionProc = CFunctionPointer<((SystemSoundID, UnsafeMutablePointer<Void>) -> Void)>
I'm not sure how to implement AudioServicesSystemSoundCompletionProc correctly in Swift.
You could do it as a closure, as of Swift 2.0.
AudioServicesAddSystemSoundCompletion(soundID, nil, nil, { (soundID, clientData) -> Void in
// Your completion callback...
AudioServicesDisposeSystemSoundID(soundID)
}, nil)
Further info from Apple (scroll down to Function Pointers):
C function pointers are imported into Swift as closures
David's answer is correct. But just to clarify, AudioServicesSystemSoundCompletionProc needs to be done in objective-c, then bridged across to swift. You can either write an implementation yourself, or... it is already done for you here: https://gist.github.com/jparishy/7b76edf8d0fcca1d63b0 (as mentioned by David)
You need to go to that link, download the files FunctionPointer.h and FunctionPointer.c, put it in your project folder, then link it to swift using bridging header.
To do that:
Go to your project's build settings, scroll down to where it says Objective-C Bridging Header, edit the content to "YourProject/Bridging-Header.h" (where YourProject is the name of your project)
Create an objective-c header file in your project, and name it Bridging-Header.h
Inside the header file, add #import "FunctionPointer.h"
Now you can access FunctionPointer anywhere inside your swift project... sweet
Back to the compeletion handler, in your code, where you want to use AudioServicesAddSystemSoundCompletion, do something like this:
// at the top of the class
var functionPointer: AudioServicesCompletionFunctionPointer?
// in the code
var me = self
let userData = withUnsafePointer(&me) { ptr in
return unsafeBitCast(ptr, UnsafeMutablePointer<Void>.self)
}
self.functionPointer = AudioServicesCompletionFunctionPointer(systemSoundID: soundId, block: {(systemSoundID: SystemSoundID, userData: UnsafeMutablePointer<Void>) -> () in
// sound has ended, do your stuff here
}, userData: userData)
AudioServicesAddSystemSoundCompletion(soundId, CFRunLoopGetMain(), kCFRunLoopDefaultMode, AudioServicesCompletionFunctionPointer.completionHandler(), userData)
where you'll have to change NSObject to TheClassYou'reUsingThisIn and soundId to your soundId
let myData = unsafeBitCast(self, UnsafeMutablePointer<Void>.self)
AudioServicesAddSystemSoundCompletion(YOUR_SOUND_ID, CFRunLoopGetMain(), kCFRunLoopDefaultMode,{ (mSound, mVoid) in
let me = unsafeBitCast(mVoid, YOURCURRENTCLASS.self)
//me it is your current object so if yo have a variable like
// var someVar you can do
print(me.someVar)
}, myData)
The answer for this can be seen here: https://gist.github.com/jparishy/7b76edf8d0fcca1d63b0
Solution by Julius Parishy (https://stackoverflow.com/users/1164263/julius-parishy)
This works in Swift 3:
let weakSelf = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
let error = AudioServicesAddSystemSoundCompletion(soundId,
nil,
nil,
{soundId, weakSelfPointer in
guard let weakSelfPointer = weakSelfPointer else {
return
}
let weakSelfValue = Unmanaged<NAME_OF_SELF_CLASS>.fromOpaque(weakSelfPointer).takeUnretainedValue()
// Then you can use `weakSelfValue` as you would do with `self`.
weakSelfValue.A_METHOD_OF_SELF
}, weakSelf)