I'm trying to interact with an old C terminal app from Swift. I've successfully integrated the source code and bridged the headers from C to Swift. The code compiles and runs from Xcode 6.3 beta. I've renamed the terminal app's main entry point to:
int initialize(int argc, char **argv);
Nevertheless, I'm struggling to pass the arguments from Swift to this C function. My challenge is to convert the arguments in the right format. Typical input from Swift would look like:
let args = ["-c", "1.2.3.4", "-p", "8000"]
I've tried messing with "cStringUsingEncoding(NSUTF8StringEncoding)" and "withUnsafePointer", but no luck so far. Any help is greatly appreciated!
The C function
int initialize(int argc, char **argv);
is mapped to Swift as
func initialize(argc: Int32, argv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>>) -> Int32
This is a possible solution:
let args = ["-c", "1.2.3.4", "-p", "8000"]
// Create [UnsafeMutablePointer<Int8>]:
var cargs = args.map { strdup($0) }
// Call C function:
let result = initialize(Int32(args.count), &cargs)
// Free the duplicated strings:
for ptr in cargs { free(ptr) }
It uses the fact that in strdup($0)
the Swift string $0 is automatically converted to a C string,
as explained in String value to UnsafePointer<UInt8> function parameter behavior.
Building on Martin’s answer, if you find yourself doing this a lot, you could wrap the dup/free part into a function in a similar style to String.withCString:
import Darwin
func withCStrings
<R, S: SequenceType where S.Generator.Element == String>
(strings: S, #noescape body: (UnsafeBufferPointer<UnsafeMutablePointer<Int8>>) -> R)
-> R {
let cstrings = map(strings) { strdup($0) } + [nil]
let result = cstrings.withUnsafeBufferPointer(body)
for ptr in cstrings { free(ptr) }
return result
}
let execvargs = ["/usr/bin/say"] + dropFirst(Process.arguments)
let execvresult = withCStrings(execvargs) {
execv($0[0], $0.baseAddress)
}
Related
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.
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.
Currently I have three files
main.swift
var dog = Dog()
dog.age = 12
dog.name = "H" // This is the non workable code
CTester.c
#include <stdio.h>
struct Dog {
int age;
char name[10];
} Dog;
and C Test-Bridging-Header
#import "CTester.c"
I am trying to use the C struct in Swift, however, the Char array shows up as an array of 10 Int8 in Swift. How can I take a Swift string and assign it to the char array?
So, you can write some extension for your Dog like this:
Swift 2
extension Dog {
var Name: String {
mutating get {
return withUnsafePointer(&self.name) {namePtr in
let charPtr = UnsafePointer<CChar>(namePtr)
//Sorry, this code may crash here...
return String(CString: charPtr, encoding: NSUTF8StringEncoding)!
}
}
set {
withUnsafeMutablePointer(&self.name) {namePtr in
let charPtr = UnsafeMutablePointer<CChar>(namePtr)
let size = sizeofValue(self.name)
strncpy(charPtr, newValue, size - 1)
charPtr[size - 1] = 0
}
}
}
}
Swift 3(Tested with Xcode 8 beta 4)
extension Dog {
var Name: String {
mutating get {
return withUnsafePointer(&self.name) {namePtr in
let charPtr = UnsafePointer<CChar>(namePtr)
//The result may contain the Unicode replacement character ("\u{FFFD}")
return String(cString: charPtr)
}
}
set {
withUnsafeMutablePointer(&self.name) {namePtr in
let charPtr = UnsafeMutablePointer<CChar>(namePtr)
let size = sizeofValue(self.name)
strncpy(charPtr, newValue, size - 1)
charPtr[size - 1] = 0
}
}
}
}
Seeing the linked thread in Martin R's comment, there may be some room to improve...
(Especially using strlcpy reduces your code size with better safety. Please check it.)
But anyway, it works as:
dog.Name = "H"
print(dog.Name) //->H
I have found that getter of Name may crash your app...
"It works" in a restriction that you store only ASCII characters.
I'm trying to interact with an old C terminal app from Swift. I've successfully integrated the source code and bridged the headers from C to Swift. The code compiles and runs from Xcode 6.3 beta. I've renamed the terminal app's main entry point to:
int initialize(int argc, char **argv);
Nevertheless, I'm struggling to pass the arguments from Swift to this C function. My challenge is to convert the arguments in the right format. Typical input from Swift would look like:
let args = ["-c", "1.2.3.4", "-p", "8000"]
I've tried messing with "cStringUsingEncoding(NSUTF8StringEncoding)" and "withUnsafePointer", but no luck so far. Any help is greatly appreciated!
The C function
int initialize(int argc, char **argv);
is mapped to Swift as
func initialize(argc: Int32, argv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>>) -> Int32
This is a possible solution:
let args = ["-c", "1.2.3.4", "-p", "8000"]
// Create [UnsafeMutablePointer<Int8>]:
var cargs = args.map { strdup($0) }
// Call C function:
let result = initialize(Int32(args.count), &cargs)
// Free the duplicated strings:
for ptr in cargs { free(ptr) }
It uses the fact that in strdup($0)
the Swift string $0 is automatically converted to a C string,
as explained in String value to UnsafePointer<UInt8> function parameter behavior.
Building on Martin’s answer, if you find yourself doing this a lot, you could wrap the dup/free part into a function in a similar style to String.withCString:
import Darwin
func withCStrings
<R, S: SequenceType where S.Generator.Element == String>
(strings: S, #noescape body: (UnsafeBufferPointer<UnsafeMutablePointer<Int8>>) -> R)
-> R {
let cstrings = map(strings) { strdup($0) } + [nil]
let result = cstrings.withUnsafeBufferPointer(body)
for ptr in cstrings { free(ptr) }
return result
}
let execvargs = ["/usr/bin/say"] + dropFirst(Process.arguments)
let execvresult = withCStrings(execvargs) {
execv($0[0], $0.baseAddress)
}
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)